summaryrefslogtreecommitdiff
path: root/i386/i386at/gpl/linux/block
diff options
context:
space:
mode:
authorThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
committerThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
commitf07a4c844da9f0ecae5bbee1ab94be56505f26f7 (patch)
tree12b07c7e578fc1a5f53dbfde2632408491ff2a70 /i386/i386at/gpl/linux/block
Initial source
Diffstat (limited to 'i386/i386at/gpl/linux/block')
-rw-r--r--i386/i386at/gpl/linux/block/cmd640.c738
-rw-r--r--i386/i386at/gpl/linux/block/floppy.c4100
-rw-r--r--i386/i386at/gpl/linux/block/genhd.c610
-rw-r--r--i386/i386at/gpl/linux/block/ide-cd.c2770
-rw-r--r--i386/i386at/gpl/linux/block/ide.c3087
-rw-r--r--i386/i386at/gpl/linux/block/ide.h655
-rw-r--r--i386/i386at/gpl/linux/block/ide_modes.h142
-rw-r--r--i386/i386at/gpl/linux/block/rz1000.c56
-rw-r--r--i386/i386at/gpl/linux/block/triton.c459
9 files changed, 12617 insertions, 0 deletions
diff --git a/i386/i386at/gpl/linux/block/cmd640.c b/i386/i386at/gpl/linux/block/cmd640.c
new file mode 100644
index 0000000..99a139d
--- /dev/null
+++ b/i386/i386at/gpl/linux/block/cmd640.c
@@ -0,0 +1,738 @@
+/*
+ * linux/drivers/block/cmd640.c Version 0.07 Jan 27, 1996
+ *
+ * Copyright (C) 1995-1996 Linus Torvalds & author (see below)
+ */
+
+/*
+ * Principal Author/Maintainer: abramov@cecmow.enet.dec.com (Igor Abramov)
+ *
+ * This file provides support for the advanced features and bugs
+ * of IDE interfaces using the CMD Technologies 0640 IDE interface chip.
+ *
+ * Version 0.01 Initial version, hacked out of ide.c,
+ * and #include'd rather than compiled separately.
+ * This will get cleaned up in a subsequent release.
+ *
+ * Version 0.02 Fixes for vlb initialization code, enable
+ * read-ahead for versions 'B' and 'C' of chip by
+ * default, some code cleanup.
+ *
+ * Version 0.03 Added reset of secondary interface,
+ * and black list for devices which are not compatible
+ * with read ahead mode. Separate function for setting
+ * readahead is added, possibly it will be called some
+ * day from ioctl processing code.
+ *
+ * Version 0.04 Now configs/compiles separate from ide.c -ml
+ *
+ * Version 0.05 Major rewrite of interface timing code.
+ * Added new function cmd640_set_mode to set PIO mode
+ * from ioctl call. New drives added to black list.
+ *
+ * Version 0.06 More code cleanup. Readahead is enabled only for
+ * detected hard drives, not included in readahed
+ * black list.
+ *
+ * Version 0.07 Changed to more conservative drive tuning policy.
+ * Unknown drives, which report PIO < 4 are set to
+ * (reported_PIO - 1) if it is supported, or to PIO0.
+ * List of known drives extended by info provided by
+ * CMD at their ftp site.
+ *
+ * Version 0.08 Added autotune/noautotune support. -ml
+ *
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <asm/io.h>
+#include "ide.h"
+#include "ide_modes.h"
+
+int cmd640_vlb = 0;
+
+/*
+ * CMD640 specific registers definition.
+ */
+
+#define VID 0x00
+#define DID 0x02
+#define PCMD 0x04
+#define PSTTS 0x06
+#define REVID 0x08
+#define PROGIF 0x09
+#define SUBCL 0x0a
+#define BASCL 0x0b
+#define BaseA0 0x10
+#define BaseA1 0x14
+#define BaseA2 0x18
+#define BaseA3 0x1c
+#define INTLINE 0x3c
+#define INPINE 0x3d
+
+#define CFR 0x50
+#define CFR_DEVREV 0x03
+#define CFR_IDE01INTR 0x04
+#define CFR_DEVID 0x18
+#define CFR_AT_VESA_078h 0x20
+#define CFR_DSA1 0x40
+#define CFR_DSA0 0x80
+
+#define CNTRL 0x51
+#define CNTRL_DIS_RA0 0x40
+#define CNTRL_DIS_RA1 0x80
+#define CNTRL_ENA_2ND 0x08
+
+#define CMDTIM 0x52
+#define ARTTIM0 0x53
+#define DRWTIM0 0x54
+#define ARTTIM1 0x55
+#define DRWTIM1 0x56
+#define ARTTIM23 0x57
+#define DIS_RA2 0x04
+#define DIS_RA3 0x08
+#define DRWTIM23 0x58
+#define BRST 0x59
+
+static ide_tuneproc_t cmd640_tune_drive;
+
+/* Interface to access cmd640x registers */
+static void (*put_cmd640_reg)(int reg_no, int val);
+static byte (*get_cmd640_reg)(int reg_no);
+
+enum { none, vlb, pci1, pci2 };
+static int bus_type = none;
+static int cmd640_chip_version;
+static int cmd640_key;
+static int bus_speed; /* MHz */
+
+/*
+ * For some unknown reasons pcibios functions which read and write registers
+ * do not always work with cmd640. We use direct IO instead.
+ */
+
+/* PCI method 1 access */
+
+static void put_cmd640_reg_pci1(int reg_no, int val)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outl_p((reg_no & 0xfc) | cmd640_key, 0xcf8);
+ outb_p(val, (reg_no & 3) + 0xcfc);
+ restore_flags(flags);
+}
+
+static byte get_cmd640_reg_pci1(int reg_no)
+{
+ byte b;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outl_p((reg_no & 0xfc) | cmd640_key, 0xcf8);
+ b = inb_p(0xcfc + (reg_no & 3));
+ restore_flags(flags);
+ return b;
+}
+
+/* PCI method 2 access (from CMD datasheet) */
+
+static void put_cmd640_reg_pci2(int reg_no, int val)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(0x10, 0xcf8);
+ outb_p(val, cmd640_key + reg_no);
+ outb_p(0, 0xcf8);
+ restore_flags(flags);
+}
+
+static byte get_cmd640_reg_pci2(int reg_no)
+{
+ byte b;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(0x10, 0xcf8);
+ b = inb_p(cmd640_key + reg_no);
+ outb_p(0, 0xcf8);
+ restore_flags(flags);
+ return b;
+}
+
+/* VLB access */
+
+static void put_cmd640_reg_vlb(int reg_no, int val)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(reg_no, cmd640_key + 8);
+ outb_p(val, cmd640_key + 0xc);
+ restore_flags(flags);
+}
+
+static byte get_cmd640_reg_vlb(int reg_no)
+{
+ byte b;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(reg_no, cmd640_key + 8);
+ b = inb_p(cmd640_key + 0xc);
+ restore_flags(flags);
+ return b;
+}
+
+/*
+ * Probe for CMD640x -- pci method 1
+ */
+
+static int probe_for_cmd640_pci1(void)
+{
+ long id;
+ int k;
+
+ for (k = 0x80000000; k <= 0x8000f800; k += 0x800) {
+ outl(k, 0xcf8);
+ id = inl(0xcfc);
+ if (id != 0x06401095)
+ continue;
+ put_cmd640_reg = put_cmd640_reg_pci1;
+ get_cmd640_reg = get_cmd640_reg_pci1;
+ cmd640_key = k;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Probe for CMD640x -- pci method 2
+ */
+
+static int probe_for_cmd640_pci2(void)
+{
+ int i;
+ int v_id;
+ int d_id;
+
+ for (i = 0xc000; i <= 0xcf00; i += 0x100) {
+ outb(0x10, 0xcf8);
+ v_id = inw(i);
+ d_id = inw(i + 2);
+ outb(0, 0xcf8);
+ if (v_id != 0x1095 || d_id != 0x640)
+ continue;
+ put_cmd640_reg = put_cmd640_reg_pci2;
+ get_cmd640_reg = get_cmd640_reg_pci2;
+ cmd640_key = i;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Probe for CMD640x -- vlb
+ */
+
+static int probe_for_cmd640_vlb(void) {
+ byte b;
+
+ outb(CFR, 0x178);
+ b = inb(0x17c);
+ if (b == 0xff || b == 0 || (b & CFR_AT_VESA_078h)) {
+ outb(CFR, 0x78);
+ b = inb(0x7c);
+ if (b == 0xff || b == 0 || !(b & CFR_AT_VESA_078h))
+ return 0;
+ cmd640_key = 0x70;
+ } else {
+ cmd640_key = 0x170;
+ }
+ put_cmd640_reg = put_cmd640_reg_vlb;
+ get_cmd640_reg = get_cmd640_reg_vlb;
+ return 1;
+}
+
+/*
+ * Low level reset for controller, actually it has nothing specific for
+ * CMD640, but I don't know how to use standard reset routine before
+ * we recognized any drives.
+ */
+
+static void cmd640_reset_controller(int iface_no)
+{
+ int retry_count = 600;
+ int base_port = iface_no ? 0x170 : 0x1f0;
+
+ outb_p(4, base_port + 7);
+ udelay(5);
+ outb_p(0, base_port + 7);
+
+ do {
+ udelay(5);
+ retry_count -= 1;
+ } while ((inb_p(base_port + 7) & 0x80) && retry_count);
+
+ if (retry_count == 0)
+ printk("cmd640: failed to reset controller %d\n", iface_no);
+#if 0
+ else
+ printk("cmd640: controller %d reset [%d]\n",
+ iface_no, retry_count);
+#endif
+}
+
+/*
+ * Probe for Cmd640x and initialize it if found
+ */
+
+int ide_probe_for_cmd640x(void)
+{
+ int second_port;
+ byte b;
+
+ if (probe_for_cmd640_pci1()) {
+ bus_type = pci1;
+ } else if (probe_for_cmd640_pci2()) {
+ bus_type = pci2;
+ } else if (cmd640_vlb && probe_for_cmd640_vlb()) {
+ /* May be remove cmd640_vlb at all, and probe in any case */
+ bus_type = vlb;
+ } else {
+ return 0;
+ }
+
+ ide_hwifs[0].serialized = 1; /* ensure this *always* gets set */
+
+#if 0
+ /* Dump initial state of chip registers */
+ for (b = 0; b != 0xff; b++) {
+ printk(" %2x%c", get_cmd640_reg(b),
+ ((b&0xf) == 0xf) ? '\n' : ',');
+ }
+
+#endif
+
+ /*
+ * Undocumented magic. (There is no 0x5b port in specs)
+ */
+
+ put_cmd640_reg(0x5b, 0xbd);
+ if (get_cmd640_reg(0x5b) != 0xbd) {
+ printk("ide: can't initialize cmd640 -- wrong value in 0x5b\n");
+ return 0;
+ }
+ put_cmd640_reg(0x5b, 0);
+
+ /*
+ * Documented magic.
+ */
+
+ cmd640_chip_version = get_cmd640_reg(CFR) & CFR_DEVREV;
+ if (cmd640_chip_version == 0) {
+ printk ("ide: wrong CMD640 version -- 0\n");
+ return 0;
+ }
+
+ /*
+ * Setup the most conservative timings for all drives,
+ */
+ put_cmd640_reg(ARTTIM0, 0xc0);
+ put_cmd640_reg(ARTTIM1, 0xc0);
+ put_cmd640_reg(ARTTIM23, 0xcc); /* 0xc0? */
+
+ /*
+ * Do not initialize secondary controller for vlbus
+ */
+ second_port = (bus_type != vlb);
+
+ /*
+ * Set the maximum allowed bus speed (it is safest until we
+ * find how to detect bus speed)
+ * Normally PCI bus runs at 33MHz, but often works overclocked to 40
+ */
+ bus_speed = (bus_type == vlb) ? 50 : 40;
+
+ /*
+ * Setup Control Register
+ */
+ b = get_cmd640_reg(CNTRL);
+
+ if (second_port)
+ b |= CNTRL_ENA_2ND;
+ else
+ b &= ~CNTRL_ENA_2ND;
+
+ /*
+ * Disable readahead for drives at primary interface
+ */
+ b |= (CNTRL_DIS_RA0 | CNTRL_DIS_RA1);
+
+ put_cmd640_reg(CNTRL, b);
+
+ /*
+ * Note that we assume that the first interface is at 0x1f0,
+ * and that the second interface, if enabled, is at 0x170.
+ */
+ ide_hwifs[0].chipset = ide_cmd640;
+ ide_hwifs[0].tuneproc = &cmd640_tune_drive;
+ if (ide_hwifs[0].drives[0].autotune == 0)
+ ide_hwifs[0].drives[0].autotune = 1;
+ if (ide_hwifs[0].drives[1].autotune == 0)
+ ide_hwifs[0].drives[1].autotune = 1;
+
+ /*
+ * Initialize 2nd IDE port, if required
+ */
+ if (second_port) {
+ ide_hwifs[1].chipset = ide_cmd640;
+ ide_hwifs[1].tuneproc = &cmd640_tune_drive;
+ if (ide_hwifs[1].drives[0].autotune == 0)
+ ide_hwifs[1].drives[0].autotune = 1;
+ if (ide_hwifs[1].drives[1].autotune == 0)
+ ide_hwifs[1].drives[1].autotune = 1;
+ /* We reset timings, and disable read-ahead */
+ put_cmd640_reg(ARTTIM23, (DIS_RA2 | DIS_RA3));
+ put_cmd640_reg(DRWTIM23, 0);
+
+ cmd640_reset_controller(1);
+ }
+
+ printk("ide: buggy CMD640%c interface at ",
+ 'A' - 1 + cmd640_chip_version);
+ switch (bus_type) {
+ case vlb :
+ printk("local bus, port 0x%x", cmd640_key);
+ break;
+ case pci1:
+ printk("pci, (0x%x)", cmd640_key);
+ break;
+ case pci2:
+ printk("pci,(access method 2) (0x%x)", cmd640_key);
+ break;
+ }
+
+ /*
+ * Reset interface timings
+ */
+ put_cmd640_reg(CMDTIM, 0);
+
+ printk("\n ... serialized, secondary interface %s\n",
+ second_port ? "enabled" : "disabled");
+
+ return 1;
+}
+
+int cmd640_off(void) {
+ static int a = 0;
+ byte b;
+
+ if (bus_type == none || a == 1)
+ return 0;
+ a = 1;
+ b = get_cmd640_reg(CNTRL);
+ b &= ~CNTRL_ENA_2ND;
+ put_cmd640_reg(CNTRL, b);
+ return 1;
+}
+
+/*
+ * Sets readahead mode for specific drive
+ * in the future it could be called from ioctl
+ */
+
+static void set_readahead_mode(int mode, int if_num, int dr_num)
+{
+ static int masks[2][2] =
+ {
+ {CNTRL_DIS_RA0, CNTRL_DIS_RA1},
+ {DIS_RA2, DIS_RA3}
+ };
+
+ int port = (if_num == 0) ? CNTRL : ARTTIM23;
+ int mask = masks[if_num][dr_num];
+ byte b;
+
+ b = get_cmd640_reg(port);
+ if (mode)
+ b &= ~mask; /* Enable readahead for specific drive */
+ else
+ b |= mask; /* Disable readahed for specific drive */
+ put_cmd640_reg(port, b);
+}
+
+static struct readahead_black_list {
+ const char* name;
+ int mode;
+} drives_ra[] = {
+ { "ST3655A", 0 },
+ { "SAMSUNG", 0 }, /* Be conservative */
+ { NULL, 0 }
+};
+
+static int strmatch(const char* pattern, const char* name) {
+ char c1, c2;
+
+ while (1) {
+ c1 = *pattern++;
+ c2 = *name++;
+ if (c1 == 0) {
+ return 0;
+ }
+ if (c1 != c2)
+ return 1;
+ }
+}
+
+static int known_drive_readahead(char* name) {
+ int i;
+
+ for (i = 0; drives_ra[i].name != NULL; i++) {
+ if (strmatch(drives_ra[i].name, name) == 0) {
+ return drives_ra[i].mode;
+ }
+ }
+ return -1;
+}
+
+static int arttim[4] = {2, 2, 2, 2}; /* Address setup count (in clocks) */
+static int a_count[4] = {1, 1, 1, 1}; /* Active count (encoded) */
+static int r_count[4] = {1, 1, 1, 1}; /* Recovery count (encoded) */
+
+/*
+ * Convert address setup count from number of clocks
+ * to representation used by controller
+ */
+
+inline static int pack_arttim(int clocks)
+{
+ if (clocks <= 2) return 0x40;
+ else if (clocks == 3) return 0x80;
+ else if (clocks == 4) return 0x00;
+ else return 0xc0;
+}
+
+/*
+ * Pack active and recovery counts into single byte representation
+ * used by controller
+ */
+
+inline static int pack_counts(int act_count, int rec_count)
+{
+ return ((act_count & 0x0f)<<4) | (rec_count & 0x0f);
+}
+
+inline int max(int a, int b) { return a > b ? a : b; }
+inline int max4(int *p) { return max(p[0], max(p[1], max(p[2], p[3]))); }
+
+/*
+ * Set timing parameters
+ */
+
+static void cmd640_set_timing(int if_num, int dr_num)
+{
+ int b_reg;
+ int ac, rc, at;
+
+ /*
+ * Set address setup count and drive read/write timing registers.
+ * Primary interface has individual count/timing registers for
+ * each drive. Secondary interface has common set of registers, and
+ * we should set timings for the slowest drive.
+ */
+
+ if (if_num == 0) {
+ b_reg = dr_num ? ARTTIM1 : ARTTIM0;
+ at = arttim[dr_num];
+ ac = a_count[dr_num];
+ rc = r_count[dr_num];
+ } else {
+ b_reg = ARTTIM23;
+ at = max(arttim[2], arttim[3]);
+ ac = max(a_count[2], a_count[3]);
+ rc = max(r_count[2], r_count[3]);
+ }
+
+ put_cmd640_reg(b_reg, pack_arttim(at));
+ put_cmd640_reg(b_reg + 1, pack_counts(ac, rc));
+
+ /*
+ * Update CMDTIM (IDE Command Block Timing Register)
+ */
+
+ ac = max4(r_count);
+ rc = max4(a_count);
+ put_cmd640_reg(CMDTIM, pack_counts(ac, rc));
+}
+
+/*
+ * Standard timings for PIO modes
+ */
+
+static struct pio_timing {
+ int mc_time; /* Minimal cycle time (ns) */
+ int av_time; /* Address valid to DIOR-/DIOW- setup (ns) */
+ int ds_time; /* DIOR data setup (ns) */
+} pio_timings[6] = {
+ { 70, 165, 600 }, /* PIO Mode 0 */
+ { 50, 125, 383 }, /* PIO Mode 1 */
+ { 30, 100, 240 }, /* PIO Mode 2 */
+ { 30, 80, 180 }, /* PIO Mode 3 */
+ { 25, 70, 125 }, /* PIO Mode 4 -- should be 120, not 125 */
+ { 20, 50, 100 } /* PIO Mode ? (nonstandard) */
+};
+
+static void cmd640_timings_to_clocks(int mc_time, int av_time, int ds_time,
+ int clock_time, int drv_idx)
+{
+ int a, b;
+
+ arttim[drv_idx] = (mc_time + clock_time - 1)/clock_time;
+
+ a = (av_time + clock_time - 1)/clock_time;
+ if (a < 2)
+ a = 2;
+ b = (ds_time + clock_time - 1)/clock_time - a;
+ if (b < 2)
+ b = 2;
+ if (b > 0x11) {
+ a += b - 0x11;
+ b = 0x11;
+ }
+ if (a > 0x10)
+ a = 0x10;
+ if (cmd640_chip_version > 1)
+ b -= 1;
+ if (b > 0x10)
+ b = 0x10;
+
+ a_count[drv_idx] = a;
+ r_count[drv_idx] = b;
+}
+
+static void set_pio_mode(int if_num, int drv_num, int mode_num) {
+ int p_base;
+ int i;
+
+ p_base = if_num ? 0x170 : 0x1f0;
+ outb_p(3, p_base + 1);
+ outb_p(mode_num | 8, p_base + 2);
+ outb_p((drv_num | 0xa) << 4, p_base + 6);
+ outb_p(0xef, p_base + 7);
+ for (i = 0; (i < 100) && (inb (p_base + 7) & 0x80); i++)
+ udelay(10000);
+}
+
+/*
+ * Set a specific pio_mode for a drive
+ */
+
+static void cmd640_set_mode(ide_drive_t* drive, int pio_mode) {
+ int interface_number;
+ int drive_number;
+ int clock_time; /* ns */
+ int mc_time, av_time, ds_time;
+
+ interface_number = HWIF(drive)->index;
+ drive_number = drive->select.b.unit;
+ clock_time = 1000/bus_speed;
+
+ mc_time = pio_timings[pio_mode].mc_time;
+ av_time = pio_timings[pio_mode].av_time;
+ ds_time = pio_timings[pio_mode].ds_time;
+
+ cmd640_timings_to_clocks(mc_time, av_time, ds_time, clock_time,
+ interface_number*2 + drive_number);
+ set_pio_mode(interface_number, drive_number, pio_mode);
+ cmd640_set_timing(interface_number, drive_number);
+}
+
+/*
+ * Drive PIO mode "autoconfiguration".
+ * Ideally, this code should *always* call cmd640_set_mode(), but it doesn't.
+ */
+
+static void cmd640_tune_drive(ide_drive_t *drive, byte pio_mode) {
+ int interface_number;
+ int drive_number;
+ int clock_time; /* ns */
+ int max_pio;
+ int mc_time, av_time, ds_time;
+ struct hd_driveid* id;
+ int readahead; /* there is a global named read_ahead */
+
+ if (pio_mode != 255) {
+ cmd640_set_mode(drive, pio_mode);
+ return;
+ }
+
+ interface_number = HWIF(drive)->index;
+ drive_number = drive->select.b.unit;
+ clock_time = 1000/bus_speed;
+ id = drive->id;
+ if ((max_pio = ide_scan_pio_blacklist(id->model)) != -1) {
+ ds_time = pio_timings[max_pio].ds_time;
+ } else {
+ max_pio = id->tPIO;
+ ds_time = pio_timings[max_pio].ds_time;
+ if (id->field_valid & 2) {
+ if ((id->capability & 8) && (id->eide_pio_modes & 7)) {
+ if (id->eide_pio_modes & 4) max_pio = 5;
+ else if (id->eide_pio_modes & 2) max_pio = 4;
+ else max_pio = 3;
+ ds_time = id->eide_pio_iordy;
+ } else {
+ ds_time = id->eide_pio;
+ }
+ if (ds_time == 0)
+ ds_time = pio_timings[max_pio].ds_time;
+ }
+
+ /*
+ * Conservative "downgrade"
+ */
+ if (max_pio < 4 && max_pio != 0) {
+ max_pio -= 1;
+ ds_time = pio_timings[max_pio].ds_time;
+ }
+ }
+ mc_time = pio_timings[max_pio].mc_time;
+ av_time = pio_timings[max_pio].av_time;
+ cmd640_timings_to_clocks(mc_time, av_time, ds_time, clock_time,
+ interface_number*2 + drive_number);
+ set_pio_mode(interface_number, drive_number, max_pio);
+ cmd640_set_timing(interface_number, drive_number);
+
+ /*
+ * Disable (or set) readahead mode
+ */
+
+ readahead = 0;
+ if (cmd640_chip_version > 1) { /* Mmmm.. probably should be > 2 ?? */
+ readahead = known_drive_readahead(id->model);
+ if (readahead == -1)
+ readahead = 1; /* Mmmm.. probably be 0 ?? */
+ set_readahead_mode(readahead, interface_number, drive_number);
+ }
+
+ printk ("Mode and Timing set to PIO%d, Readahead is %s\n",
+ max_pio, readahead ? "enabled" : "disabled");
+}
+
diff --git a/i386/i386at/gpl/linux/block/floppy.c b/i386/i386at/gpl/linux/block/floppy.c
new file mode 100644
index 0000000..ee4a898
--- /dev/null
+++ b/i386/i386at/gpl/linux/block/floppy.c
@@ -0,0 +1,4100 @@
+/*
+ * linux/kernel/floppy.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 1993, 1994 Alain Knaff
+ */
+/*
+ * 02.12.91 - Changed to static variables to indicate need for reset
+ * and recalibrate. This makes some things easier (output_byte reset
+ * checking etc), and means less interrupt jumping in case of errors,
+ * so the code is hopefully easier to understand.
+ */
+
+/*
+ * This file is certainly a mess. I've tried my best to get it working,
+ * but I don't like programming floppies, and I have only one anyway.
+ * Urgel. I should check for more errors, and do more graceful error
+ * recovery. Seems there are problems with several drives. I've tried to
+ * correct them. No promises.
+ */
+
+/*
+ * As with hd.c, all routines within this file can (and will) be called
+ * by interrupts, so extreme caution is needed. A hardware interrupt
+ * handler may not sleep, or a kernel panic will happen. Thus I cannot
+ * call "floppy-on" directly, but have to set a special timer interrupt
+ * etc.
+ */
+
+/*
+ * 28.02.92 - made track-buffering routines, based on the routines written
+ * by entropy@wintermute.wpi.edu (Lawrence Foard). Linus.
+ */
+
+/*
+ * Automatic floppy-detection and formatting written by Werner Almesberger
+ * (almesber@nessie.cs.id.ethz.ch), who also corrected some problems with
+ * the floppy-change signal detection.
+ */
+
+/*
+ * 1992/7/22 -- Hennus Bergman: Added better error reporting, fixed
+ * FDC data overrun bug, added some preliminary stuff for vertical
+ * recording support.
+ *
+ * 1992/9/17: Added DMA allocation & DMA functions. -- hhb.
+ *
+ * TODO: Errors are still not counted properly.
+ */
+
+/* 1992/9/20
+ * Modifications for ``Sector Shifting'' by Rob Hooft (hooft@chem.ruu.nl)
+ * 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.
+ * There is a bug in the driver that makes it impossible to format a
+ * floppy as the first thing after bootup.
+ */
+
+/*
+ * 1993/4/29 -- Linus -- cleaned up the timer handling in the kernel, and
+ * this helped the floppy driver as well. Much cleaner, and still seems to
+ * work.
+ */
+
+/* 1994/6/24 --bbroad-- added the floppy table entries and made
+ * minor modifications to allow 2.88 floppies to be run.
+ */
+
+/* 1994/7/13 -- Paul Vojta -- modified the probing code to allow three or more
+ * disk types.
+ */
+
+/*
+ * 1994/8/8 -- Alain Knaff -- Switched to fdpatch driver: Support for bigger
+ * format bug fixes, but unfortunately some new bugs too...
+ */
+
+/* 1994/9/17 -- Koen Holtman -- added logging of physical floppy write
+ * errors to allow safe writing by specialized programs.
+ */
+
+/* 1995/4/24 -- Dan Fandrich -- added support for Commodore 1581 3.5" disks
+ * by defining bit 1 of the "stretch" parameter to mean put sectors on the
+ * opposite side of the disk, leaving the sector IDs alone (i.e. Commodore's
+ * drives are "upside-down").
+ */
+
+/*
+ * 1995/8/26 -- Andreas Busse -- added Mips support.
+ */
+
+/*
+ * 1995/10/18 -- Ralf Baechle -- Portability cleanup; move machine dependend
+ * features to asm/floppy.h.
+ */
+
+
+#define FLOPPY_SANITY_CHECK
+#undef FLOPPY_SILENT_DCL_CLEAR
+
+#define REALLY_SLOW_IO
+
+#define DEBUGT 2
+#define DCL_DEBUG /* debug disk change line */
+
+/* do print messages for unexpected interrupts */
+static int print_unex=1;
+#include <linux/utsname.h>
+#include <linux/module.h>
+
+/* the following is the mask of allowed drives. By default units 2 and
+ * 3 of both floppy controllers are disabled, because switching on the
+ * motor of these drives causes system hangs on some PCI computers. drive
+ * 0 is the low bit (0x1), and drive 7 is the high bit (0x80). Bits are on if
+ * a drive is allowed. */
+static int FLOPPY_IRQ=6;
+static int FLOPPY_DMA=2;
+static int allowed_drive_mask = 0x33;
+
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/tqueue.h>
+#define FDPATCHES
+#include <linux/fdreg.h>
+
+
+#include <linux/fd.h>
+
+
+#define OLDFDRAWCMD 0x020d /* send a raw command to the fdc */
+
+struct old_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;
+};
+
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/mc146818rtc.h> /* CMOS defines */
+#include <linux/ioport.h>
+
+#include <asm/dma.h>
+#include <asm/floppy.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#define MAJOR_NR FLOPPY_MAJOR
+
+#include <linux/blk.h>
+
+
+/* Dma Memory related stuff */
+
+/* Pure 2^n version of get_order */
+static inline int __get_order (int size)
+{
+ int order;
+
+#ifdef _ASM_IO_H2
+ __asm__ __volatile__("bsr %1,%0"
+ : "=r" (order)
+ : "r" (size / PAGE_SIZE));
+#else
+ for (order = 0; order < NR_MEM_LISTS; ++order)
+ if (size <= (PAGE_SIZE << order))
+ return order;
+#endif
+ return NR_MEM_LISTS;
+}
+
+static unsigned long dma_mem_alloc(int size)
+{
+ int order = __get_order(size);
+
+ if (order >= NR_MEM_LISTS)
+ return(0);
+ return __get_dma_pages(GFP_KERNEL,order);
+}
+
+/* End dma memory related stuff */
+
+static unsigned int fake_change = 0;
+static int initialising=1;
+
+static inline int TYPE(kdev_t x) {
+ return (MINOR(x)>>2) & 0x1f;
+}
+static inline int DRIVE(kdev_t x) {
+ return (MINOR(x)&0x03) | ((MINOR(x)&0x80) >> 5);
+}
+#define ITYPE(x) (((x)>>2) & 0x1f)
+#define TOMINOR(x) ((x & 3) | ((x & 4) << 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 DRWE (&write_errors[current_drive])
+#define FDCS (&fdc_state[fdc])
+#define CLEARF(x) (clear_bit(x##_BIT, &DRS->flags))
+#define SETF(x) (set_bit(x##_BIT, &DRS->flags))
+#define TESTF(x) (test_bit(x##_BIT, &DRS->flags))
+
+#define UDP (&drive_params[drive])
+#define UDRS (&drive_state[drive])
+#define UDRWE (&write_errors[drive])
+#define UFDCS (&fdc_state[FDC(drive)])
+#define UCLEARF(x) (clear_bit(x##_BIT, &UDRS->flags))
+#define USETF(x) (set_bit(x##_BIT, &UDRS->flags))
+#define UTESTF(x) (test_bit(x##_BIT, &UDRS->flags))
+
+#define DPRINT(x) printk(DEVICE_NAME "%d: " x,current_drive)
+
+#define DPRINT1(x,x1) printk(DEVICE_NAME "%d: " x,current_drive,(x1))
+
+#define DPRINT2(x,x1,x2) printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2))
+
+#define DPRINT3(x,x1,x2,x3) printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2),(x3))
+
+#define PH_HEAD(floppy,head) (((((floppy)->stretch & 2) >>1) ^ head) << 2)
+#define STRETCH(floppy) ((floppy)->stretch & FD_STRETCH)
+
+#define CLEARSTRUCT(x) memset((x), 0, sizeof(*(x)))
+
+/* 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.
+ * [Now it is rather a minimum]
+ */
+#define MAX_DISK_SIZE 2 /* 3984*/
+
+#define K_64 0x10000 /* 64KB */
+
+/*
+ * globals used by 'result()'
+ */
+#define MAX_REPLIES 17
+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])
+
+#define SEL_DLY (2*HZ/100)
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+/*
+ * this struct defines the different floppy drive types.
+ */
+static struct {
+ struct floppy_drive_params params;
+ const 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, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 80, 3*HZ, 20, {3,1,2,0,2}, 0,
+ 0, { 7, 4, 8, 2, 1, 5, 3,10}, 3*HZ/2, 0 }, "unknown" },
+
+{{1, 300, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 40, 3*HZ, 17, {3,1,2,0,2}, 0,
+ 0, { 1, 0, 0, 0, 0, 0, 0, 0}, 3*HZ/2, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/
+
+{{2, 500, 16, 16, 6000, 4*HZ/10, 3*HZ, 14, SEL_DLY, 6, 83, 3*HZ, 17, {3,1,2,0,2}, 0,
+ 0, { 2, 5, 6,23,10,20,11, 0}, 3*HZ/2, 2 }, "1.2M" }, /*5 1/4 HD AT*/
+
+{{3, 250, 16, 16, 3000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
+ 0, { 4,22,21,30, 3, 0, 0, 0}, 3*HZ/2, 4 }, "720k" }, /*3 1/2 DD*/
+
+{{4, 500, 16, 16, 4000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
+ 0, { 7, 4,25,22,31,21,29,11}, 3*HZ/2, 7 }, "1.44M" }, /*3 1/2 HD*/
+
+{{5, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
+ 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/
+
+{{6, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
+ 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 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 drive_state[N_DRIVE];
+static struct floppy_write_errors write_errors[N_DRIVE];
+static struct floppy_raw_cmd *raw_cmd, default_raw_cmd;
+
+/*
+ * This struct defines the different floppy types.
+ *
+ * Bit 0 of 'stretch' tells if the tracks need to be doubled for some
+ * types (e.g. 360kB diskette in 1.2MB drive, etc.). Bit 1 of 'stretch'
+ * tells if the disk is in Commodore 1581 format, which means side 0 sectors
+ * are located on side 1 of the disk but with a side 0 ID, and vice-versa.
+ * This is the same as the Sharp MZ-80 5.25" CP/M disk format, except that the
+ * 1581's logical side 0 is on physical side 1, whereas the Sharp's logical
+ * side 0 is on physical side 0 (but with the misnamed sector IDs).
+ * 'stretch' should probably be renamed to something more general, like
+ * 'options'. Other parameters should be self-explanatory (see also
+ * setfdprm(8)).
+ */
+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 */
+ { 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"h1200" }, /* 2 1.2MB AT */
+ { 720, 9,1,80,0,0x2A,0x02,0xDF,0x50,"D360" }, /* 3 360KB SS 3.5" */
+ { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"D720" }, /* 4 720KB 3.5" */
+ { 720, 9,2,40,1,0x23,0x01,0xDF,0x50,"h360" }, /* 5 360KB AT */
+ { 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,"h720" }, /* 6 720KB AT */
+ { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"H1440" }, /* 7 1.44MB 3.5" */
+ { 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"E2880" }, /* 8 2.88MB 3.5" */
+ { 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,0x0C,"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,0x00,"h880" }, /* 20 880KB 5.25" */
+ { 2080,13,2,80,0,0x1C,0x01,0xCF,0x00,"D1040" }, /* 21 1.04MB 3.5" */
+ { 2240,14,2,80,0,0x1C,0x19,0xCF,0x00,"D1120" }, /* 22 1.12MB 3.5" */
+ { 3200,20,2,80,0,0x1C,0x20,0xCF,0x2C,"h1600" }, /* 23 1.6MB 5.25" */
+ { 3520,22,2,80,0,0x1C,0x08,0xCF,0x2e,"H1760" }, /* 24 1.76MB 3.5" */
+ { 3840,24,2,80,0,0x1C,0x20,0xCF,0x00,"H1920" }, /* 25 1.92MB 3.5" */
+ { 6400,40,2,80,0,0x25,0x5B,0xCF,0x00,"E3200" }, /* 26 3.20MB 3.5" */
+ { 7040,44,2,80,0,0x25,0x5B,0xCF,0x00,"E3520" }, /* 27 3.52MB 3.5" */
+ { 7680,48,2,80,0,0x25,0x63,0xCF,0x00,"E3840" }, /* 28 3.84MB 3.5" */
+
+ { 3680,23,2,80,0,0x1C,0x10,0xCF,0x00,"H1840" }, /* 29 1.84MB 3.5" */
+ { 1600,10,2,80,0,0x25,0x02,0xDF,0x2E,"D800" }, /* 30 800KB 3.5" */
+ { 3200,20,2,80,0,0x1C,0x00,0xCF,0x2C,"H1600" }, /* 31 1.6MB 3.5" */
+};
+
+#define NUMBER(x) (sizeof(x) / sizeof(*(x)))
+#define SECTSIZE (_FD_SECTSIZE(*floppy))
+
+/* Auto-detection: Disk type used until the next media change occurs. */
+static struct floppy_struct *current_type[N_DRIVE] = {
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL
+};
+
+/*
+ * User-provided type information. current_type points to
+ * the respective entry of this array.
+ */
+static struct floppy_struct user_params[N_DRIVE];
+
+static int floppy_sizes[256];
+static int floppy_blocksizes[256] = { 0, };
+
+/*
+ * The driver is trying to determine the correct media format
+ * while probing is set. rw_interrupt() clears it after a
+ * successful access.
+ */
+static int probing = 0;
+
+/* Synchronization of FDC access. */
+#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;
+#ifdef MACH
+extern int issig (void);
+#define NO_SIGNAL (! issig () || ! interruptible)
+#else
+#define NO_SIGNAL (!(current->signal & ~current->blocked) || !interruptible)
+#endif
+#define CALL(x) if ((x) == -EINTR) return -EINTR
+#define ECALL(x) if ((ret = (x))) return ret;
+#define _WAIT(x,i) CALL(ret=wait_til_done((x),i))
+#define WAIT(x) _WAIT((x),interruptible)
+#define IWAIT(x) _WAIT((x),1)
+
+/* Errors during formatting are counted here. */
+static int format_errors;
+
+/* Format request descriptor. */
+static struct format_descr format_req;
+
+/*
+ * Rate is 0 for 500kb/s, 1 for 300kbps, 2 for 250kbps
+ * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
+ * H is head unload time (1=16ms, 2=32ms, etc)
+ */
+
+/*
+ * Track buffer
+ * Because these are written to by the DMA controller, they must
+ * not contain a 64k byte boundary crossing, or data will be
+ * corrupted/lost. Alignment of these is enforced in boot/head.S.
+ * Note that you must not change the sizes below without updating head.S.
+ */
+static char *floppy_track_buffer=0;
+static int max_buffer_sectors=0;
+
+static int *errors;
+typedef void (*done_f)(int);
+static 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=NULL;
+
+static void floppy_ready(void);
+static void floppy_start(void);
+static void process_fd_request(void);
+static void recalibrate_floppy(void);
+static void floppy_shutdown(void);
+
+static int floppy_grab_irq_and_dma(void);
+static 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 -1
+#define NEED_1_RECAL -2
+#define NEED_2_RECAL -3
+
+/* */
+static int usage_count = 0;
+
+
+/* buffer related variables */
+static int buffer_track = -1;
+static int buffer_drive = -1;
+static int buffer_min = -1;
+static int buffer_max = -1;
+
+/* fdc related variables, should end up in a struct */
+static struct floppy_fdc_state fdc_state[N_FDC];
+static int fdc; /* current fdc */
+
+static struct floppy_struct * floppy = floppy_type;
+static unsigned char current_drive = 0;
+static long current_count_sectors = 0;
+static unsigned char sector_t; /* sector in track */
+
+#ifdef DEBUGT
+static long unsigned debugtimer;
+#endif
+
+/*
+ * Debugging
+ * =========
+ */
+static inline void set_debugt(void)
+{
+#ifdef DEBUGT
+ debugtimer = jiffies;
+#endif
+}
+
+static inline void debugt(const char *message)
+{
+#ifdef DEBUGT
+ if (DP->flags & DEBUGT)
+ printk("%s dtime=%lu\n", message, jiffies-debugtimer);
+#endif
+}
+
+typedef void (*timeout_fn)(unsigned long);
+static struct timer_list fd_timeout ={ NULL, NULL, 0, 0,
+ (timeout_fn) floppy_shutdown };
+
+static const char *timeout_message;
+
+#ifdef FLOPPY_SANITY_CHECK
+static void is_alive(const char *message)
+{
+ /* this routine checks whether the floppy driver is "alive" */
+ if (fdc_busy && command_status < 2 && !fd_timeout.prev){
+ DPRINT1("timeout handler died: %s\n",message);
+ }
+}
+#endif
+
+#ifdef FLOPPY_SANITY_CHECK
+
+#define OLOGSIZE 20
+
+static void (*lasthandler)(void) = NULL;
+static int interruptjiffies=0;
+static int resultjiffies=0;
+static int resultsize=0;
+static int lastredo=0;
+
+static struct output_log {
+ unsigned char data;
+ unsigned char status;
+ unsigned long jiffies;
+} output_log[OLOGSIZE];
+
+static int output_log_pos=0;
+#endif
+
+#define CURRENTD -1
+#define MAXTIMEOUT -2
+
+static void reschedule_timeout(int drive, const char *message, int marg)
+{
+ if (drive == CURRENTD)
+ drive = current_drive;
+ del_timer(&fd_timeout);
+ if (drive < 0 || drive > N_DRIVE) {
+ fd_timeout.expires = jiffies + 20*HZ;
+ drive=0;
+ } else
+ fd_timeout.expires = jiffies + UDP->timeout;
+ add_timer(&fd_timeout);
+ if (UDP->flags & FD_DEBUG){
+ DPRINT("reschedule timeout ");
+ printk(message, marg);
+ printk("\n");
+ }
+ timeout_message = message;
+}
+
+static int maximum(int a, int b)
+{
+ if(a > b)
+ return a;
+ else
+ return b;
+}
+#define INFBOUND(a,b) (a)=maximum((a),(b));
+
+static int minimum(int a, int b)
+{
+ if(a < b)
+ return a;
+ else
+ return b;
+}
+#define SUPBOUND(a,b) (a)=minimum((a),(b));
+
+
+/*
+ * Bottom half floppy driver.
+ * ==========================
+ *
+ * This part of the file contains the code talking directly to the hardware,
+ * and also the main service loop (seek-configure-spinup-command)
+ */
+
+/*
+ * disk change.
+ * This routine is responsible for maintaining the FD_DISK_CHANGE flag,
+ * and the last_checked date.
+ *
+ * last_checked is the date of the last check which showed 'no disk change'
+ * FD_DISK_CHANGE is set under two conditions:
+ * 1. The floppy has been changed after some i/o to that floppy already
+ * took place.
+ * 2. No floppy disk is in the drive. This is done in order to ensure that
+ * requests are quickly flushed in case there is no disk in the drive. It
+ * follows that FD_DISK_CHANGE can only be cleared if there is a disk in
+ * the drive.
+ *
+ * For 1., maxblock is observed. Maxblock is 0 if no i/o has taken place yet.
+ * For 2., FD_DISK_NEWCHANGE is watched. FD_DISK_NEWCHANGE is cleared on
+ * each seek. If a disk is present, the disk change line should also be
+ * cleared on each seek. Thus, if FD_DISK_NEWCHANGE is clear, but the disk
+ * change line is set, this means either that no disk is in the drive, or
+ * that it has been removed since the last seek.
+ *
+ * This means that we really have a third possibility too:
+ * The floppy has been changed after the last seek.
+ */
+
+static int disk_change(int drive)
+{
+ int fdc=FDC(drive);
+#ifdef FLOPPY_SANITY_CHECK
+ if (jiffies < UDP->select_delay + UDRS->select_date)
+ DPRINT("WARNING disk change called early\n");
+ if (!(FDCS->dor & (0x10 << UNIT(drive))) ||
+ (FDCS->dor & 3) != UNIT(drive) ||
+ fdc != FDC(drive)){
+ DPRINT("probing disk change on unselected drive\n");
+ DPRINT3("drive=%d fdc=%d dor=%x\n",drive, FDC(drive),
+ FDCS->dor);
+ }
+#endif
+
+#ifdef DCL_DEBUG
+ if (UDP->flags & FD_DEBUG){
+ DPRINT1("checking disk change line for drive %d\n",drive);
+ DPRINT1("jiffies=%ld\n", jiffies);
+ DPRINT1("disk change line=%x\n",fd_inb(FD_DIR)&0x80);
+ DPRINT1("flags=%x\n",UDRS->flags);
+ }
+#endif
+ if (UDP->flags & FD_BROKEN_DCL)
+ return UTESTF(FD_DISK_CHANGED);
+ if ((fd_inb(FD_DIR) ^ UDP->flags) & 0x80){
+ USETF(FD_VERIFY); /* verify write protection */
+ if (UDRS->maxblock){
+ /* mark it changed */
+ USETF(FD_DISK_CHANGED);
+
+ /* invalidate its geometry */
+ if (UDRS->keep_data >= 0) {
+ if ((UDP->flags & FTD_MSG) &&
+ current_type[drive] != NULL)
+ DPRINT("Disk type is undefined after "
+ "disk change\n");
+ current_type[drive] = NULL;
+ floppy_sizes[TOMINOR(current_drive)] = MAX_DISK_SIZE;
+ }
+ }
+ /*USETF(FD_DISK_NEWCHANGE);*/
+ return 1;
+ } else {
+ UDRS->last_checked=jiffies;
+ UCLEARF(FD_DISK_NEWCHANGE);
+ }
+ return 0;
+}
+
+static inline int is_selected(int dor, int unit)
+{
+ return ((dor & (0x10 << unit)) && (dor &3) == unit);
+}
+
+static int set_dor(int fdc, char mask, char data)
+{
+ register unsigned char drive, unit, newdor,olddor;
+
+ if (FDCS->address == -1)
+ return -1;
+
+ olddor = FDCS->dor;
+ newdor = (olddor & mask) | data;
+ if (newdor != olddor){
+ unit = olddor & 0x3;
+ if (is_selected(olddor, unit) && !is_selected(newdor,unit)){
+ drive = REVDRIVE(fdc,unit);
+#ifdef DCL_DEBUG
+ if (UDP->flags & FD_DEBUG){
+ DPRINT("calling disk change from set_dor\n");
+ }
+#endif
+ disk_change(drive);
+ }
+ FDCS->dor = newdor;
+ fd_outb(newdor, FD_DOR);
+
+ unit = newdor & 0x3;
+ if (!is_selected(olddor, unit) && is_selected(newdor,unit)){
+ drive = REVDRIVE(fdc,unit);
+ UDRS->select_date = jiffies;
+ }
+ }
+ if (newdor & 0xf0)
+ floppy_grab_irq_and_dma();
+ if (olddor & 0xf0)
+ floppy_release_irq_and_dma();
+ return olddor;
+}
+
+static void twaddle(void)
+{
+ if (DP->select_delay)
+ return;
+ fd_outb(FDCS->dor & ~(0x10<<UNIT(current_drive)),FD_DOR);
+ fd_outb(FDCS->dor, FD_DOR);
+ DRS->select_date = jiffies;
+}
+
+/* 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 input/dma. */
+static void set_fdc(int drive)
+{
+ if (drive >= 0 && drive < N_DRIVE){
+ fdc = FDC(drive);
+ current_drive = drive;
+ }
+ if (fdc != 1 && fdc != 0) {
+ printk("bad fdc value\n");
+ return;
+ }
+ set_dor(fdc,~0,8);
+ set_dor(1-fdc, ~8, 0);
+ if (FDCS->rawcmd == 2)
+ reset_fdc_info(1);
+ if (fd_inb(FD_STATUS) != STATUS_READY)
+ FDCS->reset = 1;
+}
+
+/* locks the driver */
+static int lock_fdc(int drive, int interruptible)
+{
+ if (!usage_count){
+ printk("trying to lock fdc while usage count=0\n");
+ return -1;
+ }
+ floppy_grab_irq_and_dma();
+ cli();
+ while (fdc_busy && NO_SIGNAL)
+ interruptible_sleep_on(&fdc_wait);
+ if (fdc_busy){
+ sti();
+ return -EINTR;
+ }
+ fdc_busy = 1;
+ sti();
+ command_status = FD_COMMAND_NONE;
+ reschedule_timeout(drive, "lock fdc", 0);
+ set_fdc(drive);
+ return 0;
+}
+
+#define LOCK_FDC(drive,interruptible) \
+if (lock_fdc(drive,interruptible)) return -EINTR;
+
+
+/* unlocks the driver */
+static inline void unlock_fdc(void)
+{
+ raw_cmd = 0;
+ if (!fdc_busy)
+ DPRINT("FDC access conflict!\n");
+
+ if (DEVICE_INTR)
+ DPRINT1("device interrupt still active at FDC release: %p!\n",
+ DEVICE_INTR);
+ command_status = FD_COMMAND_NONE;
+ del_timer(&fd_timeout);
+ cont = NULL;
+ fdc_busy = 0;
+ floppy_release_irq_and_dma();
+ wake_up(&fdc_wait);
+}
+
+/* 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[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 },
+ { 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 }
+};
+
+/* schedules motor off */
+static void floppy_off(unsigned int drive)
+{
+ unsigned long volatile delta;
+ register int fdc=FDC(drive);
+
+ if (!(FDCS->dor & (0x10 << UNIT(drive))))
+ return;
+
+ del_timer(motor_off_timer+drive);
+
+ /* make spindle stop in a position which minimizes spinup time
+ * next time */
+ if (UDP->rps){
+ delta = jiffies - UDRS->first_read_date + HZ -
+ UDP->spindown_offset;
+ delta = ((delta * UDP->rps) % HZ) / UDP->rps;
+ motor_off_timer[drive].expires = jiffies + UDP->spindown - delta;
+ }
+ add_timer(motor_off_timer+drive);
+}
+
+/*
+ * cycle through all N_DRIVE floppy drives, for disk change testing.
+ * stopping at current drive. This is done before any long operation, to
+ * be sure to have up to date disk change information.
+ */
+static void scandrives(void)
+{
+ int i, drive, saved_drive;
+
+ if (DP->select_delay)
+ return;
+
+ saved_drive = current_drive;
+ for (i=0; i < N_DRIVE; i++){
+ drive = (saved_drive + i + 1) % N_DRIVE;
+ if (UDRS->fd_ref == 0 || UDP->select_delay != 0)
+ continue; /* skip closed drives */
+ set_fdc(drive);
+ 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);
+ }
+ set_fdc(saved_drive);
+}
+
+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)
+{
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("calling disk change from watchdog\n");
+ }
+#endif
+
+ if (disk_change(current_drive)){
+ DPRINT("disk removed during i/o\n");
+ floppy_shutdown();
+ } else {
+ del_timer(&fd_timer);
+ fd_timer.function = (timeout_fn) fd_watchdog;
+ fd_timer.expires = jiffies + HZ / 10;
+ add_timer(&fd_timer);
+ }
+}
+
+static void main_command_interrupt(void)
+{
+ del_timer(&fd_timer);
+ cont->interrupt();
+}
+
+/* waits for a delay (spinup or select) to pass */
+static int wait_for_completion(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 (jiffies < delay){
+ del_timer(&fd_timer);
+ fd_timer.function = function;
+ fd_timer.expires = delay;
+ add_timer(&fd_timer);
+ return 1;
+ }
+ return 0;
+}
+
+static int hlt_disabled=0;
+static void floppy_disable_hlt(void)
+{
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ if (!hlt_disabled){
+ hlt_disabled=1;
+#ifdef HAVE_DISABLE_HLT
+ disable_hlt();
+#endif
+ }
+ restore_flags(flags);
+}
+
+static void floppy_enable_hlt(void)
+{
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ if (hlt_disabled){
+ hlt_disabled=0;
+#ifdef HAVE_DISABLE_HLT
+ enable_hlt();
+#endif
+ }
+ restore_flags(flags);
+}
+
+
+static void setup_DMA(void)
+{
+#ifdef FLOPPY_SANITY_CHECK
+ if (raw_cmd->length == 0){
+ int i;
+
+ printk("zero dma transfer size:");
+ for (i=0; i < raw_cmd->cmd_count; i++)
+ printk("%x,", raw_cmd->cmd[i]);
+ printk("\n");
+ cont->done(0);
+ FDCS->reset = 1;
+ return;
+ }
+ if ((long) raw_cmd->kernel_data % 512){
+ printk("non aligned address: %p\n", raw_cmd->kernel_data);
+ cont->done(0);
+ FDCS->reset=1;
+ return;
+ }
+ if (CROSS_64KB(raw_cmd->kernel_data, raw_cmd->length)) {
+ printk("DMA crossing 64-K boundary %p-%p\n",
+ raw_cmd->kernel_data,
+ raw_cmd->kernel_data + raw_cmd->length);
+ cont->done(0);
+ FDCS->reset=1;
+ return;
+ }
+#endif
+ cli();
+ fd_disable_dma();
+ fd_clear_dma_ff();
+ fd_set_dma_mode((raw_cmd->flags & FD_RAW_READ)?
+ DMA_MODE_READ : DMA_MODE_WRITE);
+ fd_set_dma_addr(virt_to_bus(raw_cmd->kernel_data));
+ fd_set_dma_count(raw_cmd->length);
+ fd_enable_dma();
+ sti();
+ floppy_disable_hlt();
+}
+
+/* sends a command byte to the fdc */
+static int output_byte(char byte)
+{
+ int counter;
+ unsigned char status = 0;
+ unsigned char rstatus;
+
+ if (FDCS->reset)
+ return -1;
+ for (counter = 0; counter < 10000 && !FDCS->reset; counter++) {
+ rstatus = fd_inb(FD_STATUS);
+ status = rstatus &(STATUS_READY|STATUS_DIR|STATUS_DMA);
+ if (!(status & STATUS_READY))
+ continue;
+ if (status == STATUS_READY){
+ fd_outb(byte,FD_DATA);
+
+#ifdef FLOPPY_SANITY_CHECK
+ output_log[output_log_pos].data = byte;
+ output_log[output_log_pos].status = rstatus;
+ output_log[output_log_pos].jiffies = jiffies;
+ output_log_pos = (output_log_pos + 1) % OLOGSIZE;
+#endif
+ return 0;
+ } else
+ break;
+ }
+ FDCS->reset = 1;
+ if (!initialising)
+ DPRINT2("Unable to send byte %x to FDC. Status=%x\n",
+ byte, status);
+ return -1;
+}
+#define LAST_OUT(x) if (output_byte(x)){ reset_fdc();return;}
+
+/* gets the response from the fdc */
+static int result(void)
+{
+ int i = 0, counter, status = 0;
+
+ if (FDCS->reset)
+ return -1;
+ for (counter = 0; counter < 10000 && !FDCS->reset; counter++) {
+ status = fd_inb(FD_STATUS)&
+ (STATUS_DIR|STATUS_READY|STATUS_BUSY|STATUS_DMA);
+ if (!(status & STATUS_READY))
+ continue;
+ if (status == STATUS_READY){
+#ifdef FLOPPY_SANITY_CHECK
+ resultjiffies = jiffies;
+ resultsize = i;
+#endif
+ return i;
+ }
+ if (status & STATUS_DMA)
+ break;
+ if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) {
+ if (i >= MAX_REPLIES) {
+ DPRINT("floppy_stat reply overrun\n");
+ break;
+ }
+ reply_buffer[i++] = fd_inb(FD_DATA);
+ }
+ }
+ FDCS->reset = 1;
+ if (!initialising)
+ DPRINT3("Getstatus times out (%x) on fdc %d [%d]\n",
+ status, fdc, i);
+ return -1;
+}
+
+/* Set perpendicular mode as required, based on data rate, if supported.
+ * 82077 Now tested. 1Mbps data rate only possible with 82077-1.
+ */
+static inline void perpendicular_mode(void)
+{
+ unsigned char perp_mode;
+
+ if (raw_cmd->rate & 0x40){
+ switch(raw_cmd->rate & 3){
+ case 0:
+ perp_mode=2;
+ break;
+ case 3:
+ perp_mode=3;
+ break;
+ default:
+ DPRINT("Invalid data rate for perpendicular mode!\n");
+ cont->done(0);
+ 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(perp_mode);
+ FDCS->perp_mode = perp_mode;
+ } else if (perp_mode) {
+ DPRINT("perpendicular mode not supported by this FDC.\n");
+ }
+} /* perpendicular_mode */
+
+#define NOMINAL_DTR 500
+
+/* 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 fdc_specify(void)
+{
+ 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);
+ output_byte(0);
+ output_byte(0x2A); /* FIFO on, polling off, 10 byte threshold */
+ output_byte(0); /* precompensation from track 0 upwards */
+ if (FDCS->reset){
+ FDCS->has_fifo=0;
+ return;
+ }
+ FDCS->need_configure = 0;
+ /*DPRINT("FIFO enabled\n");*/
+ }
+
+ 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;
+ SUPBOUND(srt, 0xf);
+ INFBOUND(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;
+ spec2 = (hlt << 1);
+
+ /* 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(FDCS->spec1 = spec1);
+ output_byte(FDCS->spec2 = spec2);
+ }
+} /* fdc_specify */
+
+/* Set the FDC's data transfer rate on behalf of the specified drive.
+ * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue
+ * of the specify command (i.e. using the fdc_specify function).
+ */
+static int fdc_dtr(void)
+{
+ /* If data rate not already set to desired value, set it. */
+ if ((raw_cmd->rate & 3) == FDCS->dtr)
+ return 0;
+
+ /* Set dtr */
+ fd_outb(raw_cmd->rate & 3, FD_DCR);
+
+ /* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB)
+ * need a stabilization period of several milliseconds to be
+ * enforced after data rate changes before R/W operations.
+ * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies)
+ */
+ FDCS->dtr = raw_cmd->rate & 3;
+ return(wait_for_completion(jiffies+2*HZ/100,
+ (timeout_fn) floppy_ready));
+} /* fdc_dtr */
+
+static void tell_sector(void)
+{
+ printk(": track %d, head %d, sector %d, size %d",
+ R_TRACK, R_HEAD, R_SECTOR, R_SIZECODE);
+} /* tell_sector */
+
+
+/*
+ * 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 int interpret_errors(void)
+{
+ char bad;
+
+ if (inr!=7) {
+ DPRINT("-- FDC reply error");
+ FDCS->reset = 1;
+ return 1;
+ }
+
+ /* check IC to find cause of interrupt */
+ switch (ST0 & ST0_INTR) {
+ case 0x40: /* error occurred during command execution */
+ bad = 1;
+ if (ST1 & ST1_WP) {
+ DPRINT("Drive is write protected\n");
+ CLEARF(FD_DISK_WRITABLE);
+ cont->done(0);
+ bad = 2;
+ } else if (ST1 & ST1_ND) {
+ SETF(FD_NEED_TWADDLE);
+ } else if (ST1 & ST1_OR) {
+ if (DP->flags & FTD_MSG)
+ DPRINT("Over/Underrun - retrying\n");
+ bad = 0;
+ }else if (*errors >= DP->max_errors.reporting){
+ DPRINT("");
+ if (ST0 & ST0_ECE) {
+ printk("Recalibrate failed!");
+ } else if (ST2 & ST2_CRC) {
+ printk("data CRC error");
+ tell_sector();
+ } else if (ST1 & ST1_CRC) {
+ printk("CRC error");
+ tell_sector();
+ } else if ((ST1 & (ST1_MAM|ST1_ND)) || (ST2 & ST2_MAM)) {
+ if (!probing) {
+ printk("sector not found");
+ tell_sector();
+ } else
+ printk("probe failed...");
+ } else if (ST2 & ST2_WC) { /* seek error */
+ printk("wrong cylinder");
+ } else if (ST2 & ST2_BC) { /* cylinder marked as bad */
+ printk("bad cylinder");
+ } else {
+ printk("unknown error. ST[0..2] are: 0x%x 0x%x 0x%x", ST0, ST1, ST2);
+ tell_sector();
+ }
+ printk("\n");
+
+ }
+ if (ST2 & ST2_WC || ST2 & ST2_BC)
+ /* wrong cylinder => recal */
+ DRS->track = NEED_2_RECAL;
+ return bad;
+ case 0x80: /* invalid command given */
+ DPRINT("Invalid FDC command given!\n");
+ cont->done(0);
+ return 2;
+ case 0xc0:
+ DPRINT("Abnormal termination caused by polling\n");
+ cont->error();
+ return 2;
+ default: /* (0) Normal command termination */
+ return 0;
+ }
+}
+
+/*
+ * This routine is called when everything should be correctly set up
+ * for the transfer (ie floppy motor is on, the correct floppy is
+ * selected, and the head is sitting on the right track).
+ */
+static void setup_rw_floppy(void)
+{
+ 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) && !(flags & FD_RAW_NO_MOTOR)){
+ 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_start;
+ } else
+ function = (timeout_fn) setup_rw_floppy;
+
+ /* wait until the floppy is spinning fast enough */
+ if (wait_for_completion(ready_date,function))
+ return;
+ }
+ dflags = DRS->flags;
+
+ if ((flags & FD_RAW_READ) || (flags & FD_RAW_WRITE))
+ 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();
+}
+
+static int blind_seek;
+
+/*
+ * This is the routine called after every seek (or recalibrate) interrupt
+ * from the floppy controller.
+ */
+static void seek_interrupt(void)
+{
+#ifdef DEBUGT
+ debugt("seek interrupt:");
+#endif
+ if (inr != 2 || (ST0 & 0xF8) != 0x20) {
+ DPRINT("seek failed\n");
+ DRS->track = NEED_2_RECAL;
+ cont->error();
+ cont->redo();
+ return;
+ }
+ if (DRS->track >= 0 && DRS->track != ST1 && !blind_seek){
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("clearing NEWCHANGE flag because of effective seek\n");
+ DPRINT1("jiffies=%ld\n", jiffies);
+ }
+#endif
+ CLEARF(FD_DISK_NEWCHANGE); /* effective seek */
+ DRS->select_date = jiffies;
+ }
+ DRS->track = ST1;
+ floppy_ready();
+}
+
+static void check_wp(void)
+{
+ if (TESTF(FD_VERIFY)) {
+ /* check write protection */
+ output_byte(FD_GETSTATUS);
+ output_byte(UNIT(current_drive));
+ if (result() != 1){
+ FDCS->reset = 1;
+ return;
+ }
+ CLEARF(FD_VERIFY);
+ CLEARF(FD_NEED_TWADDLE);
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("checking whether disk is write protected\n");
+ DPRINT1("wp=%x\n",ST3 & 0x40);
+ }
+#endif
+ if (!(ST3 & 0x40))
+ SETF(FD_DISK_WRITABLE);
+ else
+ CLEARF(FD_DISK_WRITABLE);
+ }
+}
+
+static void seek_floppy(void)
+{
+ int track;
+
+ blind_seek=0;
+
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("calling disk change from seek\n");
+ }
+#endif
+
+ if (!TESTF(FD_DISK_NEWCHANGE) &&
+ disk_change(current_drive) &&
+ (raw_cmd->flags & FD_RAW_NEED_DISK)){
+ /* the media changed flag should be cleared after the seek.
+ * If it isn't, this means that there is really no disk in
+ * the drive.
+ */
+ SETF(FD_DISK_CHANGED);
+ cont->done(0);
+ cont->redo();
+ return;
+ }
+ if (DRS->track <= NEED_1_RECAL){
+ recalibrate_floppy();
+ return;
+ } else if (TESTF(FD_DISK_NEWCHANGE) &&
+ (raw_cmd->flags & FD_RAW_NEED_DISK) &&
+ (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 {
+ if (DP->flags & FD_SILENT_DCL_CLEAR){
+ set_dor(fdc, ~(0x10 << UNIT(current_drive)), 0);
+ blind_seek = 1;
+ raw_cmd->flags |= FD_RAW_NEED_SEEK;
+ }
+ track = 1;
+ }
+ } else {
+ check_wp();
+ if (raw_cmd->track != DRS->track &&
+ (raw_cmd->flags & FD_RAW_NEED_SEEK))
+ track = raw_cmd->track;
+ else {
+ setup_rw_floppy();
+ return;
+ }
+ }
+
+ SET_INTR(seek_interrupt);
+ output_byte(FD_SEEK);
+ output_byte(UNIT(current_drive));
+ LAST_OUT(track);
+#ifdef DEBUGT
+ debugt("seek command:");
+#endif
+}
+
+static void recal_interrupt(void)
+{
+#ifdef DEBUGT
+ debugt("recal interrupt:");
+#endif
+ if (inr !=2)
+ FDCS->reset = 1;
+ else if (ST0 & ST0_ECE) {
+ switch(DRS->track){
+ case NEED_1_RECAL:
+#ifdef DEBUGT
+ debugt("recal interrupt need 1 recal:");
+#endif
+ /* after a second recalibrate, we still haven't
+ * reached track 0. Probably no drive. Raise an
+ * error, as failing immediately might upset
+ * computers possessed by the Devil :-) */
+ cont->error();
+ cont->redo();
+ return;
+ case NEED_2_RECAL:
+#ifdef DEBUGT
+ debugt("recal interrupt need 2 recal:");
+#endif
+ /* 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 */
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("clearing NEWCHANGE flag because of second recalibrate\n");
+ }
+#endif
+
+ CLEARF(FD_DISK_NEWCHANGE);
+ DRS->select_date = jiffies;
+ /* fall through */
+ default:
+#ifdef DEBUGT
+ debugt("recal interrupt default:");
+#endif
+ /* 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;
+ floppy_ready();
+}
+
+/*
+ * Unexpected interrupt - Print as much debugging info as we can...
+ * All bets are off...
+ */
+static void unexpected_floppy_interrupt(void)
+{
+ int i;
+ if (initialising)
+ return;
+ if (print_unex){
+ DPRINT("unexpected interrupt\n");
+ if (inr >= 0)
+ for (i=0; i<inr; i++)
+ printk("%d %x\n", i, reply_buffer[i]);
+ }
+ while(1){
+ output_byte(FD_SENSEI);
+ inr=result();
+ if (inr != 2)
+ break;
+ if (print_unex){
+ printk("sensei\n");
+ for (i=0; i<inr; i++)
+ printk("%d %x\n", i, reply_buffer[i]);
+ }
+ }
+ FDCS->reset = 1;
+}
+
+static struct tq_struct floppy_tq =
+{ 0, 0, (void *) (void *) unexpected_floppy_interrupt, 0 };
+
+/* interrupt handler */
+static void floppy_interrupt(int irq, struct pt_regs * regs)
+{
+ void (*handler)(void) = DEVICE_INTR;
+
+ lasthandler = handler;
+ interruptjiffies = jiffies;
+
+ floppy_enable_hlt();
+ CLEAR_INTR;
+ if (fdc >= N_FDC || FDCS->address == -1){
+ /* we don't even know which FDC is the culprit */
+ printk("DOR0=%x\n", fdc_state[0].dor);
+ printk("floppy interrupt on bizarre fdc %d\n",fdc);
+ printk("handler=%p\n", handler);
+ is_alive("bizarre fdc");
+ return;
+ }
+ inr = result();
+ if (!handler){
+ unexpected_floppy_interrupt();
+ is_alive("unexpected");
+ return;
+ }
+ if (inr == 0){
+ do {
+ output_byte(FD_SENSEI);
+ inr = result();
+ } while ((ST0 & 0x83) != UNIT(current_drive) && inr == 2);
+ }
+ floppy_tq.routine = (void *)(void *) handler;
+ queue_task_irq(&floppy_tq, &tq_timer);
+ is_alive("normal interrupt end");
+}
+
+static void recalibrate_floppy(void)
+{
+#ifdef DEBUGT
+ debugt("recalibrate floppy:");
+#endif
+ SET_INTR(recal_interrupt);
+ output_byte(FD_RECALIBRATE);
+ LAST_OUT(UNIT(current_drive));
+}
+
+/*
+ * Must do 4 FD_SENSEIs after reset because of ``drive polling''.
+ */
+static void reset_interrupt(void)
+{
+#ifdef DEBUGT
+ debugt("reset interrupt:");
+#endif
+ /* fdc_specify(); reprogram fdc */
+ result(); /* get the status ready for set_fdc */
+ if (FDCS->reset) {
+ printk("reset set in interrupt, calling %p\n", cont->error);
+ cont->error(); /* a reset just after a reset. BAD! */
+ }
+ cont->redo();
+}
+
+/*
+ * reset is done by pulling bit 2 of DOR low for a while (old 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)
+ fd_outb(0x80 | (FDCS->dtr &3), FD_STATUS);
+ else {
+ fd_outb(FDCS->dor & ~0x04, FD_DOR);
+ udelay(FD_RESET_DELAY);
+ outb(FDCS->dor, FD_DOR);
+ }
+}
+
+static void empty(void)
+{
+}
+
+void show_floppy(void)
+{
+ int i;
+
+ printk("\n");
+ printk("floppy driver state\n");
+ printk("-------------------\n");
+ printk("now=%ld last interrupt=%d last called handler=%p\n",
+ jiffies, interruptjiffies, lasthandler);
+
+
+#ifdef FLOPPY_SANITY_CHECK
+ printk("timeout_message=%s\n", timeout_message);
+ printk("last output bytes:\n");
+ for (i=0; i < OLOGSIZE; i++)
+ printk("%2x %2x %ld\n",
+ output_log[(i+output_log_pos) % OLOGSIZE].data,
+ output_log[(i+output_log_pos) % OLOGSIZE].status,
+ output_log[(i+output_log_pos) % OLOGSIZE].jiffies);
+ printk("last result at %d\n", resultjiffies);
+ printk("last redo_fd_request at %d\n", lastredo);
+ for (i=0; i<resultsize; i++){
+ printk("%2x ", reply_buffer[i]);
+ }
+ printk("\n");
+#endif
+
+ printk("status=%x\n", fd_inb(FD_STATUS));
+ printk("fdc_busy=%d\n", fdc_busy);
+ if (DEVICE_INTR)
+ printk("DEVICE_INTR=%p\n", DEVICE_INTR);
+ if (floppy_tq.sync)
+ printk("floppy_tq.routine=%p\n", floppy_tq.routine);
+ if (fd_timer.prev)
+ printk("fd_timer.function=%p\n", fd_timer.function);
+ if (fd_timeout.prev){
+ printk("timer_table=%p\n",fd_timeout.function);
+ printk("expires=%ld\n",fd_timeout.expires-jiffies);
+ printk("now=%ld\n",jiffies);
+ }
+ printk("cont=%p\n", cont);
+ printk("CURRENT=%p\n", CURRENT);
+ printk("command_status=%d\n", command_status);
+ printk("\n");
+}
+
+static void floppy_shutdown(void)
+{
+ if (!initialising)
+ show_floppy();
+ CLEAR_INTR;
+ floppy_tq.routine = (void *)(void *) empty;
+ del_timer(&fd_timer);
+ sti();
+
+ floppy_enable_hlt();
+ fd_disable_dma();
+ /* avoid dma going to a random drive after shutdown */
+
+ if (!initialising)
+ DPRINT("floppy timeout\n");
+ FDCS->reset = 1;
+ if (cont){
+ cont->done(0);
+ cont->redo(); /* this will recall reset when needed */
+ } else {
+ printk("no cont in shutdown!\n");
+ process_fd_request();
+ }
+ is_alive("floppy shutdown");
+}
+/*typedef void (*timeout_fn)(unsigned long);*/
+
+/* start motor, check media-changed condition and write protection */
+static int start_motor(void (*function)(void) )
+{
+ int mask, data;
+
+ mask = 0xfc;
+ data = UNIT(current_drive);
+ if (!(raw_cmd->flags & FD_RAW_NO_MOTOR)){
+ 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;
+ data |= (0x10 << UNIT(current_drive));
+ }
+ } else
+ if (FDCS->dor & (0x10 << UNIT(current_drive)))
+ mask &= ~(0x10 << UNIT(current_drive));
+
+ /* starts motor and selects floppy */
+ del_timer(motor_off_timer + current_drive);
+ set_dor(fdc, mask, data);
+
+ /* wait_for_completion also schedules reset if needed. */
+ return(wait_for_completion(DRS->select_date+DP->select_delay,
+ (timeout_fn) function));
+}
+
+static void floppy_ready(void)
+{
+ CHECK_RESET;
+ if (start_motor(floppy_ready)) return;
+ if (fdc_dtr()) return;
+
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("calling disk change from floppy_ready\n");
+ }
+#endif
+
+ if (!(raw_cmd->flags & FD_RAW_NO_MOTOR) &&
+ disk_change(current_drive) &&
+ !DP->select_delay)
+ twaddle(); /* this clears the dcl on certain drive/controller
+ * combinations */
+
+ if (raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)){
+ perpendicular_mode();
+ fdc_specify(); /* must be done here because of hut, hlt ... */
+ seek_floppy();
+ } else
+ setup_rw_floppy();
+}
+
+static void floppy_start(void)
+{
+ reschedule_timeout(CURRENTD, "floppy start", 0);
+
+ scandrives();
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("setting NEWCHANGE in floppy_start\n");
+ }
+#endif
+ SETF(FD_DISK_NEWCHANGE);
+ floppy_ready();
+}
+
+/*
+ * ========================================================================
+ * here ends the bottom half. Exported routines are:
+ * floppy_start, 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 do_wakeup(void)
+{
+ reschedule_timeout(MAXTIMEOUT, "do wakeup", 0);
+ cont = 0;
+ command_status += 2;
+ wake_up(&command_done);
+}
+
+static struct cont_t wakeup_cont={
+ empty,
+ do_wakeup,
+ empty,
+ (done_f)empty
+};
+
+static int wait_til_done(void (*handler)(void), int interruptible)
+{
+ int ret;
+
+ floppy_tq.routine = (void *)(void *) handler;
+ queue_task(&floppy_tq, &tq_timer);
+
+ cli();
+ while(command_status < 2 && NO_SIGNAL){
+ is_alive("wait_til_done");
+ if (interruptible)
+ interruptible_sleep_on(&command_done);
+ else
+ sleep_on(&command_done);
+ }
+ if (command_status < 2){
+ floppy_shutdown();
+ sti();
+ process_fd_request();
+ return -EINTR;
+ }
+ sti();
+
+ 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 generic_done(int result)
+{
+ command_status = result;
+ cont = &wakeup_cont;
+}
+
+static void generic_success(void)
+{
+ cont->done(1);
+}
+
+static void generic_failure(void)
+{
+ cont->done(0);
+}
+
+static void success_and_wakeup(void)
+{
+ generic_success();
+ cont->redo();
+}
+
+
+/*
+ * formatting and rw support.
+ * ==========================
+ */
+
+static int next_valid_format(void)
+{
+ int probed_format;
+
+ probed_format = DRS->probed_format;
+ while(1){
+ if (probed_format >= 8 ||
+ !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 bad_flp_intr(void)
+{
+ if (probing){
+ DRS->probed_format++;
+ if (!next_valid_format())
+ return;
+ }
+ (*errors)++;
+ INFBOUND(DRWE->badness, *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(kdev_t 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();
+}
+
+#define CODE2SIZE (ssize = ((1 << SIZECODE) + 3) >> 2)
+#define FM_MODE(x,y) ((y) & ~(((x)->rate & 0x80) >>1))
+#define CT(x) ((x) | 0x40)
+static void setup_format_params(int track)
+{
+ struct fparm {
+ unsigned char track,head,sect,size;
+ } *here = (struct fparm *)floppy_track_buffer;
+ int il,n;
+ int count,head_shift,track_shift;
+
+ raw_cmd = &default_raw_cmd;
+ raw_cmd->track = track;
+
+ 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 & 0x43;
+ raw_cmd->cmd_count = NR_F;
+ COMMAND = FM_MODE(floppy,FD_FORMAT);
+ DR_SELECT = UNIT(current_drive) + PH_HEAD(floppy,format_req.head);
+ F_SIZECODE = FD_SIZECODE(floppy);
+ F_SECT_PER_TRACK = floppy->sect << 2 >> F_SIZECODE;
+ F_GAP = floppy->fmt_gap;
+ F_FILL = FD_FILL_BYTE;
+
+ raw_cmd->kernel_data = 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 + 3;
+
+ /* 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 && F_SIZECODE == 2)
+ 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;
+ }
+ }
+ }
+}
+
+static void redo_format(void)
+{
+ buffer_track = -1;
+ setup_format_params(format_req.track << STRETCH(floppy));
+ floppy_start();
+#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(kdev_t device, struct format_descr *tmp_format_req)
+{
+ int ret;
+ int drive=DRIVE(device);
+
+ LOCK_FDC(drive,1);
+ set_floppy(device);
+ if (!floppy ||
+ floppy->track > DP->tracks ||
+ tmp_format_req->track >= floppy->track ||
+ tmp_format_req->head >= floppy->head ||
+ (floppy->sect << 2) % (1 << FD_SIZECODE(floppy)) ||
+ !floppy->fmt_gap) {
+ process_fd_request();
+ return -EINVAL;
+ }
+ format_req = *tmp_format_req;
+ format_errors = 0;
+ cont = &format_cont;
+ errors = &format_errors;
+ IWAIT(redo_format);
+ process_fd_request();
+ return ret;
+}
+
+/*
+ * 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;
+ reschedule_timeout(MAXTIMEOUT, "request done %d", uptodate);
+
+ if (!CURRENT){
+ DPRINT("request list destroyed in floppy request done\n");
+ return;
+ }
+ if (uptodate){
+ /* maintain values for invalidation on geometry
+ * change */
+ block = current_count_sectors + CURRENT->sector;
+ INFBOUND(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)
+ DPRINT("request list destroyed in floppy request done\n");
+
+ } else {
+ if (CURRENT->cmd == WRITE) {
+ /* record write error information */
+ DRWE->write_errors++;
+ if (DRWE->write_errors == 1) {
+ DRWE->first_error_sector = CURRENT->sector;
+ DRWE->first_error_generation = DRS->generation;
+ }
+ DRWE->last_error_sector = CURRENT->sector;
+ DRWE->last_error_generation = DRS->generation;
+ }
+ end_request(0);
+ }
+}
+
+/* Interrupt handler evaluating the result of the r/w operation */
+static void rw_interrupt(void)
+{
+ int nr_sectors, ssize;
+
+ if (!DRS->first_read_date)
+ DRS->first_read_date = jiffies;
+
+ nr_sectors = 0;
+ CODE2SIZE;
+ nr_sectors = ((R_TRACK-TRACK)*floppy->head+R_HEAD-HEAD) *
+ floppy->sect + ((R_SECTOR-SECTOR) << SIZECODE >> 2) -
+ (sector_t % floppy->sect) % ssize;
+
+#ifdef FLOPPY_SANITY_CHECK
+ if (nr_sectors > current_count_sectors + ssize -
+ (current_count_sectors + sector_t) % ssize +
+ sector_t % ssize){
+ DPRINT2("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
+ INFBOUND(nr_sectors,0);
+ SUPBOUND(current_count_sectors, nr_sectors);
+
+ switch (interpret_errors()){
+ case 2:
+ cont->redo();
+ return;
+ case 1:
+ if (!current_count_sectors){
+ cont->error();
+ cont->redo();
+ return;
+ }
+ break;
+ case 0:
+ if (!current_count_sectors){
+ cont->redo();
+ return;
+ }
+ current_type[current_drive] = floppy;
+ floppy_sizes[TOMINOR(current_drive) ]= floppy->size>>1;
+ break;
+ }
+
+ if (probing) {
+ if (DP->flags & FTD_MSG)
+ DPRINT2("Auto-detected floppy type %s in fd%d\n",
+ floppy->name,current_drive);
+ current_type[current_drive] = floppy;
+ floppy_sizes[TOMINOR(current_drive)] = floppy->size >> 1;
+ probing = 0;
+ }
+
+ if (CT(COMMAND) != FD_READ ||
+ raw_cmd->kernel_data == CURRENT->buffer){
+ /* transfer directly from buffer */
+ cont->done(1);
+ } else if (CT(COMMAND) == FD_READ){
+ buffer_track = raw_cmd->track;
+ buffer_drive = current_drive;
+ INFBOUND(buffer_max, nr_sectors + sector_t);
+ }
+ cont->redo();
+}
+
+/* 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;
+
+ if (bh){
+ 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)
+{
+ SUPBOUND(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)
+{
+ int remaining; /* number of transferred 512-byte sectors */
+ struct buffer_head *bh;
+ char *buffer, *dma_buffer;
+ int size;
+
+ max_sector = transfer_size(ssize,
+ minimum(max_sector, max_sector_2),
+ CURRENT->nr_sectors);
+
+ if (current_count_sectors <= 0 && CT(COMMAND) == FD_WRITE &&
+ buffer_max > sector_t + CURRENT->nr_sectors)
+ current_count_sectors = minimum(buffer_max - sector_t,
+ CURRENT->nr_sectors);
+
+ remaining = current_count_sectors << 9;
+#ifdef FLOPPY_SANITY_CHECK
+ if ((remaining >> 9) > CURRENT->nr_sectors &&
+ CT(COMMAND) == FD_WRITE){
+ DPRINT("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
+
+ buffer_max = maximum(max_sector, buffer_max);
+
+ 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){
+ SUPBOUND(size, remaining);
+#ifdef FLOPPY_SANITY_CHECK
+ if (dma_buffer + size >
+ floppy_track_buffer + (max_buffer_sectors << 10) ||
+ dma_buffer < floppy_track_buffer){
+ DPRINT1("buffer overrun in copy buffer %d\n",
+ (int) ((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 (CT(COMMAND) == FD_READ)
+ printk("read\n");
+ if (CT(COMMAND) == FD_READ)
+ printk("write\n");
+ break;
+ }
+ if (((unsigned long)buffer) % 512)
+ DPRINT1("%p buffer not aligned\n", buffer);
+#endif
+ if (CT(COMMAND) == FD_READ) {
+ fd_cacheflush(dma_buffer, size);
+ memcpy(buffer, dma_buffer, size);
+ } else {
+ memcpy(dma_buffer, buffer, size);
+ fd_cacheflush(dma_buffer, size);
+ }
+ remaining -= size;
+ if (!remaining)
+ break;
+
+ dma_buffer += size;
+ bh = bh->b_reqnext;
+#ifdef FLOPPY_SANITY_CHECK
+ if (!bh){
+ DPRINT("bh=null in copy buffer after copy\n");
+ break;
+ }
+#endif
+ size = bh->b_size;
+ buffer = bh->b_data;
+ }
+#ifdef FLOPPY_SANITY_CHECK
+ if (remaining){
+ if (remaining > 0)
+ max_sector -= remaining >> 9;
+ DPRINT1("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;
+
+ set_fdc(DRIVE(CURRENT->rq_dev));
+
+ raw_cmd = &default_raw_cmd;
+ 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 = FM_MODE(floppy,FD_READ);
+ } else if (CURRENT->cmd == WRITE){
+ raw_cmd->flags |= FD_RAW_WRITE;
+ COMMAND = FM_MODE(floppy,FD_WRITE);
+ } else {
+ DPRINT("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 (((floppy->stretch & FD_SWAPSIDES) || TESTF(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 = minimum(floppy->sect - sector_t,
+ CURRENT->nr_sectors);
+ return 1;
+ }
+ SIZECODE = 2;
+ } else
+ SIZECODE = FD_SIZECODE(floppy);
+ raw_cmd->rate = floppy->rate & 0x43;
+ if ((floppy->rate & FD_2M) &&
+ (TRACK || HEAD) &&
+ raw_cmd->rate == 2)
+ raw_cmd->rate = 1;
+
+ if (SIZECODE)
+ SIZECODE2 = 0xff;
+ else
+ SIZECODE2 = 0x80;
+ raw_cmd->track = TRACK << STRETCH(floppy);
+ DR_SELECT = UNIT(current_drive) + PH_HEAD(floppy,HEAD);
+ GAP = floppy->gap;
+ CODE2SIZE;
+ SECT_PER_TRACK = floppy->sect << 2 >> SIZECODE;
+ SECTOR = ((sector_t % floppy->sect) << 2 >> SIZECODE) + 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 (CT(COMMAND) == FD_READ) {
+ copy_buffer(1, max_sector, buffer_max);
+ return 1;
+ }
+ } else if (aligned_sector_t != sector_t || CURRENT->nr_sectors < ssize){
+ if (CT(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 = FM_MODE(floppy,FD_READ);
+ } else if ((unsigned long)CURRENT->buffer < MAX_DMA_ADDRESS) {
+ unsigned long dma_limit;
+ int direct, indirect;
+
+ indirect= transfer_size(ssize,max_sector,max_buffer_sectors*2) -
+ sector_t;
+
+ /*
+ * Do NOT use minimum() here---MAX_DMA_ADDRESS is 64 bits wide
+ * on a 64 bit machine!
+ */
+ max_size = buffer_chain_size();
+ dma_limit = (MAX_DMA_ADDRESS - ((unsigned long) CURRENT->buffer)) >> 9;
+ if ((unsigned long) max_size > dma_limit) {
+ max_size = dma_limit;
+ }
+ /* 64 kb boundaries */
+ if (CROSS_64KB(CURRENT->buffer, max_size << 9))
+ 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 (!direct ||
+ (indirect * 2 > direct * 3 &&
+ *errors < DP->max_errors.read_track &&
+ /*!TESTF(FD_NEED_TWADDLE) &&*/
+ ((!probing || (DP->read_track&(1<<DRS->probed_format)))))){
+ max_size = CURRENT->nr_sectors;
+ } else {
+ raw_cmd->kernel_data = CURRENT->buffer;
+ raw_cmd->length = current_count_sectors << 9;
+ if (raw_cmd->length == 0){
+ DPRINT("zero dma transfer attempted from make_raw_request\n");
+ DPRINT3("indirect=%d direct=%d sector_t=%d",
+ indirect, direct, sector_t);
+ return 0;
+ }
+ return 2;
+ }
+ }
+
+ if (CT(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_max ||
+ sector_t < buffer_min ||
+ ((CT(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;
+ }
+ raw_cmd->kernel_data = floppy_track_buffer +
+ ((aligned_sector_t-buffer_min)<<9);
+
+ if (CT(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 FLOPPY_SANITY_CHECK
+ if (sector_t != aligned_sector_t && buffer_track == -1)
+ DPRINT("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 FLOPPY_SANITY_CHECK
+ if ((raw_cmd->length < current_count_sectors << 9) ||
+ (raw_cmd->kernel_data != CURRENT->buffer &&
+ CT(COMMAND) == FD_WRITE &&
+ (aligned_sector_t + (raw_cmd->length >> 9) > buffer_max ||
+ aligned_sector_t < buffer_min)) ||
+ raw_cmd->length % (128 << SIZECODE) ||
+ raw_cmd->length <= 0 || current_count_sectors <= 0){
+ DPRINT2("fractionary current count b=%lx s=%lx\n",
+ raw_cmd->length, current_count_sectors);
+ if (raw_cmd->kernel_data != CURRENT->buffer)
+ printk("addr=%d, length=%ld\n",
+ (int) ((raw_cmd->kernel_data -
+ 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 (raw_cmd->kernel_data != CURRENT->buffer){
+ if (raw_cmd->kernel_data < floppy_track_buffer ||
+ current_count_sectors < 0 ||
+ raw_cmd->length < 0 ||
+ raw_cmd->kernel_data + raw_cmd->length >
+ floppy_track_buffer + (max_buffer_sectors << 10)){
+ DPRINT("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 (CT(COMMAND) == FD_READ)
+ printk("read\n");
+ if (CT(COMMAND) == FD_READ)
+ printk("write\n");
+ return 0;
+ }
+ } else if (raw_cmd->length > CURRENT->nr_sectors << 9 ||
+ current_count_sectors > CURRENT->nr_sectors){
+ DPRINT("buffer overrun in direct transfer\n");
+ return 0;
+ } else if (raw_cmd->length < current_count_sectors << 9){
+ DPRINT("more sectors than bytes\n");
+ printk("bytes=%ld\n", raw_cmd->length >> 9);
+ printk("sectors=%ld\n", current_count_sectors);
+ }
+ if (raw_cmd->length == 0){
+ DPRINT("zero dma transfer attempted from make_raw_request\n");
+ return 0;
+ }
+#endif
+ return 2;
+}
+
+static void redo_fd_request(void)
+{
+#define REPEAT {request_done(0); continue; }
+ kdev_t device;
+ int tmp;
+
+ lastredo = jiffies;
+ if (current_drive < N_DRIVE)
+ floppy_off(current_drive);
+
+ if (CURRENT && CURRENT->rq_status == RQ_INACTIVE){
+ DPRINT("current not active!\n");
+ return;
+ }
+
+ while(1){
+ if (!CURRENT) {
+ CLEAR_INTR;
+ unlock_fdc();
+ return;
+ }
+ if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
+ panic(DEVICE_NAME ": request list destroyed");
+ if (CURRENT->bh && !buffer_locked(CURRENT->bh))
+ panic(DEVICE_NAME ": block not locked");
+
+ device = CURRENT->rq_dev;
+ set_fdc(DRIVE(device));
+ reschedule_timeout(CURRENTD, "redo fd request", 0);
+
+ set_floppy(device);
+ raw_cmd = & default_raw_cmd;
+ raw_cmd->flags = 0;
+ if (start_motor(redo_fd_request)) return;
+ if (test_bit(current_drive, &fake_change) ||
+ TESTF(FD_DISK_CHANGED)){
+ DPRINT("disk absent or changed during operation\n");
+ REPEAT;
+ }
+ if (!floppy) { /* Autodetection */
+ if (!probing){
+ DRS->probed_format = 0;
+ if (next_valid_format()){
+ DPRINT("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 (TESTF(FD_NEED_TWADDLE))
+ twaddle();
+ floppy_tq.routine = (void *)(void *) floppy_start;
+ queue_task(&floppy_tq, &tq_timer);
+#ifdef DEBUGT
+ debugt("queue fd request");
+#endif
+ return;
+ }
+#undef REPEAT
+}
+
+static struct cont_t rw_cont={
+ rw_interrupt,
+ redo_fd_request,
+ bad_flp_intr,
+ request_done };
+
+static struct tq_struct request_tq =
+{ 0, 0, (void *) (void *) redo_fd_request, 0 };
+
+static void process_fd_request(void)
+{
+ cont = &rw_cont;
+ queue_task(&request_tq, &tq_timer);
+}
+
+static void do_fd_request(void)
+{
+ if (fdc_busy){
+ /* fdc busy, this new request will be treated when the
+ current one is done */
+ is_alive("do fd request, old request running");
+ return;
+ }
+ lock_fdc(MAXTIMEOUT,0);
+ process_fd_request();
+ is_alive("do fd request");
+}
+
+static struct cont_t poll_cont={
+ success_and_wakeup,
+ floppy_ready,
+ generic_failure,
+ generic_done };
+
+static int poll_drive(int interruptible, int flag)
+{
+ int ret;
+ /* no auto-sense, just clear dcl */
+ raw_cmd = &default_raw_cmd;
+ raw_cmd->flags= flag;
+ raw_cmd->track=0;
+ raw_cmd->cmd_count=0;
+ cont = &poll_cont;
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("setting NEWCHANGE in poll_drive\n");
+ }
+#endif
+ SETF(FD_DISK_NEWCHANGE);
+ WAIT(floppy_ready);
+ return ret;
+}
+
+/*
+ * 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 interruptible)
+{
+ int ret;
+
+ ret=0;
+ LOCK_FDC(drive,interruptible);
+ if (arg == FD_RESET_ALWAYS)
+ FDCS->reset=1;
+ if (FDCS->reset){
+ cont = &reset_cont;
+ WAIT(reset_fdc);
+ }
+ process_fd_request();
+ return ret;
+}
+
+/*
+ * Misc Ioctl's and support
+ * ========================
+ */
+static int fd_copyout(void *param, const void *address, int size)
+{
+ int ret;
+
+ ECALL(verify_area(VERIFY_WRITE,param,size));
+ fd_cacheflush(address, size); /* is this necessary ??? */
+ /* Ralf: Yes; only the l2 cache is completly chipset
+ controlled */
+ memcpy_tofs(param,(void *) address, size);
+ return 0;
+}
+
+static int fd_copyin(void *param, void *address, int size)
+{
+ int ret;
+
+ ECALL(verify_area(VERIFY_READ,param,size));
+ memcpy_fromfs((void *) address, param, size);
+ return 0;
+}
+
+#define COPYOUT(x) ECALL(fd_copyout((void *)param, &(x), sizeof(x)))
+#define COPYIN(x) ECALL(fd_copyin((void *)param, &(x), sizeof(x)))
+
+static inline const 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 void raw_cmd_done(int flag)
+{
+ int i;
+
+ if (!flag) {
+ raw_cmd->flags = FD_RAW_FAILURE;
+ raw_cmd->flags |= FD_RAW_HARDFAILURE;
+ } else {
+ 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);
+
+ if ((raw_cmd->flags & FD_RAW_SOFTFAILURE) &&
+ (!raw_cmd->reply_count || (raw_cmd->reply[0] & 0xc0)))
+ raw_cmd->flags |= FD_RAW_FAILURE;
+
+ if (disk_change(current_drive))
+ raw_cmd->flags |= FD_RAW_DISK_CHANGE;
+ else
+ raw_cmd->flags &= ~FD_RAW_DISK_CHANGE;
+ if (raw_cmd->flags & FD_RAW_NO_MOTOR_AFTER)
+ motor_off_callback(current_drive);
+
+ if (raw_cmd->next &&
+ (!(raw_cmd->flags & FD_RAW_FAILURE) ||
+ !(raw_cmd->flags & FD_RAW_STOP_IF_FAILURE)) &&
+ ((raw_cmd->flags & FD_RAW_FAILURE) ||
+ !(raw_cmd->flags &FD_RAW_STOP_IF_SUCCESS))) {
+ raw_cmd = raw_cmd->next;
+ return;
+ }
+ }
+ generic_done(flag);
+}
+
+
+static struct cont_t raw_cmd_cont={
+ success_and_wakeup,
+ floppy_start,
+ generic_failure,
+ raw_cmd_done
+};
+
+static inline int raw_cmd_copyout(int cmd, char *param,
+ struct floppy_raw_cmd *ptr)
+{
+ struct old_floppy_raw_cmd old_raw_cmd;
+ int ret;
+
+ while(ptr) {
+ if (cmd == OLDFDRAWCMD) {
+ old_raw_cmd.flags = ptr->flags;
+ old_raw_cmd.data = ptr->data;
+ old_raw_cmd.length = ptr->length;
+ old_raw_cmd.rate = ptr->rate;
+ old_raw_cmd.reply_count = ptr->reply_count;
+ memcpy(old_raw_cmd.reply, ptr->reply, 7);
+ COPYOUT(old_raw_cmd);
+ param += sizeof(old_raw_cmd);
+ } else {
+ COPYOUT(*ptr);
+ param += sizeof(struct floppy_raw_cmd);
+ }
+
+ if ((ptr->flags & FD_RAW_READ) && ptr->buffer_length){
+ if (ptr->length>=0 && ptr->length<=ptr->buffer_length)
+ ECALL(fd_copyout(ptr->data,
+ ptr->kernel_data,
+ ptr->buffer_length -
+ ptr->length));
+ }
+ ptr = ptr->next;
+ }
+ return 0;
+}
+
+
+static void raw_cmd_free(struct floppy_raw_cmd **ptr)
+{
+ struct floppy_raw_cmd *next,*this;
+
+ this = *ptr;
+ *ptr = 0;
+ while(this) {
+ if (this->buffer_length) {
+ free_pages((unsigned long)this->kernel_data,
+ __get_order(this->buffer_length));
+ this->buffer_length = 0;
+ }
+ next = this->next;
+ kfree(this);
+ this = next;
+ }
+}
+
+
+static inline int raw_cmd_copyin(int cmd, char *param,
+ struct floppy_raw_cmd **rcmd)
+{
+ struct floppy_raw_cmd *ptr;
+ struct old_floppy_raw_cmd old_raw_cmd;
+ int ret;
+ int i;
+
+ *rcmd = 0;
+ while(1) {
+ ptr = (struct floppy_raw_cmd *)
+ kmalloc(sizeof(struct floppy_raw_cmd), GFP_USER);
+ if (!ptr)
+ return -ENOMEM;
+ *rcmd = ptr;
+ if (cmd == OLDFDRAWCMD){
+ COPYIN(old_raw_cmd);
+ ptr->flags = old_raw_cmd.flags;
+ ptr->data = old_raw_cmd.data;
+ ptr->length = old_raw_cmd.length;
+ ptr->rate = old_raw_cmd.rate;
+ ptr->cmd_count = old_raw_cmd.cmd_count;
+ ptr->track = old_raw_cmd.track;
+ ptr->phys_length = 0;
+ ptr->next = 0;
+ ptr->buffer_length = 0;
+ memcpy(ptr->cmd, old_raw_cmd.cmd, 9);
+ param += sizeof(struct old_floppy_raw_cmd);
+ if (ptr->cmd_count > 9)
+ return -EINVAL;
+ } else {
+ COPYIN(*ptr);
+ ptr->next = 0;
+ ptr->buffer_length = 0;
+ param += sizeof(struct floppy_raw_cmd);
+ if (ptr->cmd_count > 16)
+ return -EINVAL;
+ }
+
+ for (i=0; i< 16; i++)
+ ptr->reply[i] = 0;
+ ptr->resultcode = 0;
+ ptr->kernel_data = 0;
+
+ if (ptr->flags & (FD_RAW_READ | FD_RAW_WRITE)) {
+ if (ptr->length <= 0)
+ return -EINVAL;
+ ptr->kernel_data =(char*)dma_mem_alloc(ptr->length);
+ if (!ptr->kernel_data)
+ return -ENOMEM;
+ ptr->buffer_length = ptr->length;
+ }
+ if ( ptr->flags & FD_RAW_READ )
+ ECALL( verify_area( VERIFY_WRITE, ptr->data,
+ ptr->length ));
+ if (ptr->flags & FD_RAW_WRITE)
+ ECALL(fd_copyin(ptr->data, ptr->kernel_data,
+ ptr->length));
+ rcmd = & (ptr->next);
+ if (!(ptr->flags & FD_RAW_MORE))
+ return 0;
+ ptr->rate &= 0x43;
+ }
+}
+
+
+static int raw_cmd_ioctl(int cmd, void *param)
+{
+ int drive, ret, ret2;
+ struct floppy_raw_cmd *my_raw_cmd;
+
+ if (FDCS->rawcmd <= 1)
+ FDCS->rawcmd = 1;
+ for (drive= 0; drive < N_DRIVE; drive++){
+ if (FDC(drive) != fdc)
+ continue;
+ if (drive == current_drive){
+ if (UDRS->fd_ref > 1){
+ FDCS->rawcmd = 2;
+ break;
+ }
+ } else if (UDRS->fd_ref){
+ FDCS->rawcmd = 2;
+ break;
+ }
+ }
+
+ if (FDCS->reset)
+ return -EIO;
+
+ ret = raw_cmd_copyin(cmd, param, &my_raw_cmd);
+ if (ret) {
+ raw_cmd_free(&my_raw_cmd);
+ return ret;
+ }
+
+ raw_cmd = my_raw_cmd;
+ cont = &raw_cmd_cont;
+ ret=wait_til_done(floppy_start,1);
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("calling disk change from raw_cmd ioctl\n");
+ }
+#endif
+
+ if (ret != -EINTR && FDCS->reset)
+ ret = -EIO;
+
+ DRS->track = NO_TRACK;
+
+ ret2 = raw_cmd_copyout(cmd, param, my_raw_cmd);
+ if (!ret)
+ ret = ret2;
+ raw_cmd_free(&my_raw_cmd);
+ return ret;
+}
+
+static int invalidate_drive(kdev_t rdev)
+{
+ /* invalidate the buffer track to force a reread */
+ set_bit(DRIVE(rdev), &fake_change);
+ process_fd_request();
+ check_disk_change(rdev);
+ return 0;
+}
+
+
+static inline void clear_write_error(int drive)
+{
+ CLEARSTRUCT(UDRWE);
+}
+
+static inline int set_geometry(unsigned int cmd, struct floppy_struct *g,
+ int drive, int type, kdev_t device)
+{
+ int cnt;
+
+ /* sanity checking for parameters.*/
+ if (g->sect <= 0 ||
+ g->head <= 0 ||
+ g->track <= 0 ||
+ g->track > UDP->tracks>>STRETCH(g) ||
+ /* check if reserved bits are set */
+ (g->stretch&~(FD_STRETCH|FD_SWAPSIDES)) != 0)
+ return -EINVAL;
+ if (type){
+ if (!suser())
+ return -EPERM;
+ LOCK_FDC(drive,1);
+ for (cnt = 0; cnt < N_DRIVE; cnt++){
+ if (ITYPE(drive_state[cnt].fd_device) == type &&
+ drive_state[cnt].fd_ref)
+ set_bit(drive, &fake_change);
+ }
+ floppy_type[type] = *g;
+ floppy_type[type].name="user format";
+ for (cnt = type << 2; cnt < (type << 2) + 4; cnt++)
+ floppy_sizes[cnt]= floppy_sizes[cnt+0x80]=
+ floppy_type[type].size>>1;
+ process_fd_request();
+ for (cnt = 0; cnt < N_DRIVE; cnt++){
+ if (ITYPE(drive_state[cnt].fd_device) == type &&
+ drive_state[cnt].fd_ref)
+ check_disk_change(
+ MKDEV(FLOPPY_MAJOR,
+ drive_state[cnt].fd_device));
+ }
+ } else {
+ LOCK_FDC(drive,1);
+ if (cmd != FDDEFPRM)
+ /* notice a disk change immediately, else
+ * we loose our settings immediately*/
+ CALL(poll_drive(1,0));
+ user_params[drive] = *g;
+ if (buffer_drive == drive)
+ SUPBOUND(buffer_max, user_params[drive].sect);
+ current_type[drive] = &user_params[drive];
+ floppy_sizes[drive] = user_params[drive].size >> 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)
+ invalidate_drive(device);
+ else
+ process_fd_request();
+ }
+ return 0;
+}
+
+/* handle obsolete ioctl's */
+static struct translation_entry {
+ int newcmd;
+ int oldcmd;
+ int oldsize; /* size of 0x00xx-style ioctl. Reflects old structures, thus
+ * use numeric values. NO SIZEOFS */
+} translation_table[]= {
+ {FDCLRPRM, 0, 0},
+ {FDSETPRM, 1, 28},
+ {FDDEFPRM, 2, 28},
+ {FDGETPRM, 3, 28},
+ {FDMSGON, 4, 0},
+ {FDMSGOFF, 5, 0},
+ {FDFMTBEG, 6, 0},
+ {FDFMTTRK, 7, 12},
+ {FDFMTEND, 8, 0},
+ {FDSETEMSGTRESH, 10, 0},
+ {FDFLUSH, 11, 0},
+ {FDSETMAXERRS, 12, 20},
+ {OLDFDRAWCMD, 30, 0},
+ {FDGETMAXERRS, 14, 20},
+ {FDGETDRVTYP, 16, 16},
+ {FDSETDRVPRM, 20, 88},
+ {FDGETDRVPRM, 21, 88},
+ {FDGETDRVSTAT, 22, 52},
+ {FDPOLLDRVSTAT, 23, 52},
+ {FDRESET, 24, 0},
+ {FDGETFDCSTAT, 25, 40},
+ {FDWERRORCLR, 27, 0},
+ {FDWERRORGET, 28, 24},
+ {FDRAWCMD, 0, 0},
+ {FDTWADDLE, 40, 0} };
+
+static inline int normalize_0x02xx_ioctl(int *cmd, int *size)
+{
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(translation_table); i++) {
+ if ((*cmd & 0xffff) == (translation_table[i].newcmd & 0xffff)){
+ *size = _IOC_SIZE(*cmd);
+ *cmd = translation_table[i].newcmd;
+ if (*size > _IOC_SIZE(*cmd)) {
+ printk("ioctl not yet supported\n");
+ return -EFAULT;
+ }
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static inline int xlate_0x00xx_ioctl(int *cmd, int *size)
+{
+ int i;
+ /* old ioctls' for kernels <= 1.3.33 */
+ /* When the next even release will come around, we'll start
+ * warning against these.
+ * When the next odd release will come around, we'll fail with
+ * -EINVAL */
+ if(strcmp(system_utsname.version, "1.4.0") >= 0)
+ printk("obsolete floppy ioctl %x\n", *cmd);
+ if((system_utsname.version[0] == '1' &&
+ strcmp(system_utsname.version, "1.5.0") >= 0) ||
+ (system_utsname.version[0] >= '2' &&
+ strcmp(system_utsname.version, "2.1.0") >= 0))
+ return -EINVAL;
+ for (i=0; i < ARRAY_SIZE(translation_table); i++) {
+ if (*cmd == translation_table[i].oldcmd) {
+ *size = translation_table[i].oldsize;
+ *cmd = translation_table[i].newcmd;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long param)
+{
+#define IOCTL_MODE_BIT 8
+#define OPEN_WRITE_BIT 16
+#define IOCTL_ALLOWED (filp && (filp->f_mode & IOCTL_MODE_BIT))
+#define OUT(c,x) case c: outparam = (const char *) (x); break
+#define IN(c,x,tag) case c: *(x) = inparam. tag ; return 0
+
+ int i,drive,type;
+ kdev_t device;
+ int ret;
+ int size;
+ union inparam {
+ struct floppy_struct g; /* geometry */
+ struct format_descr f;
+ struct floppy_max_errors max_errors;
+ struct floppy_drive_params dp;
+ } inparam; /* parameters coming from user space */
+ const char *outparam; /* parameters passed back to user space */
+
+ device = inode->i_rdev;
+ switch (cmd) {
+ RO_IOCTLS(device,param);
+ }
+ type = TYPE(device);
+ drive = DRIVE(device);
+
+ /* convert the old style command into a new style command */
+ if ((cmd & 0xff00) == 0x0200) {
+ ECALL(normalize_0x02xx_ioctl(&cmd, &size));
+ } else if ((cmd & 0xff00) == 0x0000) {
+ ECALL(xlate_0x00xx_ioctl(&cmd, &size));
+ } else
+ return -EINVAL;
+
+ /* permission checks */
+ if (((cmd & 0x80) && !suser()) ||
+ ((cmd & 0x40) && !IOCTL_ALLOWED))
+ return -EPERM;
+
+ /* verify writability of result, and fail early */
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ ECALL(verify_area(VERIFY_WRITE,(void *) param, size));
+
+ /* copyin */
+ CLEARSTRUCT(&inparam);
+ if (_IOC_DIR(cmd) & _IOC_WRITE)
+ ECALL(fd_copyin((void *)param, &inparam, size))
+
+ switch (cmd) {
+ case FDCLRPRM:
+ LOCK_FDC(drive,1);
+ current_type[drive] = NULL;
+ floppy_sizes[drive] = MAX_DISK_SIZE;
+ UDRS->keep_data = 0;
+ return invalidate_drive(device);
+ case FDSETPRM:
+ case FDDEFPRM:
+ return set_geometry(cmd, & inparam.g,
+ drive, type, device);
+ case FDGETPRM:
+ if (type)
+ outparam = (char *) &floppy_type[type];
+ else
+ outparam = (char *) current_type[drive];
+ if(!outparam)
+ return -ENODEV;
+ break;
+
+ case FDMSGON:
+ UDP->flags |= FTD_MSG;
+ return 0;
+ case FDMSGOFF:
+ UDP->flags &= ~FTD_MSG;
+ return 0;
+
+ case FDFMTBEG:
+ LOCK_FDC(drive,1);
+ CALL(poll_drive(1, FD_RAW_NEED_DISK));
+ ret = UDRS->flags;
+ process_fd_request();
+ if(ret & FD_VERIFY)
+ return -ENODEV;
+ if(!(ret & FD_DISK_WRITABLE))
+ return -EROFS;
+ return 0;
+ case FDFMTTRK:
+ if (UDRS->fd_ref != 1)
+ return -EBUSY;
+ return do_format(device, &inparam.f);
+ case FDFMTEND:
+ case FDFLUSH:
+ LOCK_FDC(drive,1);
+ return invalidate_drive(device);
+
+ case FDSETEMSGTRESH:
+ UDP->max_errors.reporting =
+ (unsigned short) (param & 0x0f);
+ return 0;
+ OUT(FDGETMAXERRS, &UDP->max_errors);
+ IN(FDSETMAXERRS, &UDP->max_errors, max_errors);
+
+ case FDGETDRVTYP:
+ outparam = drive_name(type,drive);
+ SUPBOUND(size,strlen(outparam)+1);
+ break;
+
+ IN(FDSETDRVPRM, UDP, dp);
+ OUT(FDGETDRVPRM, UDP);
+
+ case FDPOLLDRVSTAT:
+ LOCK_FDC(drive,1);
+ CALL(poll_drive(1, FD_RAW_NEED_DISK));
+ process_fd_request();
+ /* fall through */
+ OUT(FDGETDRVSTAT, UDRS);
+
+ case FDRESET:
+ return user_reset_fdc(drive, (int)param, 1);
+
+ OUT(FDGETFDCSTAT,UFDCS);
+
+ case FDWERRORCLR:
+ CLEARSTRUCT(UDRWE);
+ return 0;
+ OUT(FDWERRORGET,UDRWE);
+
+ case OLDFDRAWCMD:
+ case FDRAWCMD:
+ if (type)
+ return -EINVAL;
+ LOCK_FDC(drive,1);
+ set_floppy(device);
+ CALL(i = raw_cmd_ioctl(cmd,(void *) param));
+ process_fd_request();
+ return i;
+
+ case FDTWADDLE:
+ LOCK_FDC(drive,1);
+ twaddle();
+ process_fd_request();
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ return fd_copyout((void *)param, outparam, size);
+ else
+ return 0;
+#undef IOCTL_ALLOWED
+#undef OUT
+#undef IN
+}
+
+static void config_types(void)
+{
+ int first=1;
+ int drive;
+
+ /* read drive info out of physical cmos */
+ drive=0;
+ if (!UDP->cmos)
+ UDP->cmos= FLOPPY0_TYPE;
+ drive=1;
+ if (!UDP->cmos && FLOPPY1_TYPE)
+ UDP->cmos = FLOPPY1_TYPE;
+
+ /* XXX */
+ /* additional physical CMOS drive detection should go here */
+
+ for (drive=0; drive < N_DRIVE; drive++){
+ if (UDP->cmos >= 0 && UDP->cmos <= NUMBER(default_drive_params))
+ memcpy((char *) UDP,
+ (char *) (&default_drive_params[(int)UDP->cmos].params),
+ sizeof(struct floppy_drive_params));
+ if (UDP->cmos){
+ if (first)
+ printk("Floppy drive(s): ");
+ else
+ printk(", ");
+ first=0;
+ if (UDP->cmos > 0){
+ allowed_drive_mask |= 1 << drive;
+ printk("fd%d is %s", drive,
+ default_drive_params[(int)UDP->cmos].name);
+ } else
+ printk("fd%d is unknown type %d",drive,
+ UDP->cmos);
+ }
+ }
+ if (!first)
+ printk("\n");
+}
+
+static int floppy_read(struct inode * inode, struct file * filp,
+ char * buf, int count)
+{
+ int drive = DRIVE(inode->i_rdev);
+
+ check_disk_change(inode->i_rdev);
+ if (UTESTF(FD_DISK_CHANGED))
+ return -ENXIO;
+ return block_read(inode, filp, buf, count);
+}
+
+static int floppy_write(struct inode * inode, struct file * filp,
+ const char * buf, int count)
+{
+ int block;
+ int ret;
+ int drive = DRIVE(inode->i_rdev);
+
+ if (!UDRS->maxblock)
+ UDRS->maxblock=1;/* make change detectable */
+ check_disk_change(inode->i_rdev);
+ if (UTESTF(FD_DISK_CHANGED))
+ return -ENXIO;
+ if (!UTESTF(FD_DISK_WRITABLE))
+ return -EROFS;
+ block = (filp->f_pos + count) >> 9;
+ INFBOUND(UDRS->maxblock, block);
+ ret= block_write(inode, filp, buf, count);
+ return ret;
+}
+
+static void floppy_release(struct inode * inode, struct file * filp)
+{
+ int drive;
+
+ drive = DRIVE(inode->i_rdev);
+
+ if (!filp || (filp->f_mode & (2 | OPEN_WRITE_BIT)))
+ /* if the file is mounted OR (writable now AND writable at
+ * open time) Linus: Does this cover all cases? */
+ block_fsync(inode,filp);
+
+ if (UDRS->fd_ref < 0)
+ UDRS->fd_ref=0;
+ else if (!UDRS->fd_ref--) {
+ DPRINT("floppy_release with fd_ref == 0");
+ UDRS->fd_ref = 0;
+ }
+ floppy_release_irq_and_dma();
+}
+
+/*
+ * floppy_open check for aliasing (/dev/fd0 can be the same as
+ * /dev/PS0 etc), and disallows simultaneous access to the same
+ * drive with different device numbers.
+ */
+#define RETERR(x) do{floppy_release(inode,filp); return -(x);}while(0)
+
+static int floppy_open(struct inode * inode, struct file * filp)
+{
+ int drive;
+ int old_dev;
+ int try;
+ char *tmp;
+
+ if (!filp) {
+ DPRINT("Weird, open called with filp=0\n");
+ return -EIO;
+ }
+
+ drive = DRIVE(inode->i_rdev);
+ if (drive >= N_DRIVE ||
+ !(allowed_drive_mask & (1 << drive)) ||
+ fdc_state[FDC(drive)].version == FDC_NONE)
+ return -ENXIO;
+
+ if (TYPE(inode->i_rdev) >= NUMBER(floppy_type))
+ return -ENXIO;
+ old_dev = UDRS->fd_device;
+ if (UDRS->fd_ref && old_dev != MINOR(inode->i_rdev))
+ return -EBUSY;
+
+ if (!UDRS->fd_ref && (UDP->flags & FD_BROKEN_DCL)){
+ USETF(FD_DISK_CHANGED);
+ USETF(FD_VERIFY);
+ }
+
+ if (UDRS->fd_ref == -1 ||
+ (UDRS->fd_ref && (filp->f_flags & O_EXCL)))
+ return -EBUSY;
+
+ if (floppy_grab_irq_and_dma())
+ return -EBUSY;
+
+ if (filp->f_flags & O_EXCL)
+ UDRS->fd_ref = -1;
+ else
+ UDRS->fd_ref++;
+
+ if (!floppy_track_buffer){
+ /* if opening an ED drive, reserve a big buffer,
+ * else reserve a small one */
+ if ((UDP->cmos == 6) || (UDP->cmos == 5))
+ try = 64; /* Only 48 actually useful */
+ else
+ try = 32; /* Only 24 actually useful */
+
+ tmp=(char *)dma_mem_alloc(1024 * try);
+ if (!tmp) {
+ try >>= 1; /* buffer only one side */
+ INFBOUND(try, 16);
+ tmp= (char *)dma_mem_alloc(1024*try);
+ }
+ if (!tmp) {
+ DPRINT("Unable to allocate DMA memory\n");
+ RETERR(ENXIO);
+ }
+ if (floppy_track_buffer){
+ free_pages((unsigned long)tmp,__get_order(try*1024));
+ }else {
+ buffer_min = buffer_max = -1;
+ floppy_track_buffer = tmp;
+ max_buffer_sectors = try;
+ }
+ }
+
+ UDRS->fd_device = MINOR(inode->i_rdev);
+ if (old_dev != -1 && old_dev != MINOR(inode->i_rdev)) {
+ if (buffer_drive == drive)
+ buffer_track = -1;
+ invalidate_buffers(MKDEV(FLOPPY_MAJOR,old_dev));
+ }
+
+ /* Allow ioctls if we have write-permissions even if read-only open */
+ if ((filp->f_mode & 2) || (permission(inode,2) == 0))
+ filp->f_mode |= IOCTL_MODE_BIT;
+ if (filp->f_mode & 2)
+ filp->f_mode |= OPEN_WRITE_BIT;
+
+ if (UFDCS->rawcmd == 1)
+ UFDCS->rawcmd = 2;
+
+ if (filp->f_flags & O_NDELAY)
+ return 0;
+ if (filp->f_mode & 3) {
+ UDRS->last_checked = 0;
+ check_disk_change(inode->i_rdev);
+ if (UTESTF(FD_DISK_CHANGED))
+ RETERR(ENXIO);
+ }
+ if ((filp->f_mode & 2) && !(UTESTF(FD_DISK_WRITABLE)))
+ RETERR(EROFS);
+ return 0;
+#undef RETERR
+}
+
+/*
+ * Check if the disk has been changed or if a change has been faked.
+ */
+static int check_floppy_change(kdev_t dev)
+{
+ int drive = DRIVE(dev);
+
+ if (MAJOR(dev) != MAJOR_NR) {
+ DPRINT("floppy_changed: not a floppy\n");
+ return 0;
+ }
+
+ if (UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY))
+ return 1;
+
+ if (UDRS->last_checked + UDP->checkfreq < jiffies){
+ lock_fdc(drive,0);
+ poll_drive(0,0);
+ process_fd_request();
+ }
+
+ if (UTESTF(FD_DISK_CHANGED) ||
+ UTESTF(FD_VERIFY) ||
+ test_bit(drive, &fake_change) ||
+ (!TYPE(dev) && !current_type[drive]))
+ return 1;
+ return 0;
+}
+
+/* revalidate the floppy disk, i.e. trigger format autodetection by reading
+ * the bootblock (block 0). "Autodetection" is also needed to check whether
+ * there is a disk in the drive at all... Thus we also do it for fixed
+ * geometry formats */
+static int floppy_revalidate(kdev_t dev)
+{
+#define NO_GEOM (!current_type[drive] && !TYPE(dev))
+ struct buffer_head * bh;
+ int drive=DRIVE(dev);
+ int cf;
+
+ if (UTESTF(FD_DISK_CHANGED) ||
+ UTESTF(FD_VERIFY) ||
+ test_bit(drive, &fake_change) ||
+ NO_GEOM){
+ lock_fdc(drive,0);
+ cf = UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY);
+ if (!(cf || test_bit(drive, &fake_change) || NO_GEOM)){
+ process_fd_request(); /*already done by another thread*/
+ return 0;
+ }
+ UDRS->maxblock = 0;
+ UDRS->maxtrack = 0;
+ if (buffer_drive == drive)
+ buffer_track = -1;
+ clear_bit(drive, &fake_change);
+ UCLEARF(FD_DISK_CHANGED);
+ if (cf)
+ UDRS->generation++;
+ if (NO_GEOM){
+ /* auto-sensing */
+ int size = floppy_blocksizes[MINOR(dev)];
+ if (!size)
+ size = 1024;
+ if (!(bh = getblk(dev,0,size))){
+ process_fd_request();
+ return 1;
+ }
+ if (bh && !buffer_uptodate(bh))
+ ll_rw_block(READ, 1, &bh);
+ process_fd_request();
+ wait_on_buffer(bh);
+ brelse(bh);
+ return 0;
+ }
+ if (cf)
+ poll_drive(0, FD_RAW_NEED_DISK);
+ process_fd_request();
+ }
+ return 0;
+}
+
+static struct file_operations floppy_fops = {
+ NULL, /* lseek - default */
+ floppy_read, /* read - general block-dev read */
+ floppy_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ fd_ioctl, /* ioctl */
+ NULL, /* mmap */
+ floppy_open, /* open */
+ floppy_release, /* release */
+ block_fsync, /* fsync */
+ NULL, /* fasync */
+ check_floppy_change, /* media_change */
+ floppy_revalidate, /* revalidate */
+};
+
+/*
+ * Floppy Driver initialisation
+ * =============================
+ */
+
+/* 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 %d init: DUMPREGS: unexpected return of %d bytes.\n",
+ fdc, 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 %d init: VERSION: unexpected return of %d bytes.\n",
+ fdc, 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 %d init: UNLOCK: unexpected return of %d bytes.\n",
+ fdc, r);
+ return FDC_UNKNOWN;
+ }
+ output_byte(FD_PARTID);
+ r = result();
+ if (r != 1) {
+ printk("FDC %d init: PARTID: unexpected return of %d bytes.\n",
+ fdc, r);
+ return FDC_UNKNOWN;
+ }
+ if (reply_buffer[0] == 0x80) {
+ printk("FDC %d is a post-1991 82077\n",fdc);
+ return FDC_82077; /* Revised 82077AA passes all the tests */
+ }
+ switch (reply_buffer[0] >> 5) {
+ case 0x0:
+ output_byte(FD_SAVE);
+ r = result();
+ if (r != 16) {
+ printk("FDC %d init: SAVE: unexpected return of %d bytes.\n", fdc, r);
+ return FDC_UNKNOWN;
+ }
+ if (!(reply_buffer[0] & 0x40)) {
+ printk("FDC %d is a 3Volt 82078SL.\n",fdc);
+ return FDC_82078;
+ }
+ /* Either a 82078-1 or a 82078SL running at 5Volt */
+ printk("FDC %d is a 82078-1.\n",fdc);
+ return FDC_82078_1;
+ case 0x1:
+ printk("FDC %d is a 44pin 82078\n",fdc);
+ return FDC_82078;
+ case 0x2:
+ printk("FDC %d is a S82078B\n", fdc);
+ return FDC_S82078B;
+ case 0x3:
+ printk("FDC %d is a National Semiconductor PC87306\n", fdc);
+ return FDC_87306;
+ default:
+ printk("FDC %d init: 82077 variant with PARTID=%d.\n",
+ fdc, reply_buffer[0] >> 5);
+ return FDC_82077_UNKN;
+ }
+} /* get_fdc_version */
+
+/* lilo configuration */
+
+/* we make the invert_dcl function global. One day, somebody might
+ * want to centralize all thinkpad related options into one lilo option,
+ * there are just so many thinkpad related quirks! */
+void floppy_invert_dcl(int *ints,int param)
+{
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(default_drive_params); i++){
+ if (param)
+ default_drive_params[i].params.flags |= 0x80;
+ else
+ default_drive_params[i].params.flags &= ~0x80;
+ }
+ DPRINT("Configuring drives for inverted dcl\n");
+}
+
+static void daring(int *ints,int param)
+{
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(default_drive_params); i++){
+ if (param){
+ default_drive_params[i].params.select_delay = 0;
+ default_drive_params[i].params.flags |= FD_SILENT_DCL_CLEAR;
+ } else {
+ default_drive_params[i].params.select_delay = 2*HZ/100;
+ default_drive_params[i].params.flags &= ~FD_SILENT_DCL_CLEAR;
+ }
+ }
+ DPRINT1("Assuming %s floppy hardware\n", param ? "standard" : "broken");
+}
+
+static void allow_drives(int *ints, int param)
+{
+ allowed_drive_mask=param;
+ DPRINT1("setting allowed_drive_mask to 0x%x\n", param);
+}
+
+static void fdc2_adr(int *ints, int param)
+{
+ FDC2 = param;
+ if (param)
+ DPRINT1("enabling second fdc at address 0x%3x\n", FDC2);
+ else
+ DPRINT("disabling second fdc\n");
+}
+
+static void unex(int *ints,int param)
+{
+ print_unex = param;
+ DPRINT1("%sprinting messages for unexpected interrupts\n",
+ param ? "" : "not ");
+}
+
+static void set_cmos(int *ints, int dummy)
+{
+ int current_drive=0;
+
+ if (ints[0] != 2){
+ DPRINT("wrong number of parameter for cmos\n");
+ return;
+ }
+ current_drive = ints[1];
+ if (current_drive < 0 || current_drive >= 8){
+ DPRINT("bad drive for set_cmos\n");
+ return;
+ }
+ if (current_drive >= 4 && !FDC2)
+ fdc2_adr(0, 0x370);
+ if (ints[2] <= 0 || ints[2] >= NUMBER(default_drive_params)){
+ DPRINT1("bad cmos code %d\n", ints[2]);
+ return;
+ }
+ DP->cmos = ints[2];
+ DPRINT1("setting cmos code to %d\n", ints[2]);
+}
+
+static struct param_table {
+ const char *name;
+ void (*fn)(int *ints, int param);
+ int def_param;
+} config_params[]={
+ { "allowed_drive_mask", allow_drives, 0xff },
+ { "all_drives", allow_drives, 0xff },
+ { "asus_pci", allow_drives, 0x33 },
+
+ { "daring", daring, 1},
+
+ { "two_fdc", fdc2_adr, 0x370 },
+ { "one_fdc", fdc2_adr, 0 },
+
+ { "thinkpad", floppy_invert_dcl, 1 },
+
+ { "cmos", set_cmos, 0 },
+
+ { "unexpected_interrupts", unex, 1 },
+ { "no_unexpected_interrupts", unex, 0 },
+ { "L40SX", unex, 0 } };
+
+#define FLOPPY_SETUP
+void floppy_setup(char *str, int *ints)
+{
+ int i;
+ int param;
+ if (str)
+ for (i=0; i< ARRAY_SIZE(config_params); i++){
+ if (strcmp(str,config_params[i].name) == 0){
+ if (ints[0])
+ param = ints[1];
+ else
+ param = config_params[i].def_param;
+ config_params[i].fn(ints,param);
+ return;
+ }
+ }
+ if (str) {
+ DPRINT1("unknown floppy option [%s]\n", str);
+
+ DPRINT("allowed options are:");
+ for (i=0; i< ARRAY_SIZE(config_params); i++)
+ printk(" %s",config_params[i].name);
+ printk("\n");
+ } else
+ DPRINT("botched floppy option\n");
+ DPRINT("Read linux/drivers/block/README.fd\n");
+}
+
+int floppy_init(void)
+{
+ int i,unit,drive;
+ int have_no_fdc= -EIO;
+
+ raw_cmd = 0;
+
+ sti();
+
+ if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
+ printk("Unable to get major %d for floppy\n",MAJOR_NR);
+ return -EBUSY;
+ }
+
+ for (i=0; i<256; i++)
+ if (ITYPE(i))
+ floppy_sizes[i] = floppy_type[ITYPE(i)].size >> 1;
+ else
+ floppy_sizes[i] = MAX_DISK_SIZE;
+
+ blk_size[MAJOR_NR] = floppy_sizes;
+ blksize_size[MAJOR_NR] = floppy_blocksizes;
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ reschedule_timeout(MAXTIMEOUT, "floppy init", MAXTIMEOUT);
+ config_types();
+
+ for (i = 0; i < N_FDC; i++) {
+ fdc = i;
+ CLEARSTRUCT(FDCS);
+ FDCS->dtr = -1;
+ FDCS->dor = 0x4;
+ }
+
+ fdc_state[0].address = FDC1;
+#if N_FDC > 1
+ fdc_state[1].address = FDC2;
+#endif
+
+ if (floppy_grab_irq_and_dma()){
+ unregister_blkdev(MAJOR_NR,"fd");
+ return -EBUSY;
+ }
+
+ /* initialise drive state */
+ for (drive = 0; drive < N_DRIVE; drive++) {
+ CLEARSTRUCT(UDRS);
+ CLEARSTRUCT(UDRWE);
+ UDRS->flags = FD_VERIFY | FD_DISK_NEWCHANGE | FD_DISK_CHANGED;
+ UDRS->fd_device = -1;
+ floppy_track_buffer = NULL;
+ max_buffer_sectors = 0;
+ }
+
+ for (i = 0; i < N_FDC; i++) {
+ fdc = i;
+ FDCS->driver_version = FD_DRIVER_VERSION;
+ for (unit=0; unit<4; unit++)
+ FDCS->track[unit] = 0;
+ if (FDCS->address == -1)
+ continue;
+ FDCS->rawcmd = 2;
+ if (user_reset_fdc(-1,FD_RESET_ALWAYS,0)){
+ FDCS->address = -1;
+ continue;
+ }
+ /* Try to determine the floppy controller type */
+ FDCS->version = get_fdc_version();
+ if (FDCS->version == FDC_NONE){
+ FDCS->address = -1;
+ continue;
+ }
+
+ request_region(FDCS->address, 6, "floppy");
+ request_region(FDCS->address+7, 1, "floppy DIR");
+ /* address + 6 is reserved, and may be taken by IDE.
+ * Unfortunately, Adaptec doesn't know this :-(, */
+
+ have_no_fdc = 0;
+ /* Not all FDCs seem to be able to handle the version command
+ * properly, so force a reset for the standard FDC clones,
+ * to avoid interrupt garbage.
+ */
+ FDCS->has_fifo = FDCS->version >= FDC_82077_ORIG;
+ user_reset_fdc(-1,FD_RESET_ALWAYS,0);
+ }
+ fdc=0;
+ del_timer(&fd_timeout);
+ current_drive = 0;
+ floppy_release_irq_and_dma();
+ initialising=0;
+ if (have_no_fdc) {
+ DPRINT("no floppy controllers found\n");
+ unregister_blkdev(MAJOR_NR,"fd");
+ } else
+ virtual_dma_init();
+ return have_no_fdc;
+}
+
+static int floppy_grab_irq_and_dma(void)
+{
+ int i;
+ cli();
+ if (usage_count++){
+ sti();
+ return 0;
+ }
+ sti();
+ MOD_INC_USE_COUNT;
+ for (i=0; i< N_FDC; i++){
+ if (FDCS->address != -1){
+ fdc = i;
+ reset_fdc_info(1);
+ fd_outb(FDCS->dor, FD_DOR);
+ }
+ }
+ set_dor(0, ~0, 8); /* avoid immediate interrupt */
+
+ if (fd_request_irq()) {
+ DPRINT1("Unable to grab IRQ%d for the floppy driver\n",
+ FLOPPY_IRQ);
+ return -1;
+ }
+ if (fd_request_dma()) {
+ DPRINT1("Unable to grab DMA%d for the floppy driver\n",
+ FLOPPY_DMA);
+ fd_free_irq();
+ return -1;
+ }
+ for (fdc = 0; fdc < N_FDC; fdc++)
+ if (FDCS->address != -1)
+ fd_outb(FDCS->dor, FD_DOR);
+ fdc = 0;
+ fd_enable_irq();
+ return 0;
+}
+
+static void floppy_release_irq_and_dma(void)
+{
+#ifdef FLOPPY_SANITY_CHECK
+ int drive;
+#endif
+ long tmpsize;
+ void *tmpaddr;
+
+ cli();
+ if (--usage_count){
+ sti();
+ return;
+ }
+ sti();
+ MOD_DEC_USE_COUNT;
+ fd_disable_dma();
+ fd_free_dma();
+ fd_disable_irq();
+ fd_free_irq();
+
+ set_dor(0, ~0, 8);
+#if N_FDC > 1
+ set_dor(1, ~8, 0);
+#endif
+ floppy_enable_hlt();
+
+ if (floppy_track_buffer && max_buffer_sectors) {
+ tmpsize = max_buffer_sectors*1024;
+ tmpaddr = (void *)floppy_track_buffer;
+ floppy_track_buffer = 0;
+ max_buffer_sectors = 0;
+ buffer_min = buffer_max = -1;
+ free_pages((unsigned long)tmpaddr, __get_order(tmpsize));
+ }
+
+#ifdef FLOPPY_SANITY_CHECK
+ for (drive=0; drive < N_FDC * 4; drive++)
+ if (motor_off_timer[drive].next)
+ printk("motor off timer %d still active\n", drive);
+
+ if (fd_timeout.next)
+ printk("floppy timer still active:%s\n", timeout_message);
+ if (fd_timer.next)
+ printk("auxiliary floppy timer still active\n");
+ if (floppy_tq.sync)
+ printk("task queue still active\n");
+#endif
+}
+
+
+#ifdef MODULE
+
+extern char *get_options(char *str, int *ints);
+
+static void mod_setup(char *pattern, void (*setup)(char *, int *))
+{
+ int i;
+ char c;
+ int j;
+ int match;
+ char buffer[100];
+ int ints[11];
+ int length = strlen(pattern)+1;
+
+ match=0;
+ j=1;
+
+ for (i=current->mm->env_start; i< current->mm->env_end; i ++){
+ c= get_fs_byte(i);
+ if (match){
+ if (j==99)
+ c='\0';
+ buffer[j] = c;
+ if (!c || c == ' ' || c == '\t'){
+ if (j){
+ buffer[j] = '\0';
+ setup(get_options(buffer,ints),ints);
+ }
+ j=0;
+ } else
+ j++;
+ if (!c)
+ break;
+ continue;
+ }
+ if ((!j && !c) || (j && c == pattern[j-1]))
+ j++;
+ else
+ j=0;
+ if (j==length){
+ match=1;
+ j=0;
+ }
+ }
+}
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+int init_module(void)
+{
+ printk("inserting floppy driver for %s\n", kernel_version);
+
+ mod_setup("floppy=", floppy_setup);
+
+ return floppy_init();
+}
+
+void cleanup_module(void)
+{
+ int fdc;
+
+ for (fdc=0; fdc<2; fdc++)
+ if (FDCS->address != -1){
+ release_region(FDCS->address, 6);
+ release_region(FDCS->address+7, 1);
+ }
+
+ unregister_blkdev(MAJOR_NR, "fd");
+
+ blk_dev[MAJOR_NR].request_fn = 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/i386/i386at/gpl/linux/block/genhd.c b/i386/i386at/gpl/linux/block/genhd.c
new file mode 100644
index 0000000..60cba6c
--- /dev/null
+++ b/i386/i386at/gpl/linux/block/genhd.c
@@ -0,0 +1,610 @@
+/*
+ * Code extracted from
+ * linux/kernel/hd.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ *
+ * Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
+ * in the early extended-partition checks and added DM partitions
+ *
+ * Support for DiskManager v6.0x added by Mark Lord (mlord@bnr.ca)
+ * with information provided by OnTrack. This now works for linux fdisk
+ * and LILO, as well as loadlin and bootln. Note that disks other than
+ * /dev/hda *must* have a "DOS" type 0x51 partition in the first slot (hda1).
+ *
+ * More flexible handling of extended partitions - aeb, 950831
+ *
+ * Check partition table on IDE disks for common CHS translations
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/string.h>
+
+#include <asm/system.h>
+
+#ifdef __alpha__
+/*
+ * On the Alpha, we get unaligned access exceptions on
+ * p->nr_sects and p->start_sect, when the partition table
+ * is not on a 4-byte boundary, which is frequently the case.
+ * This code uses unaligned load instructions to prevent
+ * such exceptions.
+ */
+#include <asm/unaligned.h>
+#define NR_SECTS(p) ldl_u(&p->nr_sects)
+#define START_SECT(p) ldl_u(&p->start_sect)
+#else /* __alpha__ */
+#define NR_SECTS(p) p->nr_sects
+#define START_SECT(p) p->start_sect
+#endif /* __alpha__ */
+
+#ifdef MACH
+#include <i386/ipl.h>
+#endif
+
+struct gendisk *gendisk_head = NULL;
+
+static int current_minor = 0;
+extern int *blk_size[];
+extern void rd_load(void);
+
+extern int chr_dev_init(void);
+extern int blk_dev_init(void);
+extern int scsi_dev_init(void);
+extern int net_dev_init(void);
+
+static void print_minor_name (struct gendisk *hd, int minor)
+{
+ unsigned int unit = minor >> hd->minor_shift;
+ unsigned int part = minor & ((1 << hd->minor_shift) - 1);
+
+#ifdef CONFIG_BLK_DEV_IDE
+ /*
+ * IDE devices use multiple major numbers, but the drives
+ * are named as: {hda,hdb}, {hdc,hdd}, {hde,hdf}, {hdg,hdh}..
+ * This requires some creative handling here to find the
+ * correct name to use, with some help from ide.c
+ */
+ if (!strcmp(hd->major_name,"ide")) {
+ char name[16]; /* more than large enough */
+ strcpy(name, hd->real_devices); /* courtesy ide.c */
+ name[strlen(name)-1] += unit;
+ printk(" %s", name);
+ } else
+#endif
+ printk(" %s%c", hd->major_name, 'a' + unit);
+ if (part)
+ printk("%d", part);
+ else
+ printk(":");
+}
+
+static void add_partition (struct gendisk *hd, int minor, int start, int size)
+{
+ hd->part[minor].start_sect = start;
+ hd->part[minor].nr_sects = size;
+ print_minor_name(hd, minor);
+}
+
+static inline int is_extended_partition(struct partition *p)
+{
+ return (p->sys_ind == DOS_EXTENDED_PARTITION ||
+ p->sys_ind == LINUX_EXTENDED_PARTITION);
+}
+
+#ifdef CONFIG_MSDOS_PARTITION
+/*
+ * Create devices for each logical partition in an extended partition.
+ * The logical partitions form a linked list, with each entry being
+ * a partition table with two entries. The first entry
+ * is the real data partition (with a start relative to the partition
+ * table start). The second is a pointer to the next logical partition
+ * (with a start relative to the entire extended partition).
+ * We do not create a Linux partition for the partition tables, but
+ * only for the actual data partitions.
+ */
+
+static void extended_partition(struct gendisk *hd, kdev_t dev)
+{
+ struct buffer_head *bh;
+ struct partition *p;
+ unsigned long first_sector, first_size, this_sector, this_size;
+ int mask = (1 << hd->minor_shift) - 1;
+ int i;
+
+ first_sector = hd->part[MINOR(dev)].start_sect;
+ first_size = hd->part[MINOR(dev)].nr_sects;
+ this_sector = first_sector;
+
+ while (1) {
+ if ((current_minor & mask) == 0)
+ return;
+ if (!(bh = bread(dev,0,1024)))
+ return;
+ /*
+ * This block is from a device that we're about to stomp on.
+ * So make sure nobody thinks this block is usable.
+ */
+ bh->b_state = 0;
+
+ if (*(unsigned short *) (bh->b_data+510) != 0xAA55)
+ goto done;
+
+ p = (struct partition *) (0x1BE + bh->b_data);
+
+ this_size = hd->part[MINOR(dev)].nr_sects;
+
+ /*
+ * Usually, the first entry is the real data partition,
+ * the 2nd entry is the next extended partition, or empty,
+ * and the 3rd and 4th entries are unused.
+ * However, DRDOS sometimes has the extended partition as
+ * the first entry (when the data partition is empty),
+ * and OS/2 seems to use all four entries.
+ */
+
+ /*
+ * First process the data partition(s)
+ */
+ for (i=0; i<4; i++, p++) {
+ if (!NR_SECTS(p) || is_extended_partition(p))
+ continue;
+
+ /* Check the 3rd and 4th entries -
+ these sometimes contain random garbage */
+ if (i >= 2
+ && START_SECT(p) + NR_SECTS(p) > this_size
+ && (this_sector + START_SECT(p) < first_sector ||
+ this_sector + START_SECT(p) + NR_SECTS(p) >
+ first_sector + first_size))
+ continue;
+
+ add_partition(hd, current_minor, this_sector+START_SECT(p), NR_SECTS(p));
+ current_minor++;
+ if ((current_minor & mask) == 0)
+ goto done;
+ }
+ /*
+ * Next, process the (first) extended partition, if present.
+ * (So far, there seems to be no reason to make
+ * extended_partition() recursive and allow a tree
+ * of extended partitions.)
+ * It should be a link to the next logical partition.
+ * Create a minor for this just long enough to get the next
+ * partition table. The minor will be reused for the next
+ * data partition.
+ */
+ p -= 4;
+ for (i=0; i<4; i++, p++)
+ if(NR_SECTS(p) && is_extended_partition(p))
+ break;
+ if (i == 4)
+ goto done; /* nothing left to do */
+
+ hd->part[current_minor].nr_sects = NR_SECTS(p);
+ hd->part[current_minor].start_sect = first_sector + START_SECT(p);
+ this_sector = first_sector + START_SECT(p);
+ dev = MKDEV(hd->major, current_minor);
+ brelse(bh);
+ }
+done:
+ brelse(bh);
+}
+
+static int msdos_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector)
+{
+ int i, minor = current_minor;
+ struct buffer_head *bh;
+ struct partition *p;
+ unsigned char *data;
+ int mask = (1 << hd->minor_shift) - 1;
+#ifdef CONFIG_BLK_DEV_IDE
+ int tested_for_xlate = 0;
+
+read_mbr:
+#endif
+ if (!(bh = bread(dev,0,1024))) {
+ printk(" unable to read partition table\n");
+ return -1;
+ }
+ data = bh->b_data;
+ /* In some cases we modify the geometry */
+ /* of the drive (below), so ensure that */
+ /* nobody else tries to re-use this data. */
+ bh->b_state = 0;
+#ifdef CONFIG_BLK_DEV_IDE
+check_table:
+#endif
+ if (*(unsigned short *) (0x1fe + data) != 0xAA55) {
+ brelse(bh);
+ return 0;
+ }
+ p = (struct partition *) (0x1be + data);
+
+#ifdef CONFIG_BLK_DEV_IDE
+ if (!tested_for_xlate++) { /* Do this only once per disk */
+ /*
+ * Look for various forms of IDE disk geometry translation
+ */
+ extern int ide_xlate_1024(kdev_t, int, const char *);
+ unsigned int sig = *(unsigned short *)(data + 2);
+ if (p->sys_ind == EZD_PARTITION) {
+ /*
+ * The remainder of the disk must be accessed using
+ * a translated geometry that reduces the number of
+ * apparent cylinders to less than 1024 if possible.
+ *
+ * ide_xlate_1024() will take care of the necessary
+ * adjustments to fool fdisk/LILO and partition check.
+ */
+ if (ide_xlate_1024(dev, -1, " [EZD]")) {
+ data += 512;
+ goto check_table;
+ }
+ } else if (p->sys_ind == DM6_PARTITION) {
+
+ /*
+ * Everything on the disk is offset by 63 sectors,
+ * including a "new" MBR with its own partition table,
+ * and the remainder of the disk must be accessed using
+ * a translated geometry that reduces the number of
+ * apparent cylinders to less than 1024 if possible.
+ *
+ * ide_xlate_1024() will take care of the necessary
+ * adjustments to fool fdisk/LILO and partition check.
+ */
+ if (ide_xlate_1024(dev, 1, " [DM6:DDO]")) {
+ brelse(bh);
+ goto read_mbr; /* start over with new MBR */
+ }
+ } else if (sig <= 0x1ae && *(unsigned short *)(data + sig) == 0x55AA
+ && (1 & *(unsigned char *)(data + sig + 2)) )
+ {
+ /*
+ * DM6 signature in MBR, courtesy of OnTrack
+ */
+ (void) ide_xlate_1024 (dev, 0, " [DM6:MBR]");
+ } else if (p->sys_ind == DM6_AUX1PARTITION || p->sys_ind == DM6_AUX3PARTITION) {
+ /*
+ * DM6 on other than the first (boot) drive
+ */
+ (void) ide_xlate_1024(dev, 0, " [DM6:AUX]");
+ } else {
+ /*
+ * Examine the partition table for common translations.
+ * This is necessary for drives for situations where
+ * the translated geometry is unavailable from the BIOS.
+ */
+ for (i = 0; i < 4 ; i++) {
+ struct partition *q = &p[i];
+ if (NR_SECTS(q) && q->sector == 1 && q->end_sector == 63) {
+ unsigned int heads = q->end_head + 1;
+ if (heads == 32 || heads == 64 || heads == 128) {
+
+ (void) ide_xlate_1024(dev, heads, " [PTBL]");
+ break;
+ }
+ }
+ }
+ }
+ }
+#endif /* CONFIG_BLK_DEV_IDE */
+
+ current_minor += 4; /* first "extra" minor (for extended partitions) */
+ for (i=1 ; i<=4 ; minor++,i++,p++) {
+ if (!NR_SECTS(p))
+ continue;
+ add_partition(hd, minor, first_sector+START_SECT(p), NR_SECTS(p));
+ if (is_extended_partition(p)) {
+ printk(" <");
+ /*
+ * If we are rereading the partition table, we need
+ * to set the size of the partition so that we will
+ * be able to bread the block containing the extended
+ * partition info.
+ */
+ hd->sizes[minor] = hd->part[minor].nr_sects
+ >> (BLOCK_SIZE_BITS - 9);
+ extended_partition(hd, MKDEV(hd->major, minor));
+ printk(" >");
+ /* prevent someone doing mkfs or mkswap on an
+ extended partition, but leave room for LILO */
+ if (hd->part[minor].nr_sects > 2)
+ hd->part[minor].nr_sects = 2;
+ }
+ }
+ /*
+ * Check for old-style Disk Manager partition table
+ */
+ if (*(unsigned short *) (data+0xfc) == 0x55AA) {
+ p = (struct partition *) (0x1be + data);
+ for (i = 4 ; i < 16 ; i++, current_minor++) {
+ p--;
+ if ((current_minor & mask) == 0)
+ break;
+ if (!(START_SECT(p) && NR_SECTS(p)))
+ continue;
+ add_partition(hd, current_minor, START_SECT(p), NR_SECTS(p));
+ }
+ }
+ printk("\n");
+ brelse(bh);
+ return 1;
+}
+
+#endif /* CONFIG_MSDOS_PARTITION */
+
+#ifdef CONFIG_OSF_PARTITION
+
+static int osf_partition(struct gendisk *hd, unsigned int dev, unsigned long first_sector)
+{
+ int i;
+ int mask = (1 << hd->minor_shift) - 1;
+ struct buffer_head *bh;
+ struct disklabel {
+ u32 d_magic;
+ u16 d_type,d_subtype;
+ u8 d_typename[16];
+ u8 d_packname[16];
+ u32 d_secsize;
+ u32 d_nsectors;
+ u32 d_ntracks;
+ u32 d_ncylinders;
+ u32 d_secpercyl;
+ u32 d_secprtunit;
+ u16 d_sparespertrack;
+ u16 d_sparespercyl;
+ u32 d_acylinders;
+ u16 d_rpm, d_interleave, d_trackskew, d_cylskew;
+ u32 d_headswitch, d_trkseek, d_flags;
+ u32 d_drivedata[5];
+ u32 d_spare[5];
+ u32 d_magic2;
+ u16 d_checksum;
+ u16 d_npartitions;
+ u32 d_bbsize, d_sbsize;
+ struct d_partition {
+ u32 p_size;
+ u32 p_offset;
+ u32 p_fsize;
+ u8 p_fstype;
+ u8 p_frag;
+ u16 p_cpg;
+ } d_partitions[8];
+ } * label;
+ struct d_partition * partition;
+#define DISKLABELMAGIC (0x82564557UL)
+
+ if (!(bh = bread(dev,0,1024))) {
+ printk("unable to read partition table\n");
+ return -1;
+ }
+ label = (struct disklabel *) (bh->b_data+64);
+ partition = label->d_partitions;
+ if (label->d_magic != DISKLABELMAGIC) {
+ printk("magic: %08x\n", label->d_magic);
+ brelse(bh);
+ return 0;
+ }
+ if (label->d_magic2 != DISKLABELMAGIC) {
+ printk("magic2: %08x\n", label->d_magic2);
+ brelse(bh);
+ return 0;
+ }
+ for (i = 0 ; i < label->d_npartitions; i++, partition++) {
+ if ((current_minor & mask) == 0)
+ break;
+ if (partition->p_size)
+ add_partition(hd, current_minor,
+ first_sector+partition->p_offset,
+ partition->p_size);
+ current_minor++;
+ }
+ printk("\n");
+ brelse(bh);
+ return 1;
+}
+
+#endif /* CONFIG_OSF_PARTITION */
+
+#ifdef CONFIG_SUN_PARTITION
+
+static int sun_partition(struct gendisk *hd, unsigned int dev, unsigned long first_sector)
+{
+ int i, csum;
+ unsigned short *ush;
+ struct buffer_head *bh;
+ struct sun_disklabel {
+ unsigned char info[128]; /* Informative text string */
+ unsigned char spare[292]; /* Boot information etc. */
+ unsigned short rspeed; /* Disk rotational speed */
+ unsigned short pcylcount; /* Physical cylinder count */
+ unsigned short sparecyl; /* extra sects per cylinder */
+ unsigned char spare2[4]; /* More magic... */
+ unsigned short ilfact; /* Interleave factor */
+ unsigned short ncyl; /* Data cylinder count */
+ unsigned short nacyl; /* Alt. cylinder count */
+ unsigned short ntrks; /* Tracks per cylinder */
+ unsigned short nsect; /* Sectors per track */
+ unsigned char spare3[4]; /* Even more magic... */
+ struct sun_partition {
+ unsigned long start_cylinder;
+ unsigned long num_sectors;
+ } partitions[8];
+ unsigned short magic; /* Magic number */
+ unsigned short csum; /* Label xor'd checksum */
+ } * label;
+ struct sun_partition *p;
+ unsigned long spc;
+#define SUN_LABEL_MAGIC 0xDABE
+
+ if(!(bh = bread(dev, 0, 1024))) {
+ printk("Dev %d: unable to read partition table\n", dev);
+ return -1;
+ }
+ label = (struct sun_disklabel *) bh->b_data;
+ p = label->partitions;
+ if(label->magic != SUN_LABEL_MAGIC) {
+ printk("Dev %d Sun disklabel: bad magic %08x\n", dev, label->magic);
+ brelse(bh);
+ return 0;
+ }
+ /* Look at the checksum */
+ ush = ((unsigned short *) (label+1)) - 1;
+ for(csum = 0; ush >= ((unsigned short *) label);)
+ csum ^= *ush--;
+ if(csum) {
+ printk("Dev %d Sun disklabel: Csum bad, label corrupted\n", dev);
+ brelse(bh);
+ return 0;
+ }
+ /* All Sun disks have 8 partition entries */
+ spc = (label->ntrks * label->nsect);
+ for(i=0; i < 8; i++, p++) {
+ unsigned long st_sector;
+
+ /* We register all partitions, even if zero size, so that
+ * the minor numbers end up ok as per SunOS interpretation.
+ */
+ st_sector = first_sector + (p->start_cylinder * spc);
+ add_partition(hd, current_minor, st_sector, p->num_sectors);
+ current_minor++;
+ }
+ printk("\n");
+ brelse(bh);
+ return 1;
+}
+
+#endif /* CONFIG_SUN_PARTITION */
+
+static void check_partition(struct gendisk *hd, kdev_t dev)
+{
+ static int first_time = 1;
+ unsigned long first_sector;
+
+ if (first_time)
+ printk("Partition check:\n");
+ first_time = 0;
+ first_sector = hd->part[MINOR(dev)].start_sect;
+
+ /*
+ * This is a kludge to allow the partition check to be
+ * skipped for specific drives (e.g. IDE cd-rom drives)
+ */
+ if ((int)first_sector == -1) {
+ hd->part[MINOR(dev)].start_sect = 0;
+ return;
+ }
+
+ printk(" ");
+ print_minor_name(hd, MINOR(dev));
+#ifdef CONFIG_MSDOS_PARTITION
+ if (msdos_partition(hd, dev, first_sector))
+ return;
+#endif
+#ifdef CONFIG_OSF_PARTITION
+ if (osf_partition(hd, dev, first_sector))
+ return;
+#endif
+#ifdef CONFIG_SUN_PARTITION
+ if(sun_partition(hd, dev, first_sector))
+ return;
+#endif
+ printk(" unknown partition table\n");
+}
+
+/* This function is used to re-read partition tables for removable disks.
+ Much of the cleanup from the old partition tables should have already been
+ done */
+
+/* This function will re-read the partition tables for a given device,
+and set things back up again. There are some important caveats,
+however. You must ensure that no one is using the device, and no one
+can start using the device while this function is being executed. */
+
+void resetup_one_dev(struct gendisk *dev, int drive)
+{
+ int i;
+ int first_minor = drive << dev->minor_shift;
+ int end_minor = first_minor + dev->max_p;
+
+ blk_size[dev->major] = NULL;
+ current_minor = 1 + first_minor;
+ check_partition(dev, MKDEV(dev->major, first_minor));
+
+ /*
+ * We need to set the sizes array before we will be able to access
+ * any of the partitions on this device.
+ */
+ if (dev->sizes != NULL) { /* optional safeguard in ll_rw_blk.c */
+ for (i = first_minor; i < end_minor; i++)
+ dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
+ blk_size[dev->major] = dev->sizes;
+ }
+}
+
+static void setup_dev(struct gendisk *dev)
+{
+ int i, drive;
+ int end_minor = dev->max_nr * dev->max_p;
+
+ blk_size[dev->major] = NULL;
+ for (i = 0 ; i < end_minor; i++) {
+ dev->part[i].start_sect = 0;
+ dev->part[i].nr_sects = 0;
+ }
+ dev->init(dev);
+ for (drive = 0 ; drive < dev->nr_real ; drive++) {
+ int first_minor = drive << dev->minor_shift;
+ current_minor = 1 + first_minor;
+ check_partition(dev, MKDEV(dev->major, first_minor));
+ }
+ if (dev->sizes != NULL) { /* optional safeguard in ll_rw_blk.c */
+ for (i = 0; i < end_minor; i++)
+ dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
+ blk_size[dev->major] = dev->sizes;
+ }
+}
+
+void device_setup(void)
+{
+ extern void console_map_init(void);
+ struct gendisk *p;
+ int nr=0;
+#ifdef MACH
+ extern int linux_intr_pri;
+
+ linux_intr_pri = SPL5;
+#endif
+
+#ifndef MACH
+ chr_dev_init();
+#endif
+ blk_dev_init();
+ sti();
+#ifdef CONFIG_SCSI
+ scsi_dev_init();
+#endif
+#ifdef CONFIG_INET
+#ifdef MACH
+ linux_intr_pri = SPL6;
+#endif
+ net_dev_init();
+#endif
+#ifndef MACH
+ console_map_init();
+#endif
+
+ for (p = gendisk_head ; p ; p=p->next) {
+ setup_dev(p);
+ nr += p->nr_real;
+ }
+#ifdef CONFIG_BLK_DEV_RAM
+ rd_load();
+#endif
+}
diff --git a/i386/i386at/gpl/linux/block/ide-cd.c b/i386/i386at/gpl/linux/block/ide-cd.c
new file mode 100644
index 0000000..6dc9380
--- /dev/null
+++ b/i386/i386at/gpl/linux/block/ide-cd.c
@@ -0,0 +1,2770 @@
+/*
+ * linux/drivers/block/ide-cd.c
+ *
+ * 1.00 Oct 31, 1994 -- Initial version.
+ * 1.01 Nov 2, 1994 -- Fixed problem with starting request in
+ * cdrom_check_status.
+ * 1.03 Nov 25, 1994 -- leaving unmask_intr[] as a user-setting (as for disks)
+ * (from mlord) -- minor changes to cdrom_setup()
+ * -- renamed ide_dev_s to ide_drive_t, enable irq on command
+ * 2.00 Nov 27, 1994 -- Generalize packet command interface;
+ * add audio ioctls.
+ * 2.01 Dec 3, 1994 -- Rework packet command interface to handle devices
+ * which send an interrupt when ready for a command.
+ * 2.02 Dec 11, 1994 -- Cache the TOC in the driver.
+ * Don't use SCMD_PLAYAUDIO_TI; it's not included
+ * in the current version of ATAPI.
+ * Try to use LBA instead of track or MSF addressing
+ * when possible.
+ * Don't wait for READY_STAT.
+ * 2.03 Jan 10, 1995 -- Rewrite block read routines to handle block sizes
+ * other than 2k and to move multiple sectors in a
+ * single transaction.
+ * 2.04 Apr 21, 1995 -- Add work-around for Creative Labs CD220E drives.
+ * Thanks to Nick Saw <cwsaw@pts7.pts.mot.com> for
+ * help in figuring this out. Ditto for Acer and
+ * Aztech drives, which seem to have the same problem.
+ * 2.04b May 30, 1995 -- Fix to match changes in ide.c version 3.16 -ml
+ * 2.05 Jun 8, 1995 -- Don't attempt to retry after an illegal request
+ * or data protect error.
+ * Use HWIF and DEV_HWIF macros as in ide.c.
+ * Always try to do a request_sense after
+ * a failed command.
+ * Include an option to give textual descriptions
+ * of ATAPI errors.
+ * Fix a bug in handling the sector cache which
+ * showed up if the drive returned data in 512 byte
+ * blocks (like Pioneer drives). Thanks to
+ * Richard Hirst <srh@gpt.co.uk> for diagnosing this.
+ * Properly supply the page number field in the
+ * MODE_SELECT command.
+ * PLAYAUDIO12 is broken on the Aztech; work around it.
+ * 2.05x Aug 11, 1995 -- lots of data structure renaming/restructuring in ide.c
+ * (my apologies to Scott, but now ide-cd.c is independent)
+ * 3.00 Aug 22, 1995 -- Implement CDROMMULTISESSION ioctl.
+ * Implement CDROMREADAUDIO ioctl (UNTESTED).
+ * Use input_ide_data() and output_ide_data().
+ * Add door locking.
+ * Fix usage count leak in cdrom_open, which happened
+ * when a read-write mount was attempted.
+ * Try to load the disk on open.
+ * Implement CDROMEJECT_SW ioctl (off by default).
+ * Read total cdrom capacity during open.
+ * Rearrange logic in cdrom_decode_status. Issue
+ * request sense commands for failed packet commands
+ * from here instead of from cdrom_queue_packet_command.
+ * Fix a race condition in retrieving error information.
+ * Suppress printing normal unit attention errors and
+ * some drive not ready errors.
+ * Implement CDROMVOLREAD ioctl.
+ * Implement CDROMREADMODE1/2 ioctls.
+ * Fix race condition in setting up interrupt handlers
+ * when the `serialize' option is used.
+ * 3.01 Sep 2, 1995 -- Fix ordering of reenabling interrupts in
+ * cdrom_queue_request.
+ * Another try at using ide_[input,output]_data.
+ * 3.02 Sep 16, 1995 -- Stick total disk capacity in partition table as well.
+ * Make VERBOSE_IDE_CD_ERRORS dump failed command again.
+ * Dump out more information for ILLEGAL REQUEST errs.
+ * Fix handling of errors occuring before the
+ * packet command is transferred.
+ * Fix transfers with odd bytelengths.
+ * 3.03 Oct 27, 1995 -- Some Creative drives have an id of just `CD'.
+ * `DCI-2S10' drives are broken too.
+ * 3.04 Nov 20, 1995 -- So are Vertos drives.
+ * 3.05 Dec 1, 1995 -- Changes to go with overhaul of ide.c and ide-tape.c
+ * 3.06 Dec 16, 1995 -- Add support needed for partitions.
+ * More workarounds for Vertos bugs (based on patches
+ * from Holger Dietze <dietze@aix520.informatik.uni-leipzig.de>).
+ * Try to eliminate byteorder assumptions.
+ * Use atapi_cdrom_subchnl struct definition.
+ * Add STANDARD_ATAPI compilation option.
+ * 3.07 Jan 29, 1996 -- More twiddling for broken drives: Sony 55D,
+ * Vertos 300.
+ * Add NO_DOOR_LOCKING configuration option.
+ * Handle drive_cmd requests w/NULL args (for hdparm -t).
+ * Work around sporadic Sony55e audio play problem.
+ * 3.07a Feb 11, 1996 -- check drive->id for NULL before dereferencing, to fix
+ * problem with "hde=cdrom" with no drive present. -ml
+ *
+ * NOTE: Direct audio reads will only work on some types of drive.
+ * So far, i've received reports of success for Sony and Toshiba drives.
+ *
+ * ATAPI cd-rom driver. To be used with ide.c.
+ *
+ * Copyright (C) 1994, 1995, 1996 scott snyder <snyder@fnald0.fnal.gov>
+ * May be copied or modified under the terms of the GNU General Public License
+ * (../../COPYING).
+ */
+
+
+/***************************************************************************/
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/cdrom.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/segment.h>
+#ifdef __alpha__
+# include <asm/unaligned.h>
+#endif
+
+#include "ide.h"
+
+
+
+/* Turn this on to have the driver print out the meanings of the
+ ATAPI error codes. This will use up additional kernel-space
+ memory, though. */
+
+#ifndef VERBOSE_IDE_CD_ERRORS
+#define VERBOSE_IDE_CD_ERRORS 0
+#endif
+
+
+/* Turning this on will remove code to work around various nonstandard
+ ATAPI implementations. If you know your drive follows the standard,
+ this will give you a slightly smaller kernel. */
+
+#ifndef STANDARD_ATAPI
+#define STANDARD_ATAPI 0
+#endif
+
+
+/* Turning this on will disable the door-locking functionality.
+ This is apparently needed for supermount. */
+
+#ifndef NO_DOOR_LOCKING
+#define NO_DOOR_LOCKING 0
+#endif
+
+
+/************************************************************************/
+
+#define SECTOR_SIZE 512
+#define SECTOR_BITS 9
+#define SECTORS_PER_FRAME (CD_FRAMESIZE / SECTOR_SIZE)
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+/* special command codes for strategy routine. */
+#define PACKET_COMMAND 4315
+#define REQUEST_SENSE_COMMAND 4316
+#define RESET_DRIVE_COMMAND 4317
+
+/* Some ATAPI command opcodes (just like SCSI).
+ (Some other cdrom-specific codes are in cdrom.h.) */
+#define TEST_UNIT_READY 0x00
+#define REQUEST_SENSE 0x03
+#define START_STOP 0x1b
+#define ALLOW_MEDIUM_REMOVAL 0x1e
+#define READ_CAPACITY 0x25
+#define READ_10 0x28
+#define MODE_SENSE_10 0x5a
+#define MODE_SELECT_10 0x55
+#define READ_CD 0xbe
+
+
+/* ATAPI sense keys (mostly copied from scsi.h). */
+
+#define NO_SENSE 0x00
+#define RECOVERED_ERROR 0x01
+#define NOT_READY 0x02
+#define MEDIUM_ERROR 0x03
+#define HARDWARE_ERROR 0x04
+#define ILLEGAL_REQUEST 0x05
+#define UNIT_ATTENTION 0x06
+#define DATA_PROTECT 0x07
+#define ABORTED_COMMAND 0x0b
+#define MISCOMPARE 0x0e
+
+/* We want some additional flags for cd-rom drives.
+ To save space in the ide_drive_t struct, use some fields which
+ doesn't make sense for cd-roms -- `bios_sect' and `bios_head'. */
+
+/* Configuration flags. These describe the capabilities of the drive.
+ They generally do not change after initialization, unless we learn
+ more about the drive from stuff failing. */
+struct ide_cd_config_flags {
+ __u8 drq_interrupt : 1; /* Device sends an interrupt when ready
+ for a packet command. */
+ __u8 no_doorlock : 1; /* Drive cannot lock the door. */
+#if ! STANDARD_ATAPI
+ __u8 no_playaudio12: 1; /* The PLAYAUDIO12 command is not supported. */
+
+ __u8 no_lba_toc : 1; /* Drive cannot return TOC info in LBA format. */
+ __u8 playmsf_uses_bcd : 1; /* Drive uses BCD in PLAYAUDIO_MSF. */
+ __u8 old_readcd : 1; /* Drive uses old READ CD opcode. */
+ __u8 vertos_lossage: 1; /* Drive is a Vertos 300,
+ and likes to speak BCD. */
+#endif /* not STANDARD_ATAPI */
+ __u8 reserved : 1;
+};
+#define CDROM_CONFIG_FLAGS(drive) ((struct ide_cd_config_flags *)&((drive)->bios_sect))
+
+
+/* State flags. These give information about the current state of the
+ drive, and will change during normal operation. */
+struct ide_cd_state_flags {
+ __u8 media_changed : 1; /* Driver has noticed a media change. */
+ __u8 toc_valid : 1; /* Saved TOC information is current. */
+ __u8 door_locked : 1; /* We think that the drive door is locked. */
+ __u8 eject_on_close: 1; /* Drive should eject when device is closed. */
+ __u8 reserved : 4;
+};
+#define CDROM_STATE_FLAGS(drive) ((struct ide_cd_state_flags *)&((drive)->bios_head))
+
+
+#define SECTOR_BUFFER_SIZE CD_FRAMESIZE
+
+
+
+/****************************************************************************
+ * Routines to read and write data from/to the drive, using
+ * the routines input_ide_data() and output_ide_data() from ide.c.
+ *
+ * These routines will round up any request for an odd number of bytes,
+ * so if an odd bytecount is specified, be sure that there's at least one
+ * extra byte allocated for the buffer.
+ */
+
+
+static inline
+void cdrom_in_bytes (ide_drive_t *drive, void *buffer, uint bytecount)
+{
+ ++bytecount;
+ ide_input_data (drive, buffer, bytecount / 4);
+ if ((bytecount & 0x03) >= 2)
+ {
+ insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
+ }
+}
+
+
+static inline
+void cdrom_out_bytes (ide_drive_t *drive, void *buffer, uint bytecount)
+{
+ ++bytecount;
+ ide_output_data (drive, buffer, bytecount / 4);
+ if ((bytecount & 0x03) >= 2)
+ {
+ outsw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
+ }
+}
+
+
+
+/****************************************************************************
+ * Descriptions of ATAPI error codes.
+ */
+
+#define ARY_LEN(a) ((sizeof(a) / sizeof(a[0])))
+
+#if VERBOSE_IDE_CD_ERRORS
+
+/* From Table 124 of the ATAPI 1.2 spec. */
+
+char *sense_key_texts[16] = {
+ "No sense data",
+ "Recovered error",
+ "Not ready",
+ "Medium error",
+ "Hardware error",
+ "Illegal request",
+ "Unit attention",
+ "Data protect",
+ "(reserved)",
+ "(reserved)",
+ "(reserved)",
+ "Aborted command",
+ "(reserved)",
+ "(reserved)",
+ "Miscompare",
+ "(reserved)",
+};
+
+
+/* From Table 125 of the ATAPI 1.2 spec. */
+
+struct {
+ short asc_ascq;
+ char *text;
+} sense_data_texts[] = {
+ { 0x0000, "No additional sense information" },
+ { 0x0011, "Audio play operation in progress" },
+ { 0x0012, "Audio play operation paused" },
+ { 0x0013, "Audio play operation successfully completed" },
+ { 0x0014, "Audio play operation stopped due to error" },
+ { 0x0015, "No current audio status to return" },
+
+ { 0x0200, "No seek complete" },
+
+ { 0x0400, "Logical unit not ready - cause not reportable" },
+ { 0x0401, "Logical unit not ready - in progress (sic) of becoming ready" },
+ { 0x0402, "Logical unit not ready - initializing command required" },
+ { 0x0403, "Logical unit not ready - manual intervention required" },
+
+ { 0x0600, "No reference position found" },
+
+ { 0x0900, "Track following error" },
+ { 0x0901, "Tracking servo failure" },
+ { 0x0902, "Focus servo failure" },
+ { 0x0903, "Spindle servo failure" },
+
+ { 0x1100, "Unrecovered read error" },
+ { 0x1106, "CIRC unrecovered error" },
+
+ { 0x1500, "Random positioning error" },
+ { 0x1501, "Mechanical positioning error" },
+ { 0x1502, "Positioning error detected by read of medium" },
+
+ { 0x1700, "Recovered data with no error correction applied" },
+ { 0x1701, "Recovered data with retries" },
+ { 0x1702, "Recovered data with positive head offset" },
+ { 0x1703, "Recovered data with negative head offset" },
+ { 0x1704, "Recovered data with retries and/or CIRC applied" },
+ { 0x1705, "Recovered data using previous sector ID" },
+
+ { 0x1800, "Recovered data with error correction applied" },
+ { 0x1801, "Recovered data with error correction and retries applied" },
+ { 0x1802, "Recovered data - the data was auto-reallocated" },
+ { 0x1803, "Recovered data with CIRC" },
+ { 0x1804, "Recovered data with L-EC" },
+ { 0x1805, "Recovered data - recommend reassignment" },
+ { 0x1806, "Recovered data - recommend rewrite" },
+
+ { 0x1a00, "Parameter list length error" },
+
+ { 0x2000, "Invalid command operation code" },
+
+ { 0x2100, "Logical block address out of range" },
+
+ { 0x2400, "Invalid field in command packet" },
+
+ { 0x2600, "Invalid field in parameter list" },
+ { 0x2601, "Parameter not supported" },
+ { 0x2602, "Parameter value invalid" },
+ { 0x2603, "Threshold parameters not supported" },
+
+ { 0x2800, "Not ready to ready transition, medium may have changed" },
+
+ { 0x2900, "Power on, reset or bus device reset occurred" },
+
+ { 0x2a00, "Parameters changed" },
+ { 0x2a01, "Mode parameters changed" },
+
+ { 0x3000, "Incompatible medium installed" },
+ { 0x3001, "Cannot read medium - unknown format" },
+ { 0x3002, "Cannot read medium - incompatible format" },
+
+ { 0x3700, "Rounded parameter" },
+
+ { 0x3900, "Saving parameters not supported" },
+
+ { 0x3a00, "Medium not present" },
+
+ { 0x3f00, "ATAPI CD-ROM drive operating conditions have changed" },
+ { 0x3f01, "Microcode has been changed" },
+ { 0x3f02, "Changed operating definition" },
+ { 0x3f03, "Inquiry data has changed" },
+
+ { 0x4000, "Diagnostic failure on component (ASCQ)" },
+
+ { 0x4400, "Internal ATAPI CD-ROM drive failure" },
+
+ { 0x4e00, "Overlapped commands attempted" },
+
+ { 0x5300, "Media load or eject failed" },
+ { 0x5302, "Medium removal prevented" },
+
+ { 0x5700, "Unable to recover table of contents" },
+
+ { 0x5a00, "Operator request or state change input (unspecified)" },
+ { 0x5a01, "Operator medium removal request" },
+
+ { 0x5b00, "Threshold condition met" },
+
+ { 0x5c00, "Status change" },
+
+ { 0x6300, "End of user area encountered on this track" },
+
+ { 0x6400, "Illegal mode for this track" },
+
+ { 0xbf00, "Loss of streaming" },
+};
+#endif
+
+
+
+/****************************************************************************
+ * Generic packet command support and error handling routines.
+ */
+
+
+static
+void cdrom_analyze_sense_data (ide_drive_t *drive,
+ struct atapi_request_sense *reqbuf,
+ struct packet_command *failed_command)
+{
+ /* Don't print not ready or unit attention errors for READ_SUBCHANNEL.
+ Workman (and probably other programs) uses this command to poll
+ the drive, and we don't want to fill the syslog with useless errors. */
+ if (failed_command &&
+ failed_command->c[0] == SCMD_READ_SUBCHANNEL &&
+ (reqbuf->sense_key == NOT_READY || reqbuf->sense_key == UNIT_ATTENTION))
+ return;
+
+#if VERBOSE_IDE_CD_ERRORS
+ {
+ int i;
+ char *s;
+ char buf[80];
+
+ printk ("ATAPI device %s:\n", drive->name);
+
+ printk (" Error code: 0x%02x\n", reqbuf->error_code);
+
+ if (reqbuf->sense_key >= 0 &&
+ reqbuf->sense_key < ARY_LEN (sense_key_texts))
+ s = sense_key_texts[reqbuf->sense_key];
+ else
+ s = "(bad sense key)";
+
+ printk (" Sense key: 0x%02x - %s\n", reqbuf->sense_key, s);
+
+ if (reqbuf->asc == 0x40) {
+ sprintf (buf, "Diagnostic failure on component 0x%02x", reqbuf->ascq);
+ s = buf;
+ }
+
+ else {
+ int lo, hi;
+ int key = (reqbuf->asc << 8);
+ if ( ! (reqbuf->ascq >= 0x80 && reqbuf->ascq <= 0xdd) )
+ key |= reqbuf->ascq;
+
+ lo = 0;
+ hi = ARY_LEN (sense_data_texts);
+ s = NULL;
+
+ while (hi > lo) {
+ int mid = (lo + hi) / 2;
+ if (sense_data_texts[mid].asc_ascq == key) {
+ s = sense_data_texts[mid].text;
+ break;
+ }
+ else if (sense_data_texts[mid].asc_ascq > key)
+ hi = mid;
+ else
+ lo = mid+1;
+ }
+ }
+
+ if (s == NULL) {
+ if (reqbuf->asc > 0x80)
+ s = "(vendor-specific error)";
+ else
+ s = "(reserved error code)";
+ }
+
+ printk (" Additional sense data: 0x%02x, 0x%02x - %s\n",
+ reqbuf->asc, reqbuf->ascq, s);
+
+ if (failed_command != NULL) {
+ printk (" Failed packet command: ");
+ for (i=0; i<sizeof (failed_command->c); i++)
+ printk ("%02x ", failed_command->c[i]);
+ printk ("\n");
+ }
+
+ if (reqbuf->sense_key == ILLEGAL_REQUEST &&
+ (reqbuf->sense_key_specific[0] & 0x80) != 0)
+ {
+ printk (" Error in %s byte %d",
+ (reqbuf->sense_key_specific[0] & 0x40) != 0
+ ? "command packet"
+ : "command data",
+ (reqbuf->sense_key_specific[1] << 8) +
+ reqbuf->sense_key_specific[2]);
+
+ if ((reqbuf->sense_key_specific[0] & 0x40) != 0)
+ {
+ printk (" bit %d", reqbuf->sense_key_specific[0] & 0x07);
+ }
+
+ printk ("\n");
+ }
+ }
+
+#else
+
+ /* Suppress printing unit attention and `in progress of becoming ready'
+ errors when we're not being verbose. */
+
+ if (reqbuf->sense_key == UNIT_ATTENTION ||
+ (reqbuf->sense_key == NOT_READY && (reqbuf->asc == 4 ||
+ reqbuf->asc == 0x3a)))
+ return;
+
+ printk ("%s: code: 0x%02x key: 0x%02x asc: 0x%02x ascq: 0x%02x\n",
+ drive->name,
+ reqbuf->error_code, reqbuf->sense_key, reqbuf->asc, reqbuf->ascq);
+#endif
+}
+
+
+/* Fix up a possibly partially-processed request so that we can
+ start it over entirely, or even put it back on the request queue. */
+static void restore_request (struct request *rq)
+{
+ if (rq->buffer != rq->bh->b_data)
+ {
+ int n = (rq->buffer - rq->bh->b_data) / SECTOR_SIZE;
+ rq->buffer = rq->bh->b_data;
+ rq->nr_sectors += n;
+ rq->sector -= n;
+ }
+ rq->current_nr_sectors = rq->bh->b_size >> SECTOR_BITS;
+}
+
+
+static void cdrom_queue_request_sense (ide_drive_t *drive,
+ struct semaphore *sem,
+ struct atapi_request_sense *reqbuf,
+ struct packet_command *failed_command)
+{
+ struct request *rq;
+ struct packet_command *pc;
+ int len;
+
+ /* If the request didn't explicitly specify where to put the sense data,
+ use the statically allocated structure. */
+ if (reqbuf == NULL)
+ reqbuf = &drive->cdrom_info.sense_data;
+
+ /* Make up a new request to retrieve sense information. */
+
+ pc = &HWIF(drive)->request_sense_pc;
+ memset (pc, 0, sizeof (*pc));
+
+ /* The request_sense structure has an odd number of (16-bit) words,
+ which won't work well with 32-bit transfers. However, we don't care
+ about the last two bytes, so just truncate the structure down
+ to an even length. */
+ len = sizeof (*reqbuf) / 4;
+ len *= 4;
+
+ pc->c[0] = REQUEST_SENSE;
+ pc->c[4] = len;
+ pc->buffer = (char *)reqbuf;
+ pc->buflen = len;
+ pc->sense_data = (struct atapi_request_sense *)failed_command;
+
+ /* stuff the sense request in front of our current request */
+
+ rq = &HWIF(drive)->request_sense_request;
+ ide_init_drive_cmd (rq);
+ rq->cmd = REQUEST_SENSE_COMMAND;
+ rq->buffer = (char *)pc;
+ rq->sem = sem;
+ (void) ide_do_drive_cmd (drive, rq, ide_preempt);
+}
+
+
+static void cdrom_end_request (int uptodate, ide_drive_t *drive)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+
+ /* The code in blk.h can screw us up on error recovery if the block
+ size is larger than 1k. Fix that up here. */
+ if (!uptodate && rq->bh != 0)
+ {
+ int adj = rq->current_nr_sectors - 1;
+ rq->current_nr_sectors -= adj;
+ rq->sector += adj;
+ }
+
+ if (rq->cmd == REQUEST_SENSE_COMMAND && uptodate)
+ {
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+ cdrom_analyze_sense_data (drive,
+ (struct atapi_request_sense *)(pc->buffer - pc->c[4]),
+ (struct packet_command *)pc->sense_data);
+ }
+
+ ide_end_request (uptodate, HWGROUP(drive));
+}
+
+
+/* Mark that we've seen a media change, and invalidate our internal
+ buffers. */
+static void cdrom_saw_media_change (ide_drive_t *drive)
+{
+ CDROM_STATE_FLAGS (drive)->media_changed = 1;
+ CDROM_STATE_FLAGS (drive)->toc_valid = 0;
+ drive->cdrom_info.nsectors_buffered = 0;
+}
+
+
+/* Returns 0 if the request should be continued.
+ Returns 1 if the request was ended. */
+static int cdrom_decode_status (ide_drive_t *drive, int good_stat, int *stat_ret)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ int stat, err, sense_key, cmd;
+
+ /* Check for errors. */
+ stat = GET_STAT();
+ *stat_ret = stat;
+
+ if (OK_STAT (stat, good_stat, BAD_R_STAT))
+ return 0;
+
+ /* Got an error. */
+ err = IN_BYTE (IDE_ERROR_REG);
+ sense_key = err >> 4;
+
+ if (rq == NULL)
+ printk ("%s : missing request in cdrom_decode_status\n", drive->name);
+ else
+ {
+ cmd = rq->cmd;
+
+ if (cmd == REQUEST_SENSE_COMMAND)
+ {
+ /* We got an error trying to get sense info from the drive
+ (probably while trying to recover from a former error).
+ Just give up. */
+
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+ pc->stat = 1;
+ cdrom_end_request (1, drive);
+ ide_error (drive, "request sense failure", stat);
+ return 1;
+ }
+
+ else if (cmd == PACKET_COMMAND)
+ {
+ /* All other functions, except for READ. */
+
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+ struct semaphore *sem = NULL;
+
+ /* Check for tray open. */
+ if (sense_key == NOT_READY)
+ {
+ cdrom_saw_media_change (drive);
+
+ /* Print an error message to the syslog.
+ Exception: don't print anything if this is a read subchannel
+ command. This is because workman constantly polls the drive
+ with this command, and we don't want to uselessly fill up
+ the syslog. */
+ if (pc->c[0] != SCMD_READ_SUBCHANNEL)
+ printk ("%s : tray open or drive not ready\n", drive->name);
+ }
+
+ /* Check for media change. */
+ else if (sense_key == UNIT_ATTENTION)
+ {
+ cdrom_saw_media_change (drive);
+ printk ("%s: media changed\n", drive->name);
+ }
+
+ /* Otherwise, print an error. */
+ else
+ {
+ ide_dump_status (drive, "packet command error", stat);
+ }
+
+ /* Set the error flag and complete the request.
+ Then, if we have a CHECK CONDITION status, queue a request
+ sense command. We must be careful, though: we don't want
+ the thread in cdrom_queue_packet_command to wake up until
+ the request sense has completed. We do this by transferring
+ the semaphore from the packet command request to the
+ request sense request. */
+
+ if ((stat & ERR_STAT) != 0)
+ {
+ sem = rq->sem;
+ rq->sem = NULL;
+ }
+
+ pc->stat = 1;
+ cdrom_end_request (1, drive);
+
+ if ((stat & ERR_STAT) != 0)
+ cdrom_queue_request_sense (drive, sem, pc->sense_data, pc);
+ }
+
+ else
+ {
+ /* Handle errors from READ requests. */
+
+ /* Check for tray open. */
+ if (sense_key == NOT_READY)
+ {
+ cdrom_saw_media_change (drive);
+
+ /* Fail the request. */
+ printk ("%s : tray open\n", drive->name);
+ cdrom_end_request (0, drive);
+ }
+
+ /* Check for media change. */
+ else if (sense_key == UNIT_ATTENTION)
+ {
+ cdrom_saw_media_change (drive);
+
+ /* Arrange to retry the request.
+ But be sure to give up if we've retried too many times. */
+ if (++rq->errors > ERROR_MAX)
+ {
+ cdrom_end_request (0, drive);
+ }
+ }
+ /* No point in retrying after an illegal request or
+ data protect error.*/
+ else if (sense_key == ILLEGAL_REQUEST || sense_key == DATA_PROTECT)
+ {
+ ide_dump_status (drive, "command error", stat);
+ cdrom_end_request (0, drive);
+ }
+
+ /* If there were other errors, go to the default handler. */
+ else if ((err & ~ABRT_ERR) != 0)
+ {
+ ide_error (drive, "cdrom_decode_status", stat);
+ return 1;
+ }
+
+ /* Else, abort if we've racked up too many retries. */
+ else if ((++rq->errors > ERROR_MAX))
+ {
+ cdrom_end_request (0, drive);
+ }
+
+ /* If we got a CHECK_CONDITION status, queue a request sense
+ command. */
+ if ((stat & ERR_STAT) != 0)
+ cdrom_queue_request_sense (drive, NULL, NULL, NULL);
+ }
+ }
+
+ /* Retry, or handle the next request. */
+ return 1;
+}
+
+
+/* Set up the device registers for transferring a packet command on DEV,
+ expecting to later transfer XFERLEN bytes. HANDLER is the routine
+ which actually transfers the command to the drive. If this is a
+ drq_interrupt device, this routine will arrange for HANDLER to be
+ called when the interrupt from the drive arrives. Otherwise, HANDLER
+ will be called immediately after the drive is prepared for the transfer. */
+
+static int cdrom_start_packet_command (ide_drive_t *drive, int xferlen,
+ ide_handler_t *handler)
+{
+ /* Wait for the controller to be idle. */
+ if (ide_wait_stat (drive, 0, BUSY_STAT, WAIT_READY)) return 1;
+
+ /* Set up the controller registers. */
+ OUT_BYTE (0, IDE_FEATURE_REG);
+ OUT_BYTE (0, IDE_NSECTOR_REG);
+ OUT_BYTE (0, IDE_SECTOR_REG);
+
+ OUT_BYTE (xferlen & 0xff, IDE_LCYL_REG);
+ OUT_BYTE (xferlen >> 8 , IDE_HCYL_REG);
+ OUT_BYTE (drive->ctl, IDE_CONTROL_REG);
+
+ if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt)
+ {
+ ide_set_handler (drive, handler, WAIT_CMD);
+ OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
+ }
+ else
+ {
+ OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
+ (*handler) (drive);
+ }
+
+ return 0;
+}
+
+
+/* Send a packet command to DRIVE described by CMD_BUF and CMD_LEN.
+ The device registers must have already been prepared
+ by cdrom_start_packet_command.
+ HANDLER is the interrupt handler to call when the command completes
+ or there's data ready. */
+static int cdrom_transfer_packet_command (ide_drive_t *drive,
+ char *cmd_buf, int cmd_len,
+ ide_handler_t *handler)
+{
+ if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt)
+ {
+ /* Here we should have been called after receiving an interrupt
+ from the device. DRQ should how be set. */
+ int stat_dum;
+
+ /* Check for errors. */
+ if (cdrom_decode_status (drive, DRQ_STAT, &stat_dum)) return 1;
+ }
+ else
+ {
+ /* Otherwise, we must wait for DRQ to get set. */
+ if (ide_wait_stat (drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) return 1;
+ }
+
+ /* Arm the interrupt handler. */
+ ide_set_handler (drive, handler, WAIT_CMD);
+
+ /* Send the command to the device. */
+ cdrom_out_bytes (drive, cmd_buf, cmd_len);
+
+ return 0;
+}
+
+
+
+/****************************************************************************
+ * Block read functions.
+ */
+
+/*
+ * Buffer up to SECTORS_TO_TRANSFER sectors from the drive in our sector
+ * buffer. Once the first sector is added, any subsequent sectors are
+ * assumed to be continuous (until the buffer is cleared). For the first
+ * sector added, SECTOR is its sector number. (SECTOR is then ignored until
+ * the buffer is cleared.)
+ */
+static void cdrom_buffer_sectors (ide_drive_t *drive, unsigned long sector,
+ int sectors_to_transfer)
+{
+ struct cdrom_info *info = &drive->cdrom_info;
+
+ /* Number of sectors to read into the buffer. */
+ int sectors_to_buffer = MIN (sectors_to_transfer,
+ (SECTOR_BUFFER_SIZE >> SECTOR_BITS) -
+ info->nsectors_buffered);
+
+ char *dest;
+
+ /* If we don't yet have a sector buffer, try to allocate one.
+ If we can't get one atomically, it's not fatal -- we'll just throw
+ the data away rather than caching it. */
+ if (info->sector_buffer == NULL)
+ {
+ info->sector_buffer = (char *) kmalloc (SECTOR_BUFFER_SIZE, GFP_ATOMIC);
+
+ /* If we couldn't get a buffer, don't try to buffer anything... */
+ if (info->sector_buffer == NULL)
+ sectors_to_buffer = 0;
+ }
+
+ /* If this is the first sector in the buffer, remember its number. */
+ if (info->nsectors_buffered == 0)
+ info->sector_buffered = sector;
+
+ /* Read the data into the buffer. */
+ dest = info->sector_buffer + info->nsectors_buffered * SECTOR_SIZE;
+ while (sectors_to_buffer > 0)
+ {
+ cdrom_in_bytes (drive, dest, SECTOR_SIZE);
+ --sectors_to_buffer;
+ --sectors_to_transfer;
+ ++info->nsectors_buffered;
+ dest += SECTOR_SIZE;
+ }
+
+ /* Throw away any remaining data. */
+ while (sectors_to_transfer > 0)
+ {
+ char dum[SECTOR_SIZE];
+ cdrom_in_bytes (drive, dum, sizeof (dum));
+ --sectors_to_transfer;
+ }
+}
+
+
+/*
+ * Check the contents of the interrupt reason register from the cdrom
+ * and attempt to recover if there are problems. Returns 0 if everything's
+ * ok; nonzero if the request has been terminated.
+ */
+static inline
+int cdrom_read_check_ireason (ide_drive_t *drive, int len, int ireason)
+{
+ ireason &= 3;
+ if (ireason == 2) return 0;
+
+ if (ireason == 0)
+ {
+ /* Whoops... The drive is expecting to receive data from us! */
+ printk ("%s: cdrom_read_intr: "
+ "Drive wants to transfer data the wrong way!\n",
+ drive->name);
+
+ /* Throw some data at the drive so it doesn't hang
+ and quit this request. */
+ while (len > 0)
+ {
+ int dum = 0;
+ cdrom_out_bytes (drive, &dum, sizeof (dum));
+ len -= sizeof (dum);
+ }
+ }
+
+ else
+ {
+ /* Drive wants a command packet, or invalid ireason... */
+ printk ("%s: cdrom_read_intr: bad interrupt reason %d\n",
+ drive->name, ireason);
+ }
+
+ cdrom_end_request (0, drive);
+ return -1;
+}
+
+
+/*
+ * Interrupt routine. Called when a read request has completed.
+ */
+static void cdrom_read_intr (ide_drive_t *drive)
+{
+ int stat;
+ int ireason, len, sectors_to_transfer, nskip;
+
+ struct request *rq = HWGROUP(drive)->rq;
+
+ /* Check for errors. */
+ if (cdrom_decode_status (drive, 0, &stat)) return;
+
+ /* Read the interrupt reason and the transfer length. */
+ ireason = IN_BYTE (IDE_NSECTOR_REG);
+ len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
+
+ /* If DRQ is clear, the command has completed. */
+ if ((stat & DRQ_STAT) == 0)
+ {
+ /* If we're not done filling the current buffer, complain.
+ Otherwise, complete the command normally. */
+ if (rq->current_nr_sectors > 0)
+ {
+ printk ("%s: cdrom_read_intr: data underrun (%ld blocks)\n",
+ drive->name, rq->current_nr_sectors);
+ cdrom_end_request (0, drive);
+ }
+ else
+ cdrom_end_request (1, drive);
+
+ return;
+ }
+
+ /* Check that the drive is expecting to do the same thing that we are. */
+ if (cdrom_read_check_ireason (drive, len, ireason)) return;
+
+ /* Assume that the drive will always provide data in multiples of at least
+ SECTOR_SIZE, as it gets hairy to keep track of the transfers otherwise. */
+ if ((len % SECTOR_SIZE) != 0)
+ {
+ printk ("%s: cdrom_read_intr: Bad transfer size %d\n",
+ drive->name, len);
+ printk (" This drive is not supported by this version of the driver\n");
+ cdrom_end_request (0, drive);
+ return;
+ }
+
+ /* The number of sectors we need to read from the drive. */
+ sectors_to_transfer = len / SECTOR_SIZE;
+
+ /* First, figure out if we need to bit-bucket any of the leading sectors. */
+ nskip = MIN ((int)(rq->current_nr_sectors - (rq->bh->b_size >> SECTOR_BITS)),
+ sectors_to_transfer);
+
+ while (nskip > 0)
+ {
+ /* We need to throw away a sector. */
+ char dum[SECTOR_SIZE];
+ cdrom_in_bytes (drive, dum, sizeof (dum));
+
+ --rq->current_nr_sectors;
+ --nskip;
+ --sectors_to_transfer;
+ }
+
+ /* Now loop while we still have data to read from the drive. */
+ while (sectors_to_transfer > 0)
+ {
+ int this_transfer;
+
+ /* If we've filled the present buffer but there's another chained
+ buffer after it, move on. */
+ if (rq->current_nr_sectors == 0 &&
+ rq->nr_sectors > 0)
+ cdrom_end_request (1, drive);
+
+ /* If the buffers are full, cache the rest of the data in our
+ internal buffer. */
+ if (rq->current_nr_sectors == 0)
+ {
+ cdrom_buffer_sectors (drive, rq->sector, sectors_to_transfer);
+ sectors_to_transfer = 0;
+ }
+ else
+ {
+ /* Transfer data to the buffers.
+ Figure out how many sectors we can transfer
+ to the current buffer. */
+ this_transfer = MIN (sectors_to_transfer,
+ rq->current_nr_sectors);
+
+ /* Read this_transfer sectors into the current buffer. */
+ while (this_transfer > 0)
+ {
+ cdrom_in_bytes (drive, rq->buffer, SECTOR_SIZE);
+ rq->buffer += SECTOR_SIZE;
+ --rq->nr_sectors;
+ --rq->current_nr_sectors;
+ ++rq->sector;
+ --this_transfer;
+ --sectors_to_transfer;
+ }
+ }
+ }
+
+ /* Done moving data!
+ Wait for another interrupt. */
+ ide_set_handler (drive, &cdrom_read_intr, WAIT_CMD);
+}
+
+
+/*
+ * Try to satisfy some of the current read request from our cached data.
+ * Returns nonzero if the request has been completed, zero otherwise.
+ */
+static int cdrom_read_from_buffer (ide_drive_t *drive)
+{
+ struct cdrom_info *info = &drive->cdrom_info;
+ struct request *rq = HWGROUP(drive)->rq;
+
+ /* Can't do anything if there's no buffer. */
+ if (info->sector_buffer == NULL) return 0;
+
+ /* Loop while this request needs data and the next block is present
+ in our cache. */
+ while (rq->nr_sectors > 0 &&
+ rq->sector >= info->sector_buffered &&
+ rq->sector < info->sector_buffered + info->nsectors_buffered)
+ {
+ if (rq->current_nr_sectors == 0)
+ cdrom_end_request (1, drive);
+
+ memcpy (rq->buffer,
+ info->sector_buffer +
+ (rq->sector - info->sector_buffered) * SECTOR_SIZE,
+ SECTOR_SIZE);
+ rq->buffer += SECTOR_SIZE;
+ --rq->current_nr_sectors;
+ --rq->nr_sectors;
+ ++rq->sector;
+ }
+
+ /* If we've satisfied the current request, terminate it successfully. */
+ if (rq->nr_sectors == 0)
+ {
+ cdrom_end_request (1, drive);
+ return -1;
+ }
+
+ /* Move on to the next buffer if needed. */
+ if (rq->current_nr_sectors == 0)
+ cdrom_end_request (1, drive);
+
+ /* If this condition does not hold, then the kluge i use to
+ represent the number of sectors to skip at the start of a transfer
+ will fail. I think that this will never happen, but let's be
+ paranoid and check. */
+ if (rq->current_nr_sectors < (rq->bh->b_size >> SECTOR_BITS) &&
+ (rq->sector % SECTORS_PER_FRAME) != 0)
+ {
+ printk ("%s: cdrom_read_from_buffer: buffer botch (%ld)\n",
+ drive->name, rq->sector);
+ cdrom_end_request (0, drive);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * Routine to send a read packet command to the drive.
+ * This is usually called directly from cdrom_start_read.
+ * However, for drq_interrupt devices, it is called from an interrupt
+ * when the drive is ready to accept the command.
+ */
+static void cdrom_start_read_continuation (ide_drive_t *drive)
+{
+ struct packet_command pc;
+ struct request *rq = HWGROUP(drive)->rq;
+
+ int nsect, sector, nframes, frame, nskip;
+
+ /* Number of sectors to transfer. */
+ nsect = rq->nr_sectors;
+
+ /* Starting sector. */
+ sector = rq->sector;
+
+ /* If the requested sector doesn't start on a cdrom block boundary,
+ we must adjust the start of the transfer so that it does,
+ and remember to skip the first few sectors. If the CURRENT_NR_SECTORS
+ field is larger than the size of the buffer, it will mean that
+ we're to skip a number of sectors equal to the amount by which
+ CURRENT_NR_SECTORS is larger than the buffer size. */
+ nskip = (sector % SECTORS_PER_FRAME);
+ if (nskip > 0)
+ {
+ /* Sanity check... */
+ if (rq->current_nr_sectors != (rq->bh->b_size >> SECTOR_BITS))
+ {
+ printk ("%s: cdrom_start_read_continuation: buffer botch (%ld)\n",
+ drive->name, rq->current_nr_sectors);
+ cdrom_end_request (0, drive);
+ return;
+ }
+
+ sector -= nskip;
+ nsect += nskip;
+ rq->current_nr_sectors += nskip;
+ }
+
+ /* Convert from sectors to cdrom blocks, rounding up the transfer
+ length if needed. */
+ nframes = (nsect + SECTORS_PER_FRAME-1) / SECTORS_PER_FRAME;
+ frame = sector / SECTORS_PER_FRAME;
+
+ /* Largest number of frames was can transfer at once is 64k-1. */
+ nframes = MIN (nframes, 65535);
+
+ /* Set up the command */
+ memset (&pc.c, 0, sizeof (pc.c));
+ pc.c[0] = READ_10;
+ pc.c[7] = (nframes >> 8);
+ pc.c[8] = (nframes & 0xff);
+#ifdef __alpha__
+ stl_u (htonl (frame), (unsigned int *) &pc.c[2]);
+#else
+ *(int *)(&pc.c[2]) = htonl (frame);
+#endif
+
+ /* Send the command to the drive and return. */
+ (void) cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c),
+ &cdrom_read_intr);
+}
+
+
+/*
+ * Start a read request from the CD-ROM.
+ */
+static void cdrom_start_read (ide_drive_t *drive, unsigned int block)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ int minor = MINOR (rq->rq_dev);
+
+ /* If the request is relative to a partition, fix it up to refer to the
+ absolute address. */
+ if ((minor & PARTN_MASK) != 0) {
+ rq->sector = block;
+ minor &= ~PARTN_MASK;
+ rq->rq_dev = MKDEV (MAJOR(rq->rq_dev), minor);
+ }
+
+ /* We may be retrying this request after an error.
+ Fix up any weirdness which might be present in the request packet. */
+ restore_request (rq);
+
+ /* Satisfy whatever we can of this request from our cached sector. */
+ if (cdrom_read_from_buffer (drive))
+ return;
+
+ /* Clear the local sector buffer. */
+ drive->cdrom_info.nsectors_buffered = 0;
+
+ /* Start sending the read request to the drive. */
+ cdrom_start_packet_command (drive, 32768, cdrom_start_read_continuation);
+}
+
+
+
+
+/****************************************************************************
+ * Execute all other packet commands.
+ */
+
+/* Forward declarations. */
+static int
+cdrom_lockdoor (ide_drive_t *drive, int lockflag,
+ struct atapi_request_sense *reqbuf);
+
+
+
+/* Interrupt routine for packet command completion. */
+static void cdrom_pc_intr (ide_drive_t *drive)
+{
+ int ireason, len, stat, thislen;
+ struct request *rq = HWGROUP(drive)->rq;
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+
+ /* Check for errors. */
+ if (cdrom_decode_status (drive, 0, &stat)) return;
+
+ /* Read the interrupt reason and the transfer length. */
+ ireason = IN_BYTE (IDE_NSECTOR_REG);
+ len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
+
+ /* If DRQ is clear, the command has completed.
+ Complain if we still have data left to transfer. */
+ if ((stat & DRQ_STAT) == 0)
+ {
+ /* Some of the trailing request sense fields are optional, and
+ some drives don't send them. Sigh. */
+ if (pc->c[0] == REQUEST_SENSE && pc->buflen > 0 && pc->buflen <= 5) {
+ while (pc->buflen > 0) {
+ *pc->buffer++ = 0;
+ --pc->buflen;
+ }
+ }
+
+ if (pc->buflen == 0)
+ cdrom_end_request (1, drive);
+ else
+ {
+ printk ("%s: cdrom_pc_intr: data underrun %d\n",
+ drive->name, pc->buflen);
+ pc->stat = 1;
+ cdrom_end_request (1, drive);
+ }
+ return;
+ }
+
+ /* Figure out how much data to transfer. */
+ thislen = pc->buflen;
+ if (thislen < 0) thislen = -thislen;
+ if (thislen > len) thislen = len;
+
+ /* The drive wants to be written to. */
+ if ((ireason & 3) == 0)
+ {
+ /* Check that we want to write. */
+ if (pc->buflen > 0)
+ {
+ printk ("%s: cdrom_pc_intr: Drive wants to transfer data the wrong way!\n",
+ drive->name);
+ pc->stat = 1;
+ thislen = 0;
+ }
+
+ /* Transfer the data. */
+ cdrom_out_bytes (drive, pc->buffer, thislen);
+
+ /* If we haven't moved enough data to satisfy the drive,
+ add some padding. */
+ while (len > thislen)
+ {
+ int dum = 0;
+ cdrom_out_bytes (drive, &dum, sizeof (dum));
+ len -= sizeof (dum);
+ }
+
+ /* Keep count of how much data we've moved. */
+ pc->buffer += thislen;
+ pc->buflen += thislen;
+ }
+
+ /* Same drill for reading. */
+ else if ((ireason & 3) == 2)
+ {
+ /* Check that we want to read. */
+ if (pc->buflen < 0)
+ {
+ printk ("%s: cdrom_pc_intr: Drive wants to transfer data the wrong way!\n",
+ drive->name);
+ pc->stat = 1;
+ thislen = 0;
+ }
+
+ /* Transfer the data. */
+ cdrom_in_bytes (drive, pc->buffer, thislen);
+
+ /* If we haven't moved enough data to satisfy the drive,
+ add some padding. */
+ while (len > thislen)
+ {
+ int dum = 0;
+ cdrom_in_bytes (drive, &dum, sizeof (dum));
+ len -= sizeof (dum);
+ }
+
+ /* Keep count of how much data we've moved. */
+ pc->buffer += thislen;
+ pc->buflen -= thislen;
+ }
+
+ else
+ {
+ printk ("%s: cdrom_pc_intr: The drive appears confused (ireason = 0x%2x)\n",
+ drive->name, ireason);
+ pc->stat = 1;
+ }
+
+ /* Now we wait for another interrupt. */
+ ide_set_handler (drive, &cdrom_pc_intr, WAIT_CMD);
+}
+
+
+static void cdrom_do_pc_continuation (ide_drive_t *drive)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+
+ /* Send the command to the drive and return. */
+ cdrom_transfer_packet_command (drive, pc->c, sizeof (pc->c), &cdrom_pc_intr);
+}
+
+
+static void cdrom_do_packet_command (ide_drive_t *drive)
+{
+ int len;
+ struct request *rq = HWGROUP(drive)->rq;
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+
+ len = pc->buflen;
+ if (len < 0) len = -len;
+
+ pc->stat = 0;
+
+ /* Start sending the command to the drive. */
+ cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation);
+}
+
+#ifndef MACH
+/* Sleep for TIME jiffies.
+ Not to be called from an interrupt handler. */
+static
+void cdrom_sleep (int time)
+{
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + time;
+ schedule ();
+}
+#endif
+
+static
+int cdrom_queue_packet_command (ide_drive_t *drive, struct packet_command *pc)
+{
+ struct atapi_request_sense my_reqbuf;
+ int retries = 10;
+ struct request req;
+
+ /* If our caller has not provided a place to stick any sense data,
+ use our own area. */
+ if (pc->sense_data == NULL)
+ pc->sense_data = &my_reqbuf;
+ pc->sense_data->sense_key = 0;
+
+ /* Start of retry loop. */
+ do {
+ ide_init_drive_cmd (&req);
+ req.cmd = PACKET_COMMAND;
+ req.buffer = (char *)pc;
+ (void) ide_do_drive_cmd (drive, &req, ide_wait);
+
+ if (pc->stat != 0)
+ {
+ /* The request failed. Retry if it was due to a unit attention status
+ (usually means media was changed). */
+ struct atapi_request_sense *reqbuf = pc->sense_data;
+
+ if (reqbuf->sense_key == UNIT_ATTENTION)
+ ;
+
+ /* Also retry if the drive is in the process of loading a disk.
+ This time, however, wait a little between retries to give
+ the drive time. */
+ else if (reqbuf->sense_key == NOT_READY && reqbuf->asc == 4)
+ {
+ cdrom_sleep (HZ);
+ }
+
+ /* Otherwise, don't retry. */
+ else
+ retries = 0;
+
+ --retries;
+ }
+
+ /* End of retry loop. */
+ } while (pc->stat != 0 && retries >= 0);
+
+
+ /* Return an error if the command failed. */
+ if (pc->stat != 0)
+ return -EIO;
+
+ else
+ {
+ /* The command succeeded. If it was anything other than a request sense,
+ eject, or door lock command, and we think that the door is presently
+ unlocked, lock it again. (The door was probably unlocked via
+ an explicit CDROMEJECT ioctl.) */
+ if (CDROM_STATE_FLAGS (drive)->door_locked == 0 &&
+ (pc->c[0] != REQUEST_SENSE &&
+ pc->c[0] != ALLOW_MEDIUM_REMOVAL &&
+ pc->c[0] != START_STOP))
+ {
+ (void) cdrom_lockdoor (drive, 1, NULL);
+ }
+ return 0;
+ }
+}
+
+
+
+/****************************************************************************
+ * drive_cmd handling.
+ *
+ * Most of the functions accessed via drive_cmd are not valid for ATAPI
+ * devices. Only attempt to execute those which actually should be valid.
+ */
+
+static
+void cdrom_do_drive_cmd (ide_drive_t *drive)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ byte *args = rq->buffer;
+
+ if (args)
+ {
+#if 0 /* This bit isn't done yet... */
+ if (args[0] == WIN_SETFEATURES &&
+ (args[2] == 0x66 || args[2] == 0xcc || args[2] == 0x02 ||
+ args[2] == 0xdd || args[2] == 0x5d))
+ {
+ OUT_BYTE (args[2], io_base + IDE_FEATURE_OFFSET);
+ <send cmd>
+ }
+ else
+#endif
+ {
+ printk ("%s: Unsupported drive command %02x %02x %02x\n",
+ drive->name, args[0], args[1], args[2]);
+ rq->errors = 1;
+ }
+ }
+
+ cdrom_end_request (1, drive);
+}
+
+
+
+/****************************************************************************
+ * cdrom driver request routine.
+ */
+
+void ide_do_rw_cdrom (ide_drive_t *drive, unsigned long block)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+
+ if (rq -> cmd == PACKET_COMMAND || rq -> cmd == REQUEST_SENSE_COMMAND)
+ cdrom_do_packet_command (drive);
+
+ else if (rq -> cmd == RESET_DRIVE_COMMAND)
+ {
+ cdrom_end_request (1, drive);
+ ide_do_reset (drive);
+ return;
+ }
+
+ else if (rq -> cmd == IDE_DRIVE_CMD)
+ cdrom_do_drive_cmd (drive);
+
+ else if (rq -> cmd != READ)
+ {
+ printk ("ide-cd: bad cmd %d\n", rq -> cmd);
+ cdrom_end_request (0, drive);
+ }
+ else
+ cdrom_start_read (drive, block);
+}
+
+
+
+/****************************************************************************
+ * Ioctl handling.
+ *
+ * Routines which queue packet commands take as a final argument a pointer
+ * to an atapi_request_sense struct. If execution of the command results
+ * in an error with a CHECK CONDITION status, this structure will be filled
+ * with the results of the subsequent request sense command. The pointer
+ * can also be NULL, in which case no sense information is returned.
+ */
+
+#if ! STANDARD_ATAPI
+static
+int bin2bcd (int x)
+{
+ return (x%10) | ((x/10) << 4);
+}
+
+
+static
+int bcd2bin (int x)
+{
+ return (x >> 4) * 10 + (x & 0x0f);
+}
+#endif /* not STANDARD_ATAPI */
+
+
+static inline
+void lba_to_msf (int lba, byte *m, byte *s, byte *f)
+{
+ lba += CD_BLOCK_OFFSET;
+ lba &= 0xffffff; /* negative lbas use only 24 bits */
+ *m = lba / (CD_SECS * CD_FRAMES);
+ lba %= (CD_SECS * CD_FRAMES);
+ *s = lba / CD_FRAMES;
+ *f = lba % CD_FRAMES;
+}
+
+
+static inline
+int msf_to_lba (byte m, byte s, byte f)
+{
+ return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_BLOCK_OFFSET;
+}
+
+
+static int
+cdrom_check_status (ide_drive_t *drive,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+
+ pc.sense_data = reqbuf;
+ pc.c[0] = TEST_UNIT_READY;
+
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+/* Lock the door if LOCKFLAG is nonzero; unlock it otherwise. */
+static int
+cdrom_lockdoor (ide_drive_t *drive, int lockflag,
+ struct atapi_request_sense *reqbuf)
+{
+ struct atapi_request_sense my_reqbuf;
+ int stat;
+ struct packet_command pc;
+
+ if (reqbuf == NULL)
+ reqbuf = &my_reqbuf;
+
+ /* If the drive cannot lock the door, just pretend. */
+ if (CDROM_CONFIG_FLAGS (drive)->no_doorlock)
+ stat = 0;
+ else
+ {
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.c[0] = ALLOW_MEDIUM_REMOVAL;
+ pc.c[4] = (lockflag != 0);
+ stat = cdrom_queue_packet_command (drive, &pc);
+ }
+
+ if (stat == 0)
+ CDROM_STATE_FLAGS (drive)->door_locked = lockflag;
+ else
+ {
+ /* If we got an illegal field error, the drive
+ probably cannot lock the door. */
+ if (reqbuf->sense_key == ILLEGAL_REQUEST && reqbuf->asc == 0x24)
+ {
+ printk ("%s: door locking not supported\n", drive->name);
+ CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+ stat = 0;
+ CDROM_STATE_FLAGS (drive)->door_locked = lockflag;
+ }
+ }
+ return stat;
+}
+
+
+/* Eject the disk if EJECTFLAG is 0.
+ If EJECTFLAG is 1, try to reload the disk. */
+static int
+cdrom_eject (ide_drive_t *drive, int ejectflag,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.c[0] = START_STOP;
+ pc.c[4] = 2 + (ejectflag != 0);
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+static int
+cdrom_pause (ide_drive_t *drive, int pauseflag,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.c[0] = SCMD_PAUSE_RESUME;
+ pc.c[8] = !pauseflag;
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+static int
+cdrom_startstop (ide_drive_t *drive, int startflag,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.c[0] = START_STOP;
+ pc.c[1] = 1;
+ pc.c[4] = startflag;
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+static int
+cdrom_read_capacity (ide_drive_t *drive, unsigned *capacity,
+ struct atapi_request_sense *reqbuf)
+{
+ struct {
+ unsigned lba;
+ unsigned blocklen;
+ } capbuf;
+
+ int stat;
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.c[0] = READ_CAPACITY;
+ pc.buffer = (char *)&capbuf;
+ pc.buflen = sizeof (capbuf);
+
+ stat = cdrom_queue_packet_command (drive, &pc);
+ if (stat == 0)
+ {
+ *capacity = ntohl (capbuf.lba);
+ }
+
+ return stat;
+}
+
+
+static int
+cdrom_read_tocentry (ide_drive_t *drive, int trackno, int msf_flag,
+ int format, char *buf, int buflen,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.buffer = buf;
+ pc.buflen = buflen;
+ pc.c[0] = SCMD_READ_TOC;
+ pc.c[6] = trackno;
+ pc.c[7] = (buflen >> 8);
+ pc.c[8] = (buflen & 0xff);
+ pc.c[9] = (format << 6);
+ if (msf_flag) pc.c[1] = 2;
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+/* Try to read the entire TOC for the disk into our internal buffer. */
+static int
+cdrom_read_toc (ide_drive_t *drive,
+ struct atapi_request_sense *reqbuf)
+{
+ int msf_flag;
+ int stat, ntracks, i;
+ struct atapi_toc *toc = drive->cdrom_info.toc;
+ struct {
+ struct atapi_toc_header hdr;
+ struct atapi_toc_entry ent;
+ } ms_tmp;
+
+ if (toc == NULL)
+ {
+ /* Try to allocate space. */
+ toc = (struct atapi_toc *) kmalloc (sizeof (struct atapi_toc),
+ GFP_KERNEL);
+ drive->cdrom_info.toc = toc;
+ }
+
+ if (toc == NULL)
+ {
+ printk ("%s: No cdrom TOC buffer!\n", drive->name);
+ return -EIO;
+ }
+
+ /* Check to see if the existing data is still valid.
+ If it is, just return. */
+ if (CDROM_STATE_FLAGS (drive)->toc_valid)
+ (void) cdrom_check_status (drive, NULL);
+
+ if (CDROM_STATE_FLAGS (drive)->toc_valid) return 0;
+
+#if STANDARD_ATAPI
+ msf_flag = 0;
+#else /* not STANDARD_ATAPI */
+ /* Some drives can't return TOC data in LBA format. */
+ msf_flag = (CDROM_CONFIG_FLAGS (drive)->no_lba_toc);
+#endif /* not STANDARD_ATAPI */
+
+ /* First read just the header, so we know how long the TOC is. */
+ stat = cdrom_read_tocentry (drive, 0, msf_flag, 0, (char *)&toc->hdr,
+ sizeof (struct atapi_toc_header) +
+ sizeof (struct atapi_toc_entry),
+ reqbuf);
+ if (stat) return stat;
+
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->vertos_lossage)
+ {
+ toc->hdr.first_track = bcd2bin (toc->hdr.first_track);
+ toc->hdr.last_track = bcd2bin (toc->hdr.last_track);
+ /* hopefully the length is not BCD, too ;-| */
+ }
+#endif /* not STANDARD_ATAPI */
+
+ ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
+ if (ntracks <= 0) return -EIO;
+ if (ntracks > MAX_TRACKS) ntracks = MAX_TRACKS;
+
+ /* Now read the whole schmeer. */
+ stat = cdrom_read_tocentry (drive, 0, msf_flag, 0, (char *)&toc->hdr,
+ sizeof (struct atapi_toc_header) +
+ (ntracks+1) * sizeof (struct atapi_toc_entry),
+ reqbuf);
+ if (stat) return stat;
+ toc->hdr.toc_length = ntohs (toc->hdr.toc_length);
+
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->vertos_lossage)
+ {
+ toc->hdr.first_track = bcd2bin (toc->hdr.first_track);
+ toc->hdr.last_track = bcd2bin (toc->hdr.last_track);
+ /* hopefully the length is not BCD, too ;-| */
+ }
+#endif /* not STANDARD_ATAPI */
+
+ for (i=0; i<=ntracks; i++)
+ {
+#if ! STANDARD_ATAPI
+ if (msf_flag)
+ {
+ if (CDROM_CONFIG_FLAGS (drive)->vertos_lossage)
+ {
+ toc->ent[i].track = bcd2bin (toc->ent[i].track);
+ toc->ent[i].addr.msf.m = bcd2bin (toc->ent[i].addr.msf.m);
+ toc->ent[i].addr.msf.s = bcd2bin (toc->ent[i].addr.msf.s);
+ toc->ent[i].addr.msf.f = bcd2bin (toc->ent[i].addr.msf.f);
+ }
+ toc->ent[i].addr.lba = msf_to_lba (toc->ent[i].addr.msf.m,
+ toc->ent[i].addr.msf.s,
+ toc->ent[i].addr.msf.f);
+ }
+ else
+#endif /* not STANDARD_ATAPI */
+ toc->ent[i].addr.lba = ntohl (toc->ent[i].addr.lba);
+ }
+
+ /* Read the multisession information. */
+ stat = cdrom_read_tocentry (drive, 0, msf_flag, 1,
+ (char *)&ms_tmp, sizeof (ms_tmp),
+ reqbuf);
+ if (stat) return stat;
+#if ! STANDARD_ATAPI
+ if (msf_flag)
+ toc->last_session_lba = msf_to_lba (ms_tmp.ent.addr.msf.m,
+ ms_tmp.ent.addr.msf.s,
+ ms_tmp.ent.addr.msf.f);
+ else
+#endif /* not STANDARD_ATAPI */
+ toc->last_session_lba = ntohl (ms_tmp.ent.addr.lba);
+
+ toc->xa_flag = (ms_tmp.hdr.first_track != ms_tmp.hdr.last_track);
+
+ /* Now try to get the total cdrom capacity. */
+ stat = cdrom_read_capacity (drive, &toc->capacity, reqbuf);
+ if (stat) toc->capacity = 0x1fffff;
+
+ HWIF(drive)->gd->sizes[drive->select.b.unit << PARTN_BITS]
+ = toc->capacity * SECTORS_PER_FRAME;
+ drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME;
+
+ /* Remember that we've read this stuff. */
+ CDROM_STATE_FLAGS (drive)->toc_valid = 1;
+
+ return 0;
+}
+
+
+static int
+cdrom_read_subchannel (ide_drive_t *drive,
+ char *buf, int buflen,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.buffer = buf;
+ pc.buflen = buflen;
+ pc.c[0] = SCMD_READ_SUBCHANNEL;
+ pc.c[2] = 0x40; /* request subQ data */
+ pc.c[3] = 0x01; /* Format 1: current position */
+ pc.c[7] = (buflen >> 8);
+ pc.c[8] = (buflen & 0xff);
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+/* modeflag: 0 = current, 1 = changeable mask, 2 = default, 3 = saved */
+static int
+cdrom_mode_sense (ide_drive_t *drive, int pageno, int modeflag,
+ char *buf, int buflen,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.buffer = buf;
+ pc.buflen = buflen;
+ pc.c[0] = MODE_SENSE_10;
+ pc.c[2] = pageno | (modeflag << 6);
+ pc.c[7] = (buflen >> 8);
+ pc.c[8] = (buflen & 0xff);
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+static int
+cdrom_mode_select (ide_drive_t *drive, int pageno, char *buf, int buflen,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.buffer = buf;
+ pc.buflen = - buflen;
+ pc.c[0] = MODE_SELECT_10;
+ pc.c[1] = 0x10;
+ pc.c[2] = pageno;
+ pc.c[7] = (buflen >> 8);
+ pc.c[8] = (buflen & 0xff);
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+static int
+cdrom_play_lba_range_play12 (ide_drive_t *drive, int lba_start, int lba_end,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.c[0] = SCMD_PLAYAUDIO12;
+#ifdef __alpha__
+ stq_u(((long) htonl (lba_end - lba_start) << 32) | htonl(lba_start),
+ (unsigned long *) &pc.c[2]);
+#else
+ *(int *)(&pc.c[2]) = htonl (lba_start);
+ *(int *)(&pc.c[6]) = htonl (lba_end - lba_start);
+#endif
+
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+#if ! STANDARD_ATAPI
+static int
+cdrom_play_lba_range_msf (ide_drive_t *drive, int lba_start, int lba_end,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.c[0] = SCMD_PLAYAUDIO_MSF;
+ lba_to_msf (lba_start, &pc.c[3], &pc.c[4], &pc.c[5]);
+ lba_to_msf (lba_end-1, &pc.c[6], &pc.c[7], &pc.c[8]);
+
+ if (CDROM_CONFIG_FLAGS (drive)->playmsf_uses_bcd)
+ {
+ pc.c[3] = bin2bcd (pc.c[3]);
+ pc.c[4] = bin2bcd (pc.c[4]);
+ pc.c[5] = bin2bcd (pc.c[5]);
+ pc.c[6] = bin2bcd (pc.c[6]);
+ pc.c[7] = bin2bcd (pc.c[7]);
+ pc.c[8] = bin2bcd (pc.c[8]);
+ }
+
+ return cdrom_queue_packet_command (drive, &pc);
+}
+#endif /* not STANDARD_ATAPI */
+
+
+static int
+cdrom_play_lba_range_1 (ide_drive_t *drive, int lba_start, int lba_end,
+ struct atapi_request_sense *reqbuf)
+{
+ /* This is rather annoying.
+ My NEC-260 won't recognize group 5 commands such as PLAYAUDIO12;
+ the only way to get it to play more than 64k of blocks at once
+ seems to be the PLAYAUDIO_MSF command. However, the parameters
+ the NEC 260 wants for the PLAYMSF command are incompatible with
+ the new version of the spec.
+
+ So what i'll try is this. First try for PLAYAUDIO12. If it works,
+ great. Otherwise, if the drive reports an illegal command code,
+ try PLAYAUDIO_MSF using the NEC 260-style bcd parameters. */
+
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->no_playaudio12)
+ return cdrom_play_lba_range_msf (drive, lba_start, lba_end, reqbuf);
+ else
+#endif /* not STANDARD_ATAPI */
+ {
+ int stat;
+ struct atapi_request_sense my_reqbuf;
+
+ if (reqbuf == NULL)
+ reqbuf = &my_reqbuf;
+
+ stat = cdrom_play_lba_range_play12 (drive, lba_start, lba_end, reqbuf);
+ if (stat == 0) return 0;
+
+#if ! STANDARD_ATAPI
+ /* It failed. Try to find out why. */
+ if (reqbuf->sense_key == ILLEGAL_REQUEST && reqbuf->asc == 0x20)
+ {
+ /* The drive didn't recognize the command.
+ Retry with the MSF variant. */
+ printk ("%s: Drive does not support PLAYAUDIO12; "
+ "trying PLAYAUDIO_MSF\n", drive->name);
+ CDROM_CONFIG_FLAGS (drive)->no_playaudio12 = 1;
+ CDROM_CONFIG_FLAGS (drive)->playmsf_uses_bcd = 1;
+ return cdrom_play_lba_range_msf (drive, lba_start, lba_end, reqbuf);
+ }
+#endif /* not STANDARD_ATAPI */
+
+ /* Failed for some other reason. Give up. */
+ return stat;
+ }
+}
+
+
+/* Play audio starting at LBA LBA_START and finishing with the
+ LBA before LBA_END. */
+static int
+cdrom_play_lba_range (ide_drive_t *drive, int lba_start, int lba_end,
+ struct atapi_request_sense *reqbuf)
+{
+ int i, stat;
+ struct atapi_request_sense my_reqbuf;
+
+ if (reqbuf == NULL)
+ reqbuf = &my_reqbuf;
+
+ /* Some drives, will, for certain audio cds,
+ give an error if you ask them to play the entire cd using the
+ values which are returned in the TOC. The play will succeed, however,
+ if the ending address is adjusted downwards by a few frames. */
+ for (i=0; i<75; i++)
+ {
+ stat = cdrom_play_lba_range_1 (drive, lba_start, lba_end, reqbuf);
+
+ if (stat == 0 ||
+ !(reqbuf->sense_key == ILLEGAL_REQUEST && reqbuf->asc == 0x24))
+ return stat;
+
+ --lba_end;
+ if (lba_end <= lba_start) break;
+ }
+
+ return stat;
+}
+
+
+static
+int cdrom_get_toc_entry (ide_drive_t *drive, int track,
+ struct atapi_toc_entry **ent,
+ struct atapi_request_sense *reqbuf)
+{
+ int stat, ntracks;
+ struct atapi_toc *toc;
+
+ /* Make sure our saved TOC is valid. */
+ stat = cdrom_read_toc (drive, reqbuf);
+ if (stat) return stat;
+
+ toc = drive->cdrom_info.toc;
+
+ /* Check validity of requested track number. */
+ ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
+ if (track == CDROM_LEADOUT)
+ *ent = &toc->ent[ntracks];
+ else if (track < toc->hdr.first_track ||
+ track > toc->hdr.last_track)
+ return -EINVAL;
+ else
+ *ent = &toc->ent[track - toc->hdr.first_track];
+
+ return 0;
+}
+
+
+static int
+cdrom_read_block (ide_drive_t *drive, int format, int lba,
+ char *buf, int buflen,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+ struct atapi_request_sense my_reqbuf;
+ int stat;
+
+ if (reqbuf == NULL)
+ reqbuf = &my_reqbuf;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.buffer = buf;
+ pc.buflen = buflen;
+
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->old_readcd)
+ pc.c[0] = 0xd4;
+ else
+#endif /* not STANDARD_ATAPI */
+ pc.c[0] = READ_CD;
+
+ pc.c[1] = (format << 2);
+#ifdef __alpha__
+ stl_u(htonl (lba), (unsigned int *) &pc.c[2]);
+#else
+ *(int *)(&pc.c[2]) = htonl (lba);
+#endif
+ pc.c[8] = 1; /* one block */
+ pc.c[9] = 0x10;
+
+ stat = cdrom_queue_packet_command (drive, &pc);
+
+#if ! STANDARD_ATAPI
+ /* If the drive doesn't recognize the READ CD opcode, retry the command
+ with an older opcode for that command. */
+ if (stat && reqbuf->sense_key == ILLEGAL_REQUEST && reqbuf->asc == 0x20 &&
+ CDROM_CONFIG_FLAGS (drive)->old_readcd == 0)
+ {
+ printk ("%s: Drive does not recognize READ_CD; trying opcode 0xd4\n",
+ drive->name);
+ CDROM_CONFIG_FLAGS (drive)->old_readcd = 1;
+ return cdrom_read_block (drive, format, lba, buf, buflen, reqbuf);
+ }
+#endif /* not STANDARD_ATAPI */
+
+ return stat;
+}
+
+
+int ide_cdrom_ioctl (ide_drive_t *drive, struct inode *inode,
+ struct file *file, unsigned int cmd, unsigned long arg)
+{
+ switch (cmd)
+ {
+ case CDROMEJECT:
+ {
+ int stat;
+
+ if (drive->usage > 1)
+ return -EBUSY;
+
+ stat = cdrom_lockdoor (drive, 0, NULL);
+ if (stat) return stat;
+
+ return cdrom_eject (drive, 0, NULL);
+ }
+
+ case CDROMEJECT_SW:
+ {
+ CDROM_STATE_FLAGS (drive)->eject_on_close = arg;
+ return 0;
+ }
+
+ case CDROMPAUSE:
+ return cdrom_pause (drive, 1, NULL);
+
+ case CDROMRESUME:
+ return cdrom_pause (drive, 0, NULL);
+
+ case CDROMSTART:
+ return cdrom_startstop (drive, 1, NULL);
+
+ case CDROMSTOP:
+ {
+ int stat;
+
+ stat = cdrom_startstop (drive, 0, NULL);
+ if (stat) return stat;
+ /* pit says the Dolphin needs this. */
+ return cdrom_eject (drive, 1, NULL);
+ }
+
+ case CDROMPLAYMSF:
+ {
+ struct cdrom_msf msf;
+ int stat, lba_start, lba_end;
+
+ stat = verify_area (VERIFY_READ, (void *)arg, sizeof (msf));
+ if (stat) return stat;
+
+ memcpy_fromfs (&msf, (void *) arg, sizeof(msf));
+
+ lba_start = msf_to_lba (msf.cdmsf_min0, msf.cdmsf_sec0,
+ msf.cdmsf_frame0);
+ lba_end = msf_to_lba (msf.cdmsf_min1, msf.cdmsf_sec1,
+ msf.cdmsf_frame1) + 1;
+
+ if (lba_end <= lba_start) return -EINVAL;
+
+ return cdrom_play_lba_range (drive, lba_start, lba_end, NULL);
+ }
+
+ /* Like just about every other Linux cdrom driver, we ignore the
+ index part of the request here. */
+ case CDROMPLAYTRKIND:
+ {
+ int stat, lba_start, lba_end;
+ struct cdrom_ti ti;
+ struct atapi_toc_entry *first_toc, *last_toc;
+
+ stat = verify_area (VERIFY_READ, (void *)arg, sizeof (ti));
+ if (stat) return stat;
+
+ memcpy_fromfs (&ti, (void *) arg, sizeof(ti));
+
+ stat = cdrom_get_toc_entry (drive, ti.cdti_trk0, &first_toc, NULL);
+ if (stat) return stat;
+ stat = cdrom_get_toc_entry (drive, ti.cdti_trk1, &last_toc, NULL);
+ if (stat) return stat;
+
+ if (ti.cdti_trk1 != CDROM_LEADOUT) ++last_toc;
+ lba_start = first_toc->addr.lba;
+ lba_end = last_toc->addr.lba;
+
+ if (lba_end <= lba_start) return -EINVAL;
+
+ return cdrom_play_lba_range (drive, lba_start, lba_end, NULL);
+ }
+
+ case CDROMREADTOCHDR:
+ {
+ int stat;
+ struct cdrom_tochdr tochdr;
+ struct atapi_toc *toc;
+
+ stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (tochdr));
+ if (stat) return stat;
+
+ /* Make sure our saved TOC is valid. */
+ stat = cdrom_read_toc (drive, NULL);
+ if (stat) return stat;
+
+ toc = drive->cdrom_info.toc;
+ tochdr.cdth_trk0 = toc->hdr.first_track;
+ tochdr.cdth_trk1 = toc->hdr.last_track;
+
+ memcpy_tofs ((void *) arg, &tochdr, sizeof (tochdr));
+
+ return stat;
+ }
+
+ case CDROMREADTOCENTRY:
+ {
+ int stat;
+ struct cdrom_tocentry tocentry;
+ struct atapi_toc_entry *toce;
+
+ stat = verify_area (VERIFY_READ, (void *) arg, sizeof (tocentry));
+ if (stat) return stat;
+ stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (tocentry));
+ if (stat) return stat;
+
+ memcpy_fromfs (&tocentry, (void *) arg, sizeof (tocentry));
+
+ stat = cdrom_get_toc_entry (drive, tocentry.cdte_track, &toce, NULL);
+ if (stat) return stat;
+
+ tocentry.cdte_ctrl = toce->control;
+ tocentry.cdte_adr = toce->adr;
+
+ if (tocentry.cdte_format == CDROM_MSF)
+ {
+ /* convert to MSF */
+ lba_to_msf (toce->addr.lba,
+ &tocentry.cdte_addr.msf.minute,
+ &tocentry.cdte_addr.msf.second,
+ &tocentry.cdte_addr.msf.frame);
+ }
+ else
+ tocentry.cdte_addr.lba = toce->addr.lba;
+
+ memcpy_tofs ((void *) arg, &tocentry, sizeof (tocentry));
+
+ return stat;
+ }
+
+ case CDROMSUBCHNL:
+ {
+ struct atapi_cdrom_subchnl scbuf;
+ int stat, abs_lba, rel_lba;
+ struct cdrom_subchnl subchnl;
+
+ stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (subchnl));
+ if (stat) return stat;
+ stat = verify_area (VERIFY_READ, (void *) arg, sizeof (subchnl));
+ if (stat) return stat;
+
+ memcpy_fromfs (&subchnl, (void *) arg, sizeof (subchnl));
+
+ stat = cdrom_read_subchannel (drive, (char *)&scbuf, sizeof (scbuf),
+ NULL);
+ if (stat) return stat;
+
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->vertos_lossage)
+ {
+ abs_lba = msf_to_lba (bcd2bin (scbuf.acdsc_absaddr.msf.minute),
+ bcd2bin (scbuf.acdsc_absaddr.msf.second),
+ bcd2bin (scbuf.acdsc_absaddr.msf.frame));
+ rel_lba = msf_to_lba (bcd2bin (scbuf.acdsc_reladdr.msf.minute),
+ bcd2bin (scbuf.acdsc_reladdr.msf.second),
+ bcd2bin (scbuf.acdsc_reladdr.msf.frame));
+ scbuf.acdsc_trk = bcd2bin (scbuf.acdsc_trk);
+ }
+ else
+#endif /* not STANDARD_ATAPI */
+ {
+ abs_lba = ntohl (scbuf.acdsc_absaddr.lba);
+ rel_lba = ntohl (scbuf.acdsc_reladdr.lba);
+ }
+
+ if (subchnl.cdsc_format == CDROM_MSF)
+ {
+ lba_to_msf (abs_lba,
+ &subchnl.cdsc_absaddr.msf.minute,
+ &subchnl.cdsc_absaddr.msf.second,
+ &subchnl.cdsc_absaddr.msf.frame);
+ lba_to_msf (rel_lba,
+ &subchnl.cdsc_reladdr.msf.minute,
+ &subchnl.cdsc_reladdr.msf.second,
+ &subchnl.cdsc_reladdr.msf.frame);
+ }
+ else
+ {
+ subchnl.cdsc_absaddr.lba = abs_lba;
+ subchnl.cdsc_reladdr.lba = rel_lba;
+ }
+
+ subchnl.cdsc_audiostatus = scbuf.acdsc_audiostatus;
+ subchnl.cdsc_ctrl = scbuf.acdsc_ctrl;
+ subchnl.cdsc_trk = scbuf.acdsc_trk;
+ subchnl.cdsc_ind = scbuf.acdsc_ind;
+
+ memcpy_tofs ((void *) arg, &subchnl, sizeof (subchnl));
+
+ return stat;
+ }
+
+ case CDROMVOLCTRL:
+ {
+ struct cdrom_volctrl volctrl;
+ char buffer[24], mask[24];
+ int stat;
+
+ stat = verify_area (VERIFY_READ, (void *) arg, sizeof (volctrl));
+ if (stat) return stat;
+ memcpy_fromfs (&volctrl, (void *) arg, sizeof (volctrl));
+
+ stat = cdrom_mode_sense (drive, 0x0e, 0, buffer, sizeof (buffer),NULL);
+ if (stat) return stat;
+ stat = cdrom_mode_sense (drive, 0x0e, 1, mask , sizeof (buffer),NULL);
+ if (stat) return stat;
+
+ buffer[1] = buffer[2] = 0;
+
+ buffer[17] = volctrl.channel0 & mask[17];
+ buffer[19] = volctrl.channel1 & mask[19];
+ buffer[21] = volctrl.channel2 & mask[21];
+ buffer[23] = volctrl.channel3 & mask[23];
+
+ return cdrom_mode_select (drive, 0x0e, buffer, sizeof (buffer), NULL);
+ }
+
+ case CDROMVOLREAD:
+ {
+ struct cdrom_volctrl volctrl;
+ char buffer[24];
+ int stat;
+
+ stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (volctrl));
+ if (stat) return stat;
+
+ stat = cdrom_mode_sense (drive, 0x0e, 0, buffer, sizeof (buffer), NULL);
+ if (stat) return stat;
+
+ volctrl.channel0 = buffer[17];
+ volctrl.channel1 = buffer[19];
+ volctrl.channel2 = buffer[21];
+ volctrl.channel3 = buffer[23];
+
+ memcpy_tofs ((void *) arg, &volctrl, sizeof (volctrl));
+
+ return 0;
+ }
+
+ case CDROMMULTISESSION:
+ {
+ struct cdrom_multisession ms_info;
+ struct atapi_toc *toc;
+ int stat;
+
+ stat = verify_area (VERIFY_READ, (void *)arg, sizeof (ms_info));
+ if (stat) return stat;
+ stat = verify_area (VERIFY_WRITE, (void *)arg, sizeof (ms_info));
+ if (stat) return stat;
+
+ memcpy_fromfs (&ms_info, (void *)arg, sizeof (ms_info));
+
+ /* Make sure the TOC information is valid. */
+ stat = cdrom_read_toc (drive, NULL);
+ if (stat) return stat;
+
+ toc = drive->cdrom_info.toc;
+
+ if (ms_info.addr_format == CDROM_MSF)
+ lba_to_msf (toc->last_session_lba,
+ &ms_info.addr.msf.minute,
+ &ms_info.addr.msf.second,
+ &ms_info.addr.msf.frame);
+
+ else if (ms_info.addr_format == CDROM_LBA)
+ ms_info.addr.lba = toc->last_session_lba;
+
+ else
+ return -EINVAL;
+
+ ms_info.xa_flag = toc->xa_flag;
+
+ memcpy_tofs ((void *)arg, &ms_info, sizeof (ms_info));
+
+ return 0;
+ }
+
+ /* Read 2352 byte blocks from audio tracks. */
+ case CDROMREADAUDIO:
+ {
+ int stat, lba;
+ struct atapi_toc *toc;
+ struct cdrom_read_audio ra;
+ char buf[CD_FRAMESIZE_RAW];
+
+ /* Make sure the TOC is up to date. */
+ stat = cdrom_read_toc (drive, NULL);
+ if (stat) return stat;
+
+ toc = drive->cdrom_info.toc;
+
+ stat = verify_area (VERIFY_READ, (char *)arg, sizeof (ra));
+ if (stat) return stat;
+
+ memcpy_fromfs (&ra, (void *)arg, sizeof (ra));
+
+ if (ra.nframes < 0 || ra.nframes > toc->capacity)
+ return -EINVAL;
+ else if (ra.nframes == 0)
+ return 0;
+
+ stat = verify_area (VERIFY_WRITE, (char *)ra.buf,
+ ra.nframes * CD_FRAMESIZE_RAW);
+ if (stat) return stat;
+
+ if (ra.addr_format == CDROM_MSF)
+ lba = msf_to_lba (ra.addr.msf.minute, ra.addr.msf.second,
+ ra.addr.msf.frame);
+
+ else if (ra.addr_format == CDROM_LBA)
+ lba = ra.addr.lba;
+
+ else
+ return -EINVAL;
+
+ if (lba < 0 || lba >= toc->capacity)
+ return -EINVAL;
+
+ while (ra.nframes > 0)
+ {
+ stat = cdrom_read_block (drive, 1, lba, buf,
+ CD_FRAMESIZE_RAW, NULL);
+ if (stat) return stat;
+ memcpy_tofs (ra.buf, buf, CD_FRAMESIZE_RAW);
+ ra.buf += CD_FRAMESIZE_RAW;
+ --ra.nframes;
+ ++lba;
+ }
+
+ return 0;
+ }
+
+ case CDROMREADMODE1:
+ case CDROMREADMODE2:
+ {
+ struct cdrom_msf msf;
+ int blocksize, format, stat, lba;
+ struct atapi_toc *toc;
+ char buf[CD_FRAMESIZE_RAW0];
+
+ if (cmd == CDROMREADMODE1)
+ {
+ blocksize = CD_FRAMESIZE;
+ format = 2;
+ }
+ else
+ {
+ blocksize = CD_FRAMESIZE_RAW0;
+ format = 3;
+ }
+
+ stat = verify_area (VERIFY_READ, (char *)arg, sizeof (msf));
+ if (stat) return stat;
+ stat = verify_area (VERIFY_WRITE, (char *)arg, blocksize);
+ if (stat) return stat;
+
+ memcpy_fromfs (&msf, (void *)arg, sizeof (msf));
+
+ lba = msf_to_lba (msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0);
+
+ /* Make sure the TOC is up to date. */
+ stat = cdrom_read_toc (drive, NULL);
+ if (stat) return stat;
+
+ toc = drive->cdrom_info.toc;
+
+ if (lba < 0 || lba >= toc->capacity)
+ return -EINVAL;
+
+ stat = cdrom_read_block (drive, format, lba, buf, blocksize, NULL);
+ if (stat) return stat;
+
+ memcpy_tofs ((char *)arg, buf, blocksize);
+ return 0;
+ }
+
+#if 0 /* Doesn't work reliably yet. */
+ case CDROMRESET:
+ {
+ struct request req;
+ ide_init_drive_cmd (&req);
+ req.cmd = RESET_DRIVE_COMMAND;
+ return ide_do_drive_cmd (drive, &req, ide_wait);
+ }
+#endif
+
+
+#ifdef TEST
+ case 0x1234:
+ {
+ int stat;
+ struct packet_command pc;
+ int len, lena;
+
+ memset (&pc, 0, sizeof (pc));
+
+ stat = verify_area (VERIFY_READ, (void *) arg, sizeof (pc.c));
+ if (stat) return stat;
+ memcpy_fromfs (&pc.c, (void *) arg, sizeof (pc.c));
+ arg += sizeof (pc.c);
+
+ stat = verify_area (VERIFY_READ, (void *) arg, sizeof (len));
+ if (stat) return stat;
+ memcpy_fromfs (&len, (void *) arg , sizeof (len));
+ arg += sizeof (len);
+
+ if (len > 0) {
+ stat = verify_area (VERIFY_WRITE, (void *) arg, len);
+ if (stat) return stat;
+ }
+
+ lena = len;
+ if (lena < 0) lena = 0;
+
+ {
+ char buf[lena];
+ if (len > 0) {
+ pc.buflen = len;
+ pc.buffer = buf;
+ }
+
+ stat = cdrom_queue_packet_command (drive, &pc);
+
+ if (len > 0)
+ memcpy_tofs ((void *)arg, buf, len);
+ }
+
+ return stat;
+ }
+#endif
+
+ default:
+ return -EPERM;
+ }
+
+}
+
+
+
+/****************************************************************************
+ * Other driver requests (open, close, check media change).
+ */
+
+int ide_cdrom_check_media_change (ide_drive_t *drive)
+{
+ int retval;
+
+ (void) cdrom_check_status (drive, NULL);
+
+ retval = CDROM_STATE_FLAGS (drive)->media_changed;
+ CDROM_STATE_FLAGS (drive)->media_changed = 0;
+
+ return retval;
+}
+
+
+int ide_cdrom_open (struct inode *ip, struct file *fp, ide_drive_t *drive)
+{
+ /* no write access */
+ if (fp->f_mode & 2)
+ {
+ --drive->usage;
+ return -EROFS;
+ }
+
+ /* If this is the first open, check the drive status. */
+ if (drive->usage == 1)
+ {
+ int stat;
+ struct atapi_request_sense my_reqbuf;
+ my_reqbuf.sense_key = 0;
+
+ /* Get the drive status. */
+ stat = cdrom_check_status (drive, &my_reqbuf);
+
+ /* If the tray is open, try to close it. */
+ if (stat && my_reqbuf.sense_key == NOT_READY)
+ {
+ cdrom_eject (drive, 1, &my_reqbuf);
+ stat = cdrom_check_status (drive, &my_reqbuf);
+ }
+
+ /* Return an error if there are still problems. */
+ if (stat && my_reqbuf.sense_key != UNIT_ATTENTION)
+ {
+ --drive->usage;
+ return -ENXIO;
+ }
+
+ /* Now lock the door. */
+ (void) cdrom_lockdoor (drive, 1, &my_reqbuf);
+
+ /* And try to read the TOC information now. */
+ (void) cdrom_read_toc (drive, &my_reqbuf);
+ }
+
+ return 0;
+}
+
+
+/*
+ * Close down the device. Invalidate all cached blocks.
+ */
+
+void ide_cdrom_release (struct inode *inode, struct file *file, ide_drive_t *drive)
+{
+ if (drive->usage == 0)
+ {
+ invalidate_buffers (inode->i_rdev);
+
+ /* Unlock the door. */
+ (void) cdrom_lockdoor (drive, 0, NULL);
+
+ /* Do an eject if we were requested to do so. */
+ if (CDROM_STATE_FLAGS (drive)->eject_on_close)
+ (void) cdrom_eject (drive, 0, NULL);
+ }
+}
+
+
+
+/****************************************************************************
+ * Device initialization.
+ */
+
+void ide_cdrom_setup (ide_drive_t *drive)
+{
+ blksize_size[HWIF(drive)->major][drive->select.b.unit << PARTN_BITS] = CD_FRAMESIZE;
+
+ drive->special.all = 0;
+ drive->ready_stat = 0;
+
+ CDROM_STATE_FLAGS (drive)->media_changed = 0;
+ CDROM_STATE_FLAGS (drive)->toc_valid = 0;
+ CDROM_STATE_FLAGS (drive)->door_locked = 0;
+
+ /* Turn this off by default, since many people don't like it. */
+ CDROM_STATE_FLAGS (drive)->eject_on_close= 0;
+
+#if NO_DOOR_LOCKING
+ CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+#else
+ CDROM_CONFIG_FLAGS (drive)->no_doorlock = 0;
+#endif
+
+ if (drive->id != NULL) {
+ CDROM_CONFIG_FLAGS (drive)->drq_interrupt =
+ ((drive->id->config & 0x0060) == 0x20);
+ } else {
+ CDROM_CONFIG_FLAGS (drive)->drq_interrupt = 0;
+ }
+
+#if ! STANDARD_ATAPI
+ CDROM_CONFIG_FLAGS (drive)->no_playaudio12 = 0;
+ CDROM_CONFIG_FLAGS (drive)->old_readcd = 0;
+ CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 0;
+ CDROM_CONFIG_FLAGS (drive)->playmsf_uses_bcd = 0;
+ CDROM_CONFIG_FLAGS (drive)->vertos_lossage = 0;
+
+ if (drive->id != NULL) {
+ /* Accommodate some broken drives... */
+ if (strcmp (drive->id->model, "CD220E") == 0 ||
+ strcmp (drive->id->model, "CD") == 0) /* Creative Labs */
+ CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
+
+ else if (strcmp (drive->id->model, "TO-ICSLYAL") == 0 || /* Acer CD525E */
+ strcmp (drive->id->model, "OTI-SCYLLA") == 0)
+ CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
+
+ /* I don't know who makes this.
+ Francesco Messineo <sidera@ccii.unipi.it> says this one's broken too. */
+ else if (strcmp (drive->id->model, "DCI-2S10") == 0)
+ CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
+
+ else if (strcmp (drive->id->model, "CDA26803I SE") == 0) /* Aztech */
+ {
+ CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
+
+ /* This drive _also_ does not implement PLAYAUDIO12 correctly. */
+ CDROM_CONFIG_FLAGS (drive)->no_playaudio12 = 1;
+ }
+
+ /* Vertos 300.
+ There seem to be at least two different, incompatible versions
+ of this drive floating around. Luckily, they appear to return their
+ id strings with different byte orderings. */
+ else if (strcmp (drive->id->model, "V003S0DS") == 0)
+ {
+ CDROM_CONFIG_FLAGS (drive)->vertos_lossage = 1;
+ CDROM_CONFIG_FLAGS (drive)->playmsf_uses_bcd = 1;
+ CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
+ }
+ else if (strcmp (drive->id->model, "0V300SSD") == 0 ||
+ strcmp (drive->id->model, "V003M0DP") == 0)
+ CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
+
+ /* Vertos 400. */
+ else if (strcmp (drive->id->model, "V004E0DT") == 0 ||
+ strcmp (drive->id->model, "0V400ETD") == 0)
+ CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
+
+ else if ( strcmp (drive->id->model, "CD-ROM CDU55D") == 0) /*sony cdu55d */
+ CDROM_CONFIG_FLAGS (drive)->no_playaudio12 = 1;
+
+ else if (strcmp (drive->id->model, "CD-ROM CDU55E") == 0)
+ CDROM_CONFIG_FLAGS (drive)->no_playaudio12 = 1;
+ } /* drive-id != NULL */
+#endif /* not STANDARD_ATAPI */
+
+ drive->cdrom_info.toc = NULL;
+ drive->cdrom_info.sector_buffer = NULL;
+ drive->cdrom_info.sector_buffered = 0;
+ drive->cdrom_info.nsectors_buffered = 0;
+}
+
+
+
+/*
+ * TODO:
+ * CDROM_GET_UPC
+ * CDROMRESET
+ * Lock the door when a read request completes successfully and the
+ * door is not already locked. Also try to reorganize to reduce
+ * duplicated functionality between read and ioctl paths?
+ * Establish interfaces for an IDE port driver, and break out the cdrom
+ * code into a loadable module.
+ * Support changers.
+ * Write some real documentation.
+ */
diff --git a/i386/i386at/gpl/linux/block/ide.c b/i386/i386at/gpl/linux/block/ide.c
new file mode 100644
index 0000000..4b7c5e9
--- /dev/null
+++ b/i386/i386at/gpl/linux/block/ide.c
@@ -0,0 +1,3087 @@
+/*
+ * linux/drivers/block/ide.c Version 5.28 Feb 11, 1996
+ *
+ * Copyright (C) 1994-1996 Linus Torvalds & authors (see below)
+ */
+#define _IDE_C /* needed by <linux/blk.h> */
+
+/*
+ * This is the multiple IDE interface driver, as evolved from hd.c.
+ * It supports up to four IDE interfaces, on one or more IRQs (usually 14 & 15).
+ * There can be up to two drives per interface, as per the ATA-2 spec.
+ *
+ * Primary i/f: ide0: major=3; (hda) minor=0; (hdb) minor=64
+ * Secondary i/f: ide1: major=22; (hdc or hd1a) minor=0; (hdd or hd1b) minor=64
+ * Tertiary i/f: ide2: major=33; (hde) minor=0; (hdf) minor=64
+ * Quaternary i/f: ide3: major=34; (hdg) minor=0; (hdh) minor=64
+ *
+ * It is easy to extend ide.c to handle more than four interfaces:
+ *
+ * Change the MAX_HWIFS constant in ide.h.
+ *
+ * Define some new major numbers (in major.h), and insert them into
+ * the ide_hwif_to_major table in ide.c.
+ *
+ * Fill in the extra values for the new interfaces into the two tables
+ * inside ide.c: default_io_base[] and default_irqs[].
+ *
+ * Create the new request handlers by cloning "do_ide3_request()"
+ * for each new interface, and add them to the switch statement
+ * in the ide_init() function in ide.c.
+ *
+ * Recompile, create the new /dev/ entries, and it will probably work.
+ *
+ * From hd.c:
+ * |
+ * | It traverses the request-list, using interrupts to jump between functions.
+ * | As nearly all functions can be called within interrupts, we may not sleep.
+ * | Special care is recommended. Have Fun!
+ * |
+ * | modified by Drew Eckhardt to check nr of hd's from the CMOS.
+ * |
+ * | Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
+ * | in the early extended-partition checks and added DM partitions.
+ * |
+ * | Early work on error handling by Mika Liljeberg (liljeber@cs.Helsinki.FI).
+ * |
+ * | IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
+ * | and general streamlining by Mark Lord (mlord@bnr.ca).
+ *
+ * October, 1994 -- Complete line-by-line overhaul for linux 1.1.x, by:
+ *
+ * Mark Lord (mlord@bnr.ca) (IDE Perf.Pkg)
+ * Delman Lee (delman@mipg.upenn.edu) ("Mr. atdisk2")
+ * Petri Mattila (ptjmatti@kruuna.helsinki.fi) (EIDE stuff)
+ * Scott Snyder (snyder@fnald0.fnal.gov) (ATAPI IDE cd-rom)
+ *
+ * Maintained by Mark Lord (mlord@bnr.ca): ide.c, ide.h, triton.c, hd.c, ..
+ *
+ * This was a rewrite of just about everything from hd.c, though some original
+ * code is still sprinkled about. Think of it as a major evolution, with
+ * inspiration from lots of linux users, esp. hamish@zot.apana.org.au
+ *
+ * Version 1.0 ALPHA initial code, primary i/f working okay
+ * Version 1.3 BETA dual i/f on shared irq tested & working!
+ * Version 1.4 BETA added auto probing for irq(s)
+ * Version 1.5 BETA added ALPHA (untested) support for IDE cd-roms,
+ * ...
+ * Version 3.5 correct the bios_cyl field if it's too small
+ * (linux 1.1.76) (to help fdisk with brain-dead BIOSs)
+ * Version 3.6 cosmetic corrections to comments and stuff
+ * (linux 1.1.77) reorganise probing code to make it understandable
+ * added halfway retry to probing for drive identification
+ * added "hdx=noprobe" command line option
+ * allow setting multmode even when identification fails
+ * Version 3.7 move set_geometry=1 from do_identify() to ide_init()
+ * increase DRQ_WAIT to eliminate nuisance messages
+ * wait for DRQ_STAT instead of DATA_READY during probing
+ * (courtesy of Gary Thomas gary@efland.UU.NET)
+ * Version 3.8 fixed byte-swapping for confused Mitsumi cdrom drives
+ * update of ide-cd.c from Scott, allows blocksize=1024
+ * cdrom probe fixes, inspired by jprang@uni-duisburg.de
+ * Version 3.9 don't use LBA if lba_capacity looks funny
+ * correct the drive capacity calculations
+ * fix probing for old Seagates without IDE_ALTSTATUS_REG
+ * fix byte-ordering for some NEC cdrom drives
+ * Version 3.10 disable multiple mode by default; was causing trouble
+ * Version 3.11 fix mis-identification of old WD disks as cdroms
+ * Version 3,12 simplify logic for selecting initial mult_count
+ * (fixes problems with buggy WD drives)
+ * Version 3.13 remove excess "multiple mode disabled" messages
+ * Version 3.14 fix ide_error() handling of BUSY_STAT
+ * fix byte-swapped cdrom strings (again.. arghh!)
+ * ignore INDEX bit when checking the ALTSTATUS reg
+ * Version 3.15 add SINGLE_THREADED flag for use with dual-CMD i/f
+ * ignore WRERR_STAT for non-write operations
+ * added vlb_sync support for DC-2000A & others,
+ * (incl. some Promise chips), courtesy of Frank Gockel
+ * Version 3.16 convert vlb_32bit and vlb_sync into runtime flags
+ * add ioctls to get/set VLB flags (HDIO_[SG]ET_CHIPSET)
+ * rename SINGLE_THREADED to SUPPORT_SERIALIZE,
+ * add boot flag to "serialize" operation for CMD i/f
+ * add optional support for DTC2278 interfaces,
+ * courtesy of andy@cercle.cts.com (Dyan Wile).
+ * add boot flag to enable "dtc2278" probe
+ * add probe to avoid EATA (SCSI) interfaces,
+ * courtesy of neuffer@goofy.zdv.uni-mainz.de.
+ * Version 4.00 tidy up verify_area() calls - heiko@colossus.escape.de
+ * add flag to ignore WRERR_STAT for some drives
+ * courtesy of David.H.West@um.cc.umich.edu
+ * assembly syntax tweak to vlb_sync
+ * removeable drive support from scuba@cs.tu-berlin.de
+ * add transparent support for DiskManager-6.0x "Dynamic
+ * Disk Overlay" (DDO), most of this is in genhd.c
+ * eliminate "multiple mode turned off" message at boot
+ * Version 4.10 fix bug in ioctl for "hdparm -c3"
+ * fix DM6:DDO support -- now works with LILO, fdisk, ...
+ * don't treat some naughty WD drives as removeable
+ * Version 4.11 updated DM6 support using info provided by OnTrack
+ * Version 5.00 major overhaul, multmode setting fixed, vlb_sync fixed
+ * added support for 3rd/4th/alternative IDE ports
+ * created ide.h; ide-cd.c now compiles separate from ide.c
+ * hopefully fixed infinite "unexpected_intr" from cdroms
+ * zillions of other changes and restructuring
+ * somehow reduced overall memory usage by several kB
+ * probably slowed things down slightly, but worth it
+ * Version 5.01 AT LAST!! Finally understood why "unexpected_intr"
+ * was happening at various times/places: whenever the
+ * ide-interface's ctl_port was used to "mask" the irq,
+ * it also would trigger an edge in the process of masking
+ * which would result in a self-inflicted interrupt!!
+ * (such a stupid way to build a hardware interrupt mask).
+ * This is now fixed (after a year of head-scratching).
+ * Version 5.02 got rid of need for {enable,disable}_irq_list()
+ * Version 5.03 tune-ups, comments, remove "busy wait" from drive resets
+ * removed PROBE_FOR_IRQS option -- no longer needed
+ * OOOPS! fixed "bad access" bug for 2nd drive on an i/f
+ * Version 5.04 changed "ira %d" to "irq %d" in DEBUG message
+ * added more comments, cleaned up unexpected_intr()
+ * OOOPS! fixed null pointer problem in ide reset code
+ * added autodetect for Triton chipset -- no effect yet
+ * Version 5.05 OOOPS! fixed bug in revalidate_disk()
+ * OOOPS! fixed bug in ide_do_request()
+ * added ATAPI reset sequence for cdroms
+ * Version 5.10 added Bus-Mastered DMA support for Triton Chipset
+ * some (mostly) cosmetic changes
+ * Version 5.11 added ht6560b support by malafoss@snakemail.hut.fi
+ * reworked PCI scanning code
+ * added automatic RZ1000 detection/support
+ * added automatic PCI CMD640 detection/support
+ * added option for VLB CMD640 support
+ * tweaked probe to find cdrom on hdb with disks on hda,hdc
+ * Version 5.12 some performance tuning
+ * added message to alert user to bad /dev/hd[cd] entries
+ * OOOPS! fixed bug in atapi reset
+ * driver now forces "serialize" again for all cmd640 chips
+ * noticed REALLY_SLOW_IO had no effect, moved it to ide.c
+ * made do_drive_cmd() into public ide_do_drive_cmd()
+ * Version 5.13 fixed typo ('B'), thanks to houston@boyd.geog.mcgill.ca
+ * fixed ht6560b support
+ * Version 5.13b (sss) fix problem in calling ide_cdrom_setup()
+ * don't bother invalidating nonexistent partitions
+ * Version 5.14 fixes to cmd640 support.. maybe it works now(?)
+ * added & tested full EZ-DRIVE support -- don't use LILO!
+ * don't enable 2nd CMD640 PCI port during init - conflict
+ * Version 5.15 bug fix in init_cmd640_vlb()
+ * bug fix in interrupt sharing code
+ * Version 5.16 ugh.. fix "serialize" support, broken in 5.15
+ * remove "Huh?" from cmd640 code
+ * added qd6580 interface speed select from Colten Edwards
+ * Version 5.17 kludge around bug in BIOS32 on Intel triton motherboards
+ * Version 5.18 new CMD640 code, moved to cmd640.c, #include'd for now
+ * new UMC8672 code, moved to umc8672.c, #include'd for now
+ * disallow turning on DMA when h/w not capable of DMA
+ * Version 5.19 fix potential infinite timeout on resets
+ * extend reset poll into a general purpose polling scheme
+ * add atapi tape drive support from Gadi Oxman
+ * simplify exit from _intr routines -- no IDE_DO_REQUEST
+ * Version 5.20 leave current rq on blkdev request list during I/O
+ * generalized ide_do_drive_cmd() for tape/cdrom driver use
+ * Version 5.21 fix nasty cdrom/tape bug (ide_preempt was messed up)
+ * Version 5.22 fix ide_xlate_1024() to work with/without drive->id
+ * Version 5.23 miscellaneous touch-ups
+ * Version 5.24 fix #if's for SUPPORT_CMD640
+ * Version 5.25 more touch-ups, fix cdrom resets, ...
+ * cmd640.c now configs/compiles separate from ide.c
+ * Version 5.26 keep_settings now maintains the using_dma flag
+ * fix [EZD] remap message to only output at boot time
+ * fix "bad /dev/ entry" message to say hdc, not hdc0
+ * fix ide_xlate_1024() to respect user specified CHS
+ * use CHS from partn table if it looks translated
+ * re-merged flags chipset,vlb_32bit,vlb_sync into io_32bit
+ * keep track of interface chipset type, when known
+ * add generic PIO mode "tuneproc" mechanism
+ * fix cmd640_vlb option
+ * fix ht6560b support (was completely broken)
+ * umc8672.c now configures/compiles separate from ide.c
+ * move dtc2278 support to dtc2278.c
+ * move ht6560b support to ht6560b.c
+ * move qd6580 support to qd6580.c
+ * add ali14xx support in ali14xx.c
+ * Version 5.27 add [no]autotune parameters to help cmd640
+ * move rz1000 support to rz1000.c
+ * Version 5.28 #include "ide_modes.h"
+ * fix disallow_unmask: now per-interface "no_unmask" bit
+ * force io_32bit to be the same on drive pairs of dtc2278
+ * improved IDE tape error handling, and tape DMA support
+ * bugfix in ide_do_drive_cmd() for cdroms + serialize
+ *
+ * Some additional driver compile-time options are in ide.h
+ *
+ * To do, in likely order of completion:
+ * - add Promise DC4030VL support from peterd@pnd-pc.demon.co.uk
+ * - modify kernel to obtain BIOS geometry for drives on 2nd/3rd/4th i/f
+*/
+
+#if defined (MACH) && !defined (LINUX_IDE_DEBUG)
+#undef DEBUG
+#endif
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_PCI
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#endif /* CONFIG_PCI */
+
+#include "ide.h"
+#include "ide_modes.h"
+
+static ide_hwgroup_t *irq_to_hwgroup [NR_IRQS];
+static const byte ide_hwif_to_major[MAX_HWIFS] = {IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR};
+
+static const unsigned short default_io_base[MAX_HWIFS] = {0x1f0, 0x170, 0x1e8, 0x168};
+static const byte default_irqs[MAX_HWIFS] = {14, 15, 11, 10};
+
+#if (DISK_RECOVERY_TIME > 0)
+/*
+ * For really screwy hardware (hey, at least it *can* be used with Linux)
+ * we can enforce a minimum delay time between successive operations.
+ */
+static unsigned long read_timer(void)
+{
+ 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;
+ restore_flags(flags);
+ return (t - i);
+}
+
+static void set_recovery_timer (ide_hwif_t *hwif)
+{
+ hwif->last_time = read_timer();
+}
+#define SET_RECOVERY_TIMER(drive) set_recovery_timer (drive)
+
+#else
+
+#define SET_RECOVERY_TIMER(drive)
+
+#endif /* DISK_RECOVERY_TIME */
+
+/*
+ * init_ide_data() sets reasonable default values into all fields
+ * of all instances of the hwifs and drives, but only on the first call.
+ * Subsequent calls have no effect (they don't wipe out anything).
+ *
+ * This routine is normally called at driver initialization time,
+ * but may also be called MUCH earlier during kernel "command-line"
+ * parameter processing. As such, we cannot depend on any other parts
+ * of the kernel (such as memory allocation) to be functioning yet.
+ *
+ * This is too bad, as otherwise we could dynamically allocate the
+ * ide_drive_t structs as needed, rather than always consuming memory
+ * for the max possible number (MAX_HWIFS * MAX_DRIVES) of them.
+ */
+#define MAGIC_COOKIE 0x12345678
+static void init_ide_data (void)
+{
+ byte *p;
+ unsigned int h, unit;
+ static unsigned long magic_cookie = MAGIC_COOKIE;
+
+ if (magic_cookie != MAGIC_COOKIE)
+ return; /* already initialized */
+ magic_cookie = 0;
+
+ for (h = 0; h < NR_IRQS; ++h)
+ irq_to_hwgroup[h] = NULL;
+
+ /* bulk initialize hwif & drive info with zeros */
+ p = ((byte *) ide_hwifs) + sizeof(ide_hwifs);
+ do {
+ *--p = 0;
+ } while (p > (byte *) ide_hwifs);
+
+ /* fill in any non-zero initial values */
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ ide_hwif_t *hwif = &ide_hwifs[h];
+
+ hwif->index = h;
+ hwif->noprobe = (h > 1);
+ hwif->io_base = default_io_base[h];
+ hwif->ctl_port = hwif->io_base ? hwif->io_base+0x206 : 0x000;
+#ifdef CONFIG_BLK_DEV_HD
+ if (hwif->io_base == HD_DATA)
+ hwif->noprobe = 1; /* may be overriden by ide_setup() */
+#endif /* CONFIG_BLK_DEV_HD */
+ hwif->major = ide_hwif_to_major[h];
+ hwif->name[0] = 'i';
+ hwif->name[1] = 'd';
+ hwif->name[2] = 'e';
+ hwif->name[3] = '0' + h;
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ hwif->tape_drive = NULL;
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+
+ drive->select.all = (unit<<4)|0xa0;
+ drive->hwif = hwif;
+ drive->ctl = 0x08;
+ drive->ready_stat = READY_STAT;
+ drive->bad_wstat = BAD_W_STAT;
+ drive->special.b.recalibrate = 1;
+ drive->special.b.set_geometry = 1;
+ drive->name[0] = 'h';
+ drive->name[1] = 'd';
+ drive->name[2] = 'a' + (h * MAX_DRIVES) + unit;
+ }
+ }
+}
+
+#if SUPPORT_VLB_SYNC
+/*
+ * Some localbus EIDE interfaces require a special access sequence
+ * when using 32-bit I/O instructions to transfer data. We call this
+ * the "vlb_sync" sequence, which consists of three successive reads
+ * of the sector count register location, with interrupts disabled
+ * to ensure that the reads all happen together.
+ */
+static inline void do_vlb_sync (unsigned short port) {
+ (void) inb (port);
+ (void) inb (port);
+ (void) inb (port);
+}
+#endif /* SUPPORT_VLB_SYNC */
+
+/*
+ * This is used for most PIO data transfers *from* the IDE interface
+ */
+void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
+{
+ unsigned short io_base = HWIF(drive)->io_base;
+ unsigned short data_reg = io_base+IDE_DATA_OFFSET;
+ byte io_32bit = drive->io_32bit;
+
+ if (io_32bit) {
+#if SUPPORT_VLB_SYNC
+ if (io_32bit & 2) {
+ cli();
+ do_vlb_sync(io_base+IDE_NSECTOR_OFFSET);
+ insl(data_reg, buffer, wcount);
+ if (drive->unmask)
+ sti();
+ } else
+#endif /* SUPPORT_VLB_SYNC */
+ insl(data_reg, buffer, wcount);
+ } else
+ insw(data_reg, buffer, wcount<<1);
+}
+
+/*
+ * This is used for most PIO data transfers *to* the IDE interface
+ */
+void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
+{
+ unsigned short io_base = HWIF(drive)->io_base;
+ unsigned short data_reg = io_base+IDE_DATA_OFFSET;
+ byte io_32bit = drive->io_32bit;
+
+ if (io_32bit) {
+#if SUPPORT_VLB_SYNC
+ if (io_32bit & 2) {
+ cli();
+ do_vlb_sync(io_base+IDE_NSECTOR_OFFSET);
+ outsl(data_reg, buffer, wcount);
+ if (drive->unmask)
+ sti();
+ } else
+#endif /* SUPPORT_VLB_SYNC */
+ outsl(data_reg, buffer, wcount);
+ } else
+ outsw(data_reg, buffer, wcount<<1);
+}
+
+/*
+ * This should get invoked any time we exit the driver to
+ * wait for an interrupt response from a drive. handler() points
+ * at the appropriate code to handle the next interrupt, and a
+ * timer is started to prevent us from waiting forever in case
+ * something goes wrong (see the timer_expiry() handler later on).
+ */
+void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout)
+{
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+#ifdef DEBUG
+ if (hwgroup->handler != NULL) {
+ printk("%s: ide_set_handler: handler not null; old=%p, new=%p\n",
+ drive->name, hwgroup->handler, handler);
+ }
+#endif
+ hwgroup->handler = handler;
+ hwgroup->timer.expires = jiffies + timeout;
+ add_timer(&(hwgroup->timer));
+}
+
+/*
+ * lba_capacity_is_ok() performs a sanity check on the claimed "lba_capacity"
+ * value for this drive (from its reported identification information).
+ *
+ * Returns: 1 if lba_capacity looks sensible
+ * 0 otherwise
+ */
+static int lba_capacity_is_ok (struct hd_driveid *id)
+{
+ unsigned long lba_sects = id->lba_capacity;
+ unsigned long chs_sects = id->cyls * id->heads * id->sectors;
+ unsigned long _10_percent = chs_sects / 10;
+
+ /* perform a rough sanity check on lba_sects: within 10% is "okay" */
+ if ((lba_sects - chs_sects) < _10_percent)
+ return 1; /* lba_capacity is good */
+
+ /* some drives have the word order reversed */
+ lba_sects = (lba_sects << 16) | (lba_sects >> 16);
+ if ((lba_sects - chs_sects) < _10_percent) {
+ id->lba_capacity = lba_sects; /* fix it */
+ return 1; /* lba_capacity is (now) good */
+ }
+ return 0; /* lba_capacity value is bad */
+}
+
+/*
+ * current_capacity() returns the capacity (in sectors) of a drive
+ * according to its current geometry/LBA settings.
+ */
+static unsigned long current_capacity (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ unsigned long capacity;
+
+ if (!drive->present)
+ return 0;
+ if (drive->media != ide_disk)
+ return 0x7fffffff; /* cdrom or tape */
+ /* Determine capacity, and use LBA if the drive properly supports it */
+ if (id != NULL && (id->capability & 2) && lba_capacity_is_ok(id)) {
+ drive->select.b.lba = 1;
+ capacity = id->lba_capacity;
+ } else {
+ drive->select.b.lba = 0;
+ capacity = drive->cyl * drive->head * drive->sect;
+ }
+ return (capacity - drive->sect0);
+}
+
+/*
+ * ide_geninit() is called exactly *once* for each major, from genhd.c,
+ * at the beginning of the initial partition check for the drives.
+ */
+static void ide_geninit (struct gendisk *gd)
+{
+ unsigned int unit;
+ ide_hwif_t *hwif = gd->real_devices;
+
+ for (unit = 0; unit < gd->nr_real; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (drive->present && drive->media == ide_cdrom)
+ ide_cdrom_setup(drive);
+#endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ if (drive->present && drive->media == ide_tape)
+ idetape_setup(drive);
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+ drive->part[0].nr_sects = current_capacity(drive);
+ if (!drive->present || drive->media != ide_disk) {
+ drive->part[0].start_sect = -1; /* skip partition check */
+ }
+ }
+ /*
+ * The partition check in genhd.c needs this string to identify
+ * our minor devices by name for display purposes.
+ * Note that doing this will prevent us from working correctly
+ * if ever called a second time for this major (never happens).
+ */
+ gd->real_devices = hwif->drives[0].name; /* name of first drive */
+}
+
+/*
+ * init_gendisk() (as opposed to ide_geninit) is called for each major device,
+ * after probing for drives, to allocate partition tables and other data
+ * structures needed for the routines in genhd.c. ide_geninit() gets called
+ * somewhat later, during the partition check.
+ */
+static void init_gendisk (ide_hwif_t *hwif)
+{
+ struct gendisk *gd;
+ unsigned int unit, units, minors;
+ int *bs;
+
+ /* figure out maximum drive number on the interface */
+ for (units = MAX_DRIVES; units > 0; --units) {
+ if (hwif->drives[units-1].present)
+ break;
+ }
+ minors = units * (1<<PARTN_BITS);
+ gd = kmalloc (sizeof(struct gendisk), GFP_KERNEL);
+ gd->sizes = kmalloc (minors * sizeof(int), GFP_KERNEL);
+ gd->part = kmalloc (minors * sizeof(struct hd_struct), GFP_KERNEL);
+ bs = kmalloc (minors*sizeof(int), GFP_KERNEL);
+
+ /* cdroms and msdos f/s are examples of non-1024 blocksizes */
+ blksize_size[hwif->major] = bs;
+ for (unit = 0; unit < minors; ++unit)
+ *bs++ = BLOCK_SIZE;
+
+ for (unit = 0; unit < units; ++unit)
+ hwif->drives[unit].part = &gd->part[unit << PARTN_BITS];
+
+ gd->major = hwif->major; /* our major device number */
+ gd->major_name = IDE_MAJOR_NAME; /* treated special in genhd.c */
+ gd->minor_shift = PARTN_BITS; /* num bits for partitions */
+ gd->max_p = 1<<PARTN_BITS; /* 1 + max partitions / drive */
+ gd->max_nr = units; /* max num real drives */
+ gd->nr_real = units; /* current num real drives */
+ gd->init = ide_geninit; /* initialization function */
+ gd->real_devices= hwif; /* ptr to internal data */
+
+ gd->next = gendisk_head; /* link new major into list */
+ hwif->gd = gendisk_head = gd;
+}
+
+static void do_reset1 (ide_drive_t *, int); /* needed below */
+
+#ifdef CONFIG_BLK_DEV_IDEATAPI
+/*
+ * atapi_reset_pollfunc() gets invoked to poll the interface for completion every 50ms
+ * during an atapi drive reset operation. If the drive has not yet responded,
+ * and we have not yet hit our maximum waiting time, then the timer is restarted
+ * for another 50ms.
+ */
+static void atapi_reset_pollfunc (ide_drive_t *drive)
+{
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ byte stat;
+
+ OUT_BYTE (drive->select.all, IDE_SELECT_REG);
+ udelay (10);
+
+ if (OK_STAT(stat=GET_STAT(), 0, BUSY_STAT)) {
+ printk("%s: ATAPI reset complete\n", drive->name);
+ } else {
+ if (jiffies < hwgroup->poll_timeout) {
+ ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
+ return; /* continue polling */
+ }
+ hwgroup->poll_timeout = 0; /* end of polling */
+ printk("%s: ATAPI reset timed-out, status=0x%02x\n", drive->name, stat);
+ do_reset1 (drive, 1); /* do it the old fashioned way */
+ }
+ hwgroup->poll_timeout = 0; /* done polling */
+}
+#endif /* CONFIG_BLK_DEV_IDEATAPI */
+
+/*
+ * reset_pollfunc() gets invoked to poll the interface for completion every 50ms
+ * during an ide reset operation. If the drives have not yet responded,
+ * and we have not yet hit our maximum waiting time, then the timer is restarted
+ * for another 50ms.
+ */
+static void reset_pollfunc (ide_drive_t *drive)
+{
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ ide_hwif_t *hwif = HWIF(drive);
+ byte tmp;
+
+ if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) {
+ if (jiffies < hwgroup->poll_timeout) {
+ ide_set_handler (drive, &reset_pollfunc, HZ/20);
+ return; /* continue polling */
+ }
+ printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp);
+ } else {
+ printk("%s: reset: ", hwif->name);
+ if ((tmp = GET_ERR()) == 1)
+ printk("success\n");
+ else {
+ printk("master: ");
+ switch (tmp & 0x7f) {
+ case 1: printk("passed");
+ break;
+ case 2: printk("formatter device error");
+ break;
+ case 3: printk("sector buffer error");
+ break;
+ case 4: printk("ECC circuitry error");
+ break;
+ case 5: printk("controlling MPU error");
+ break;
+ default:printk("error (0x%02x?)", tmp);
+ }
+ if (tmp & 0x80)
+ printk("; slave: failed");
+ printk("\n");
+ }
+ }
+ hwgroup->poll_timeout = 0; /* done polling */
+}
+
+/*
+ * do_reset1() attempts to recover a confused drive by resetting it.
+ * Unfortunately, resetting a disk drive actually resets all devices on
+ * the same interface, so it can really be thought of as resetting the
+ * interface rather than resetting the drive.
+ *
+ * ATAPI devices have their own reset mechanism which allows them to be
+ * individually reset without clobbering other devices on the same interface.
+ *
+ * Unfortunately, the IDE interface does not generate an interrupt to let
+ * us know when the reset operation has finished, so we must poll for this.
+ * Equally poor, though, is the fact that this may a very long time to complete,
+ * (up to 30 seconds worstcase). So, instead of busy-waiting here for it,
+ * we set a timer to poll at 50ms intervals.
+ */
+static void do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
+{
+ unsigned int unit;
+ unsigned long flags;
+ ide_hwif_t *hwif = HWIF(drive);
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+
+ save_flags(flags);
+ cli(); /* Why ? */
+
+#ifdef CONFIG_BLK_DEV_IDEATAPI
+ /* For an ATAPI device, first try an ATAPI SRST. */
+ if (drive->media != ide_disk) {
+ if (!do_not_try_atapi) {
+ if (!drive->keep_settings)
+ drive->unmask = 0;
+ OUT_BYTE (drive->select.all, IDE_SELECT_REG);
+ udelay (20);
+ OUT_BYTE (WIN_SRST, IDE_COMMAND_REG);
+ hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+ ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
+ restore_flags (flags);
+ return;
+ }
+ }
+#endif /* CONFIG_BLK_DEV_IDEATAPI */
+
+ /*
+ * First, reset any device state data we were maintaining
+ * for any of the drives on this interface.
+ */
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *rdrive = &hwif->drives[unit];
+ rdrive->special.all = 0;
+ rdrive->special.b.set_geometry = 1;
+ rdrive->special.b.recalibrate = 1;
+ if (OK_TO_RESET_CONTROLLER)
+ rdrive->mult_count = 0;
+ if (!rdrive->keep_settings) {
+ rdrive->using_dma = 0;
+ rdrive->mult_req = 0;
+ rdrive->unmask = 0;
+ }
+ if (rdrive->mult_req != rdrive->mult_count)
+ rdrive->special.b.set_multmode = 1;
+ }
+
+#if OK_TO_RESET_CONTROLLER
+ /*
+ * Note that we also set nIEN while resetting the device,
+ * to mask unwanted interrupts from the interface during the reset.
+ * However, due to the design of PC hardware, this will cause an
+ * immediate interrupt due to the edge transition it produces.
+ * This single interrupt gives us a "fast poll" for drives that
+ * recover from reset very quickly, saving us the first 50ms wait time.
+ */
+ OUT_BYTE(drive->ctl|6,IDE_CONTROL_REG); /* set SRST and nIEN */
+ udelay(5); /* more than enough time */
+ OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* clear SRST, leave nIEN */
+ hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+ ide_set_handler (drive, &reset_pollfunc, HZ/20);
+#endif /* OK_TO_RESET_CONTROLLER */
+
+ restore_flags (flags);
+}
+
+/*
+ * ide_do_reset() is the entry point to the drive/interface reset code.
+ */
+void ide_do_reset (ide_drive_t *drive)
+{
+ do_reset1 (drive, 0);
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ if (drive->media == ide_tape)
+ drive->tape.reset_issued=1;
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+}
+
+/*
+ * Clean up after success/failure of an explicit drive cmd
+ */
+void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err)
+{
+ unsigned long flags;
+ struct request *rq = HWGROUP(drive)->rq;
+
+ if (rq->cmd == IDE_DRIVE_CMD) {
+ byte *args = (byte *) rq->buffer;
+ rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
+ if (args) {
+ args[0] = stat;
+ args[1] = err;
+ args[2] = IN_BYTE(IDE_NSECTOR_REG);
+ }
+ }
+ save_flags(flags);
+ cli();
+ blk_dev[MAJOR(rq->rq_dev)].current_request = rq->next;
+ HWGROUP(drive)->rq = NULL;
+ rq->rq_status = RQ_INACTIVE;
+ if (rq->sem != NULL)
+ up(rq->sem);
+ restore_flags(flags);
+}
+
+/*
+ * Error reporting, in human readable form (luxurious, but a memory hog).
+ */
+byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat)
+{
+ unsigned long flags;
+ byte err = 0;
+
+ save_flags (flags);
+ sti();
+ printk("%s: %s: status=0x%02x", drive->name, msg, stat);
+#if FANCY_STATUS_DUMPS
+ if (drive->media == ide_disk) {
+ printk(" { ");
+ if (stat & BUSY_STAT)
+ printk("Busy ");
+ else {
+ if (stat & READY_STAT) printk("DriveReady ");
+ if (stat & WRERR_STAT) printk("DeviceFault ");
+ if (stat & SEEK_STAT) printk("SeekComplete ");
+ if (stat & DRQ_STAT) printk("DataRequest ");
+ if (stat & ECC_STAT) printk("CorrectedError ");
+ if (stat & INDEX_STAT) printk("Index ");
+ if (stat & ERR_STAT) printk("Error ");
+ }
+ printk("}");
+ }
+#endif /* FANCY_STATUS_DUMPS */
+ printk("\n");
+ if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) {
+ err = GET_ERR();
+ printk("%s: %s: error=0x%02x", drive->name, msg, err);
+#if FANCY_STATUS_DUMPS
+ if (drive->media == ide_disk) {
+ printk(" { ");
+ if (err & BBD_ERR) printk("BadSector ");
+ if (err & ECC_ERR) printk("UncorrectableError ");
+ if (err & ID_ERR) printk("SectorIdNotFound ");
+ if (err & ABRT_ERR) printk("DriveStatusError ");
+ if (err & TRK0_ERR) printk("TrackZeroNotFound ");
+ if (err & MARK_ERR) printk("AddrMarkNotFound ");
+ printk("}");
+ if (err & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {
+ byte cur = IN_BYTE(IDE_SELECT_REG);
+ if (cur & 0x40) { /* using LBA? */
+ printk(", LBAsect=%ld", (unsigned long)
+ ((cur&0xf)<<24)
+ |(IN_BYTE(IDE_HCYL_REG)<<16)
+ |(IN_BYTE(IDE_LCYL_REG)<<8)
+ | IN_BYTE(IDE_SECTOR_REG));
+ } else {
+ printk(", CHS=%d/%d/%d",
+ (IN_BYTE(IDE_HCYL_REG)<<8) +
+ IN_BYTE(IDE_LCYL_REG),
+ cur & 0xf,
+ IN_BYTE(IDE_SECTOR_REG));
+ }
+ if (HWGROUP(drive)->rq)
+ printk(", sector=%ld", HWGROUP(drive)->rq->sector);
+ }
+ }
+#endif /* FANCY_STATUS_DUMPS */
+ printk("\n");
+ }
+ restore_flags (flags);
+ return err;
+}
+
+/*
+ * try_to_flush_leftover_data() is invoked in response to a drive
+ * unexpectedly having its DRQ_STAT bit set. As an alternative to
+ * resetting the drive, this routine tries to clear the condition
+ * by read a sector's worth of data from the drive. Of course,
+ * this may not help if the drive is *waiting* for data from *us*.
+ */
+static void try_to_flush_leftover_data (ide_drive_t *drive)
+{
+ int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS;
+
+ while (i > 0) {
+ unsigned long buffer[16];
+ unsigned int wcount = (i > 16) ? 16 : i;
+ i -= wcount;
+ ide_input_data (drive, buffer, wcount);
+ }
+}
+
+/*
+ * ide_error() takes action based on the error returned by the controller.
+ */
+void ide_error (ide_drive_t *drive, const char *msg, byte stat)
+{
+ struct request *rq;
+ byte err;
+
+ err = ide_dump_status(drive, msg, stat);
+ if ((rq = HWGROUP(drive)->rq) == NULL || drive == NULL)
+ return;
+ /* retry only "normal" I/O: */
+ if (rq->cmd == IDE_DRIVE_CMD || (rq->cmd != READ && rq->cmd != WRITE && drive->media == ide_disk))
+ {
+ rq->errors = 1;
+ ide_end_drive_cmd(drive, stat, err);
+ return;
+ }
+ if (stat & BUSY_STAT) { /* other bits are useless when BUSY */
+ rq->errors |= ERROR_RESET;
+ } else {
+ if (drive->media == ide_disk && (stat & ERR_STAT)) {
+ /* err has different meaning on cdrom and tape */
+ if (err & (BBD_ERR | ECC_ERR)) /* retries won't help these */
+ rq->errors = ERROR_MAX;
+ else if (err & TRK0_ERR) /* help it find track zero */
+ rq->errors |= ERROR_RECAL;
+ }
+ if ((stat & DRQ_STAT) && rq->cmd != WRITE)
+ try_to_flush_leftover_data(drive);
+ }
+ if (GET_STAT() & (BUSY_STAT|DRQ_STAT))
+ rq->errors |= ERROR_RESET; /* Mmmm.. timing problem */
+
+ if (rq->errors >= ERROR_MAX) {
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ if (drive->media == ide_tape) {
+ rq->errors = 0;
+ idetape_end_request(0, HWGROUP(drive));
+ }
+ else
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+ ide_end_request(0, HWGROUP(drive));
+ }
+ else {
+ if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
+ ++rq->errors;
+ ide_do_reset(drive);
+ return;
+ } else if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
+ drive->special.b.recalibrate = 1;
+ ++rq->errors;
+ }
+}
+
+/*
+ * read_intr() is the handler for disk read/multread interrupts
+ */
+static void read_intr (ide_drive_t *drive)
+{
+ byte stat;
+ int i;
+ unsigned int msect, nsect;
+ struct request *rq;
+
+ if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
+ ide_error(drive, "read_intr", stat);
+ return;
+ }
+ msect = drive->mult_count;
+read_next:
+ rq = HWGROUP(drive)->rq;
+ if (msect) {
+ if ((nsect = rq->current_nr_sectors) > msect)
+ nsect = msect;
+ msect -= nsect;
+ } else
+ nsect = 1;
+ ide_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
+#ifdef DEBUG
+ printk("%s: read: sectors(%ld-%ld), buffer=0x%08lx, remaining=%ld\n",
+ drive->name, rq->sector, rq->sector+nsect-1,
+ (unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect);
+#endif
+ rq->sector += nsect;
+ rq->buffer += nsect<<9;
+ rq->errors = 0;
+ i = (rq->nr_sectors -= nsect);
+ if ((rq->current_nr_sectors -= nsect) <= 0)
+ ide_end_request(1, HWGROUP(drive));
+ if (i > 0) {
+ if (msect)
+ goto read_next;
+ ide_set_handler (drive, &read_intr, WAIT_CMD);
+ }
+}
+
+/*
+ * write_intr() is the handler for disk write interrupts
+ */
+static void write_intr (ide_drive_t *drive)
+{
+ byte stat;
+ int i;
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ struct request *rq = hwgroup->rq;
+
+ if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
+#ifdef DEBUG
+ printk("%s: write: sector %ld, buffer=0x%08lx, remaining=%ld\n",
+ drive->name, rq->sector, (unsigned long) rq->buffer,
+ rq->nr_sectors-1);
+#endif
+ if ((rq->nr_sectors == 1) ^ ((stat & DRQ_STAT) != 0)) {
+ rq->sector++;
+ rq->buffer += 512;
+ rq->errors = 0;
+ i = --rq->nr_sectors;
+ --rq->current_nr_sectors;
+ if (rq->current_nr_sectors <= 0)
+ ide_end_request(1, hwgroup);
+ if (i > 0) {
+ ide_output_data (drive, rq->buffer, SECTOR_WORDS);
+ ide_set_handler (drive, &write_intr, WAIT_CMD);
+ }
+ return;
+ }
+ }
+ ide_error(drive, "write_intr", stat);
+}
+
+/*
+ * multwrite() transfers a block of one or more sectors of data to a drive
+ * as part of a disk multwrite operation.
+ */
+static void multwrite (ide_drive_t *drive)
+{
+ struct request *rq = &HWGROUP(drive)->wrq;
+ unsigned int mcount = drive->mult_count;
+
+ do {
+ unsigned int nsect = rq->current_nr_sectors;
+ if (nsect > mcount)
+ nsect = mcount;
+ mcount -= nsect;
+
+ ide_output_data(drive, rq->buffer, nsect<<7);
+#ifdef DEBUG
+ printk("%s: multwrite: sector %ld, buffer=0x%08lx, count=%d, remaining=%ld\n",
+ drive->name, rq->sector, (unsigned long) rq->buffer,
+ nsect, rq->nr_sectors - nsect);
+#endif
+ if ((rq->nr_sectors -= nsect) <= 0)
+ break;
+ if ((rq->current_nr_sectors -= nsect) == 0) {
+ if ((rq->bh = rq->bh->b_reqnext) != NULL) {
+ rq->current_nr_sectors = rq->bh->b_size>>9;
+ rq->buffer = rq->bh->b_data;
+ } else {
+ panic("%s: buffer list corrupted\n", drive->name);
+ break;
+ }
+ } else {
+ rq->buffer += nsect << 9;
+ }
+ } while (mcount);
+}
+
+/*
+ * multwrite_intr() is the handler for disk multwrite interrupts
+ */
+static void multwrite_intr (ide_drive_t *drive)
+{
+ byte stat;
+ int i;
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ struct request *rq = &hwgroup->wrq;
+
+ if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
+ if (stat & DRQ_STAT) {
+ if (rq->nr_sectors) {
+ multwrite(drive);
+ ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
+ return;
+ }
+ } else {
+ if (!rq->nr_sectors) { /* all done? */
+ rq = hwgroup->rq;
+ for (i = rq->nr_sectors; i > 0;){
+ i -= rq->current_nr_sectors;
+ ide_end_request(1, hwgroup);
+ }
+ return;
+ }
+ }
+ }
+ ide_error(drive, "multwrite_intr", stat);
+}
+
+/*
+ * Issue a simple drive command
+ * The drive must be selected beforehand.
+ */
+static void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler)
+{
+ ide_set_handler (drive, handler, WAIT_CMD);
+ OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
+ OUT_BYTE(nsect,IDE_NSECTOR_REG);
+ OUT_BYTE(cmd,IDE_COMMAND_REG);
+}
+
+/*
+ * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd.
+ */
+static void set_multmode_intr (ide_drive_t *drive)
+{
+ byte stat = GET_STAT();
+
+ sti();
+ if (OK_STAT(stat,READY_STAT,BAD_STAT)) {
+ drive->mult_count = drive->mult_req;
+ } else {
+ drive->mult_req = drive->mult_count = 0;
+ drive->special.b.recalibrate = 1;
+ (void) ide_dump_status(drive, "set_multmode", stat);
+ }
+}
+
+/*
+ * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd.
+ */
+static void set_geometry_intr (ide_drive_t *drive)
+{
+ byte stat = GET_STAT();
+
+ sti();
+ if (!OK_STAT(stat,READY_STAT,BAD_STAT))
+ ide_error(drive, "set_geometry_intr", stat);
+}
+
+/*
+ * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd.
+ */
+static void recal_intr (ide_drive_t *drive)
+{
+ byte stat = GET_STAT();
+
+ sti();
+ if (!OK_STAT(stat,READY_STAT,BAD_STAT))
+ ide_error(drive, "recal_intr", stat);
+}
+
+/*
+ * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD.
+ */
+static void drive_cmd_intr (ide_drive_t *drive)
+{
+ byte stat = GET_STAT();
+
+ sti();
+ if (OK_STAT(stat,READY_STAT,BAD_STAT))
+ ide_end_drive_cmd (drive, stat, GET_ERR());
+ else
+ ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
+}
+
+/*
+ * do_special() is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT
+ * commands to a drive. It used to do much more, but has been scaled back.
+ */
+static inline void do_special (ide_drive_t *drive)
+{
+ special_t *s = &drive->special;
+next:
+#ifdef DEBUG
+ printk("%s: do_special: 0x%02x\n", drive->name, s->all);
+#endif
+ if (s->b.set_geometry) {
+ s->b.set_geometry = 0;
+ if (drive->media == ide_disk) {
+ OUT_BYTE(drive->sect,IDE_SECTOR_REG);
+ OUT_BYTE(drive->cyl,IDE_LCYL_REG);
+ OUT_BYTE(drive->cyl>>8,IDE_HCYL_REG);
+ OUT_BYTE(((drive->head-1)|drive->select.all)&0xBF,IDE_SELECT_REG);
+ ide_cmd(drive, WIN_SPECIFY, drive->sect, &set_geometry_intr);
+ }
+ } else if (s->b.recalibrate) {
+ s->b.recalibrate = 0;
+ if (drive->media == ide_disk) {
+ ide_cmd(drive, WIN_RESTORE, drive->sect, &recal_intr);
+ }
+ } else if (s->b.set_pio) {
+ ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
+ s->b.set_pio = 0;
+ if (tuneproc != NULL)
+ tuneproc(drive, drive->pio_req);
+ goto next;
+ } else if (s->b.set_multmode) {
+ s->b.set_multmode = 0;
+ if (drive->media == ide_disk) {
+ if (drive->id && drive->mult_req > drive->id->max_multsect)
+ drive->mult_req = drive->id->max_multsect;
+ ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr);
+ } else
+ drive->mult_req = 0;
+ } else if (s->all) {
+ s->all = 0;
+ printk("%s: bad special flag: 0x%02x\n", drive->name, s->all);
+ }
+}
+
+/*
+ * This routine busy-waits for the drive status to be not "busy".
+ * It then checks the status for all of the "good" bits and none
+ * of the "bad" bits, and if all is okay it returns 0. All other
+ * cases return 1 after invoking ide_error() -- caller should just return.
+ *
+ * This routine should get fixed to not hog the cpu during extra long waits..
+ * That could be done by busy-waiting for the first jiffy or two, and then
+ * setting a timer to wake up at half second intervals thereafter,
+ * until timeout is achieved, before timing out.
+ */
+int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout)
+{
+ byte stat;
+ unsigned long flags;
+
+test:
+ udelay(1); /* spec allows drive 400ns to change "BUSY" */
+ if (OK_STAT((stat = GET_STAT()), good, bad))
+ return 0; /* fast exit for most frequent case */
+ if (!(stat & BUSY_STAT)) {
+ ide_error(drive, "status error", stat);
+ return 1;
+ }
+
+ save_flags(flags);
+ sti();
+ timeout += jiffies;
+ do {
+ if (!((stat = GET_STAT()) & BUSY_STAT)) {
+ restore_flags(flags);
+ goto test;
+ }
+ } while (jiffies <= timeout);
+
+ restore_flags(flags);
+ ide_error(drive, "status timeout", GET_STAT());
+ return 1;
+}
+
+/*
+ * do_rw_disk() issues WIN_{MULT}READ and WIN_{MULT}WRITE commands to a disk,
+ * using LBA if supported, or CHS otherwise, to address sectors. It also takes
+ * care of issuing special DRIVE_CMDs.
+ */
+static inline void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+ unsigned short io_base = HWIF(drive)->io_base;
+
+ OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
+ OUT_BYTE(rq->nr_sectors,io_base+IDE_NSECTOR_OFFSET);
+ if (drive->select.b.lba) {
+#ifdef DEBUG
+ printk("%s: %sing: LBAsect=%ld, sectors=%ld, buffer=0x%08lx\n",
+ drive->name, (rq->cmd==READ)?"read":"writ",
+ block, rq->nr_sectors, (unsigned long) rq->buffer);
+#endif
+ OUT_BYTE(block,io_base+IDE_SECTOR_OFFSET);
+ OUT_BYTE(block>>=8,io_base+IDE_LCYL_OFFSET);
+ OUT_BYTE(block>>=8,io_base+IDE_HCYL_OFFSET);
+ OUT_BYTE(((block>>8)&0x0f)|drive->select.all,io_base+IDE_SELECT_OFFSET);
+ } else {
+ unsigned int sect,head,cyl,track;
+ track = block / drive->sect;
+ sect = block % drive->sect + 1;
+ OUT_BYTE(sect,io_base+IDE_SECTOR_OFFSET);
+ head = track % drive->head;
+ cyl = track / drive->head;
+ OUT_BYTE(cyl,io_base+IDE_LCYL_OFFSET);
+ OUT_BYTE(cyl>>8,io_base+IDE_HCYL_OFFSET);
+ OUT_BYTE(head|drive->select.all,io_base+IDE_SELECT_OFFSET);
+#ifdef DEBUG
+ printk("%s: %sing: CHS=%d/%d/%d, sectors=%ld, buffer=0x%08lx\n",
+ drive->name, (rq->cmd==READ)?"read":"writ", cyl,
+ head, sect, rq->nr_sectors, (unsigned long) rq->buffer);
+#endif
+ }
+ if (rq->cmd == READ) {
+#ifdef CONFIG_BLK_DEV_TRITON
+ if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_read, drive)))
+ return;
+#endif /* CONFIG_BLK_DEV_TRITON */
+ ide_set_handler(drive, &read_intr, WAIT_CMD);
+ OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, io_base+IDE_COMMAND_OFFSET);
+ return;
+ }
+ if (rq->cmd == WRITE) {
+#ifdef CONFIG_BLK_DEV_TRITON
+ if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_write, drive)))
+ return;
+#endif /* CONFIG_BLK_DEV_TRITON */
+ OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, io_base+IDE_COMMAND_OFFSET);
+ if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
+ printk("%s: no DRQ after issuing %s\n", drive->name,
+ drive->mult_count ? "MULTWRITE" : "WRITE");
+ return;
+ }
+ if (!drive->unmask)
+ cli();
+ if (drive->mult_count) {
+ HWGROUP(drive)->wrq = *rq; /* scratchpad */
+ ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
+ multwrite(drive);
+ } else {
+ ide_set_handler (drive, &write_intr, WAIT_CMD);
+ ide_output_data(drive, rq->buffer, SECTOR_WORDS);
+ }
+ return;
+ }
+ if (rq->cmd == IDE_DRIVE_CMD) {
+ byte *args = rq->buffer;
+ if (args) {
+#ifdef DEBUG
+ printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x\n",
+ drive->name, args[0], args[1], args[2]);
+#endif
+ OUT_BYTE(args[2],io_base+IDE_FEATURE_OFFSET);
+ ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
+ return;
+ } else {
+ /*
+ * NULL is actually a valid way of waiting for
+ * all current requests to be flushed from the queue.
+ */
+#ifdef DEBUG
+ printk("%s: DRIVE_CMD (null)\n", drive->name);
+#endif
+ ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
+ return;
+ }
+ }
+ printk("%s: bad command: %d\n", drive->name, rq->cmd);
+ ide_end_request(0, HWGROUP(drive));
+}
+
+/*
+ * do_request() initiates handling of a new I/O request
+ */
+static inline void do_request (ide_hwif_t *hwif, struct request *rq)
+{
+ unsigned int minor, unit;
+ unsigned long block, blockend;
+ ide_drive_t *drive;
+
+ sti();
+#ifdef DEBUG
+ printk("%s: do_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
+#endif
+ minor = MINOR(rq->rq_dev);
+ unit = minor >> PARTN_BITS;
+ if (MAJOR(rq->rq_dev) != hwif->major || unit >= MAX_DRIVES) {
+ printk("%s: bad device number: %s\n",
+ hwif->name, kdevname(rq->rq_dev));
+ goto kill_rq;
+ }
+ drive = &hwif->drives[unit];
+#ifdef DEBUG
+ if (rq->bh && !buffer_locked(rq->bh)) {
+ printk("%s: block not locked\n", drive->name);
+ goto kill_rq;
+ }
+#endif
+ block = rq->sector;
+ blockend = block + rq->nr_sectors;
+ if ((blockend < block) || (blockend > drive->part[minor&PARTN_MASK].nr_sects)) {
+ printk("%s%c: bad access: block=%ld, count=%ld\n", drive->name,
+ (minor&PARTN_MASK)?'0'+(minor&PARTN_MASK):' ', block, rq->nr_sectors);
+ goto kill_rq;
+ }
+ block += drive->part[minor&PARTN_MASK].start_sect + drive->sect0;
+#if FAKE_FDISK_FOR_EZDRIVE
+ if (block == 0 && drive->remap_0_to_1)
+ block = 1; /* redirect MBR access to EZ-Drive partn table */
+#endif /* FAKE_FDISK_FOR_EZDRIVE */
+ ((ide_hwgroup_t *)hwif->hwgroup)->drive = drive;
+#ifdef CONFIG_BLK_DEV_HT6560B
+ if (hwif->selectproc)
+ hwif->selectproc (drive);
+#endif /* CONFIG_BLK_DEV_HT6560B */
+#if (DISK_RECOVERY_TIME > 0)
+ while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME);
+#endif
+
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ POLL_HWIF_TAPE_DRIVE; /* macro from ide-tape.h */
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+
+ OUT_BYTE(drive->select.all,IDE_SELECT_REG);
+ if (ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
+ printk("%s: drive not ready for command\n", drive->name);
+ return;
+ }
+
+ if (!drive->special.all) {
+#ifdef CONFIG_BLK_DEV_IDEATAPI
+ switch (drive->media) {
+ case ide_disk:
+ do_rw_disk (drive, rq, block);
+ return;
+#ifdef CONFIG_BLK_DEV_IDECD
+ case ide_cdrom:
+ ide_do_rw_cdrom (drive, block);
+ return;
+#endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ case ide_tape:
+ if (rq->cmd == IDE_DRIVE_CMD) {
+ byte *args = (byte *) rq->buffer;
+ OUT_BYTE(args[2],IDE_FEATURE_REG);
+ ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
+ return;
+ }
+ idetape_do_request (drive, rq, block);
+ return;
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+
+ default:
+ printk("%s: media type %d not supported\n",
+ drive->name, drive->media);
+ goto kill_rq;
+ }
+#else
+ do_rw_disk (drive, rq, block); /* simpler and faster */
+ return;
+#endif /* CONFIG_BLK_DEV_IDEATAPI */;
+ }
+ do_special(drive);
+ return;
+kill_rq:
+ ide_end_request(0, hwif->hwgroup);
+}
+
+/*
+ * The driver enables interrupts as much as possible. In order to do this,
+ * (a) the device-interrupt is always masked before entry, and
+ * (b) the timeout-interrupt is always disabled before entry.
+ *
+ * If we enter here from, say irq14, and then start a new request for irq15,
+ * (possible with "serialize" option) then we cannot ensure that we exit
+ * before the irq15 hits us. So, we must be careful not to let this bother us.
+ *
+ * Interrupts are still masked (by default) whenever we are exchanging
+ * data/cmds with a drive, because some drives seem to have very poor
+ * tolerance for latency during I/O. For devices which don't suffer from
+ * this problem (most don't), the unmask flag can be set using the "hdparm"
+ * utility, to permit other interrupts during data/cmd transfers.
+ */
+void ide_do_request (ide_hwgroup_t *hwgroup)
+{
+ cli(); /* paranoia */
+ if (hwgroup->handler != NULL) {
+ printk("%s: EEeekk!! handler not NULL in ide_do_request()\n", hwgroup->hwif->name);
+ return;
+ }
+ do {
+ ide_hwif_t *hwif = hwgroup->hwif;
+ struct request *rq;
+ if ((rq = hwgroup->rq) == NULL) {
+ do {
+ rq = blk_dev[hwif->major].current_request;
+ if (rq != NULL && rq->rq_status != RQ_INACTIVE)
+ goto got_rq;
+ } while ((hwif = hwif->next) != hwgroup->hwif);
+ return; /* no work left for this hwgroup */
+ }
+ got_rq:
+ do_request(hwgroup->hwif = hwif, hwgroup->rq = rq);
+ cli();
+ } while (hwgroup->handler == NULL);
+}
+
+/*
+ * do_hwgroup_request() invokes ide_do_request() after first masking
+ * all possible interrupts for the current hwgroup. This prevents race
+ * conditions in the event that an unexpected interrupt occurs while
+ * we are in the driver.
+ *
+ * Note that when an interrupt is used to reenter the driver, the first level
+ * handler will already have masked the irq that triggered, but any other ones
+ * for the hwgroup will still be unmasked. The driver tries to be careful
+ * about such things.
+ */
+static void do_hwgroup_request (ide_hwgroup_t *hwgroup)
+{
+ if (hwgroup->handler == NULL) {
+ ide_hwif_t *hgif = hwgroup->hwif;
+ ide_hwif_t *hwif = hgif;
+ do {
+ disable_irq(hwif->irq);
+ } while ((hwif = hwif->next) != hgif);
+ ide_do_request (hwgroup);
+ do {
+ enable_irq(hwif->irq);
+ } while ((hwif = hwif->next) != hgif);
+ }
+}
+
+static void do_ide0_request (void) /* invoked with cli() */
+{
+ do_hwgroup_request (ide_hwifs[0].hwgroup);
+}
+
+static void do_ide1_request (void) /* invoked with cli() */
+{
+ do_hwgroup_request (ide_hwifs[1].hwgroup);
+}
+
+static void do_ide2_request (void) /* invoked with cli() */
+{
+ do_hwgroup_request (ide_hwifs[2].hwgroup);
+}
+
+static void do_ide3_request (void) /* invoked with cli() */
+{
+ do_hwgroup_request (ide_hwifs[3].hwgroup);
+}
+
+static void timer_expiry (unsigned long data)
+{
+ ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data;
+ ide_drive_t *drive = hwgroup->drive;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ if (hwgroup->poll_timeout != 0) { /* polling in progress? */
+ ide_handler_t *handler = hwgroup->handler;
+ hwgroup->handler = NULL;
+ handler(drive);
+ } else if (hwgroup->handler == NULL) { /* not waiting for anything? */
+ sti(); /* drive must have responded just as the timer expired */
+ printk("%s: marginal timeout\n", drive->name);
+ } else {
+ hwgroup->handler = NULL; /* abort the operation */
+ if (hwgroup->hwif->dmaproc)
+ (void) hwgroup->hwif->dmaproc (ide_dma_abort, drive);
+ ide_error(drive, "irq timeout", GET_STAT());
+ }
+ if (hwgroup->handler == NULL)
+ do_hwgroup_request (hwgroup);
+ restore_flags(flags);
+}
+
+/*
+ * There's nothing really useful we can do with an unexpected interrupt,
+ * other than reading the status register (to clear it), and logging it.
+ * There should be no way that an irq can happen before we're ready for it,
+ * so we needn't worry much about losing an "important" interrupt here.
+ *
+ * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the
+ * drive enters "idle", "standby", or "sleep" mode, so if the status looks
+ * "good", we just ignore the interrupt completely.
+ *
+ * This routine assumes cli() is in effect when called.
+ *
+ * If an unexpected interrupt happens on irq15 while we are handling irq14
+ * and if the two interfaces are "serialized" (CMD640B), then it looks like
+ * we could screw up by interfering with a new request being set up for irq15.
+ *
+ * In reality, this is a non-issue. The new command is not sent unless the
+ * drive is ready to accept one, in which case we know the drive is not
+ * trying to interrupt us. And ide_set_handler() is always invoked before
+ * completing the issuance of any new drive command, so we will not be
+ * accidently invoked as a result of any valid command completion interrupt.
+ *
+ */
+static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup)
+{
+ byte stat;
+ unsigned int unit;
+ ide_hwif_t *hwif = hwgroup->hwif;
+
+ /*
+ * handle the unexpected interrupt
+ */
+ do {
+ if (hwif->irq == irq) {
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ if (!drive->present)
+ continue;
+#ifdef CONFIG_BLK_DEV_HT6560B
+ if (hwif->selectproc)
+ hwif->selectproc (drive);
+#endif /* CONFIG_BLK_DEV_HT6560B */
+ if (!OK_STAT(stat=GET_STAT(), drive->ready_stat, BAD_STAT))
+ (void) ide_dump_status(drive, "unexpected_intr", stat);
+ if ((stat & DRQ_STAT))
+ try_to_flush_leftover_data(drive);
+ }
+ }
+ } while ((hwif = hwif->next) != hwgroup->hwif);
+#ifdef CONFIG_BLK_DEV_HT6560B
+ if (hwif->selectproc)
+ hwif->selectproc (hwgroup->drive);
+#endif /* CONFIG_BLK_DEV_HT6560B */
+}
+
+/*
+ * entry point for all interrupts, caller does cli() for us
+ */
+static void ide_intr (int irq, struct pt_regs *regs)
+{
+ ide_hwgroup_t *hwgroup = irq_to_hwgroup[irq];
+ ide_handler_t *handler;
+
+ if (irq == hwgroup->hwif->irq && (handler = hwgroup->handler) != NULL) {
+ ide_drive_t *drive = hwgroup->drive;
+ hwgroup->handler = NULL;
+ del_timer(&(hwgroup->timer));
+ if (drive->unmask)
+ sti();
+ handler(drive);
+ cli(); /* this is necessary, as next rq may be different irq */
+ if (hwgroup->handler == NULL) {
+ SET_RECOVERY_TIMER(HWIF(drive));
+ ide_do_request(hwgroup);
+ }
+ } else {
+ unexpected_intr(irq, hwgroup);
+ }
+ cli();
+}
+
+/*
+ * get_info_ptr() returns the (ide_drive_t *) for a given device number.
+ * It returns NULL if the given device number does not match any present drives.
+ */
+static ide_drive_t *get_info_ptr (kdev_t i_rdev)
+{
+ int major = MAJOR(i_rdev);
+ unsigned int h;
+
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ ide_hwif_t *hwif = &ide_hwifs[h];
+ if (hwif->present && major == hwif->major) {
+ unsigned unit = DEVICE_NR(i_rdev);
+ if (unit < MAX_DRIVES) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ if (drive->present)
+ return drive;
+ } else if (major == IDE0_MAJOR && unit < 4) {
+ printk("ide: probable bad entry for /dev/hd%c\n", 'a'+unit);
+ printk("ide: to fix it, run: /usr/src/linux/drivers/block/MAKEDEV.ide\n");
+ }
+ break;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * This function is intended to be used prior to invoking ide_do_drive_cmd().
+ */
+void ide_init_drive_cmd (struct request *rq)
+{
+ rq->buffer = NULL;
+ rq->cmd = IDE_DRIVE_CMD;
+ rq->sector = 0;
+ rq->nr_sectors = 0;
+ rq->current_nr_sectors = 0;
+ rq->sem = NULL;
+ rq->bh = NULL;
+ rq->bhtail = NULL;
+ rq->next = NULL;
+
+#if 0 /* these are done each time through ide_do_drive_cmd() */
+ rq->errors = 0;
+ rq->rq_status = RQ_ACTIVE;
+ rq->rq_dev = ????;
+#endif
+}
+
+/*
+ * This function issues a special IDE device request
+ * onto the request queue.
+ *
+ * If action is ide_wait, then then rq is queued at the end of
+ * the request queue, and the function sleeps until it has been
+ * processed. This is for use when invoked from an ioctl handler.
+ *
+ * If action is ide_preempt, then the rq is queued at the head of
+ * the request queue, displacing the currently-being-processed
+ * request and this function returns immediately without waiting
+ * for the new rq to be completed. This is VERY DANGEROUS, and is
+ * intended for careful use by the ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_next, then the rq is queued immediately after
+ * the currently-being-processed-request (if any), and the function
+ * returns without waiting for the new rq to be completed. As above,
+ * This is VERY DANGEROUS, and is intended for careful use by the
+ * ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_end, then the rq is queued at the end of the
+ * request queue, and the function returns immediately without waiting
+ * for the new rq to be completed. This is again intended for careful
+ * use by the ATAPI tape/cdrom driver code. (Currently used by ide-tape.c,
+ * when operating in the pipelined operation mode).
+ */
+int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action)
+{
+ unsigned long flags;
+ unsigned int major = HWIF(drive)->major;
+ struct request *cur_rq;
+ struct blk_dev_struct *bdev = &blk_dev[major];
+ struct semaphore sem = MUTEX_LOCKED;
+
+ rq->errors = 0;
+ rq->rq_status = RQ_ACTIVE;
+ rq->rq_dev = MKDEV(major,(drive->select.b.unit)<<PARTN_BITS);
+ if (action == ide_wait)
+ rq->sem = &sem;
+
+ save_flags(flags);
+ cli();
+ cur_rq = bdev->current_request;
+
+ if (cur_rq == NULL || action == ide_preempt) {
+ rq->next = cur_rq;
+ bdev->current_request = rq;
+ if (action == ide_preempt) {
+ HWGROUP(drive)->rq = NULL;
+ } else
+ if (HWGROUP(drive)->rq == NULL) { /* is this necessary (?) */
+ bdev->request_fn();
+ cli();
+ }
+ } else {
+ if (action == ide_wait || action == ide_end) {
+ while (cur_rq->next != NULL) /* find end of list */
+ cur_rq = cur_rq->next;
+ }
+ rq->next = cur_rq->next;
+ cur_rq->next = rq;
+ }
+ if (action == ide_wait && rq->rq_status != RQ_INACTIVE)
+ down(&sem); /* wait for it to be serviced */
+ restore_flags(flags);
+ return rq->errors ? -EIO : 0; /* return -EIO if errors */
+}
+
+static int ide_open(struct inode * inode, struct file * filp)
+{
+ ide_drive_t *drive;
+ unsigned long flags;
+
+ if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
+ return -ENODEV;
+ save_flags(flags);
+ cli();
+ while (drive->busy)
+ sleep_on(&drive->wqueue);
+ drive->usage++;
+ restore_flags(flags);
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (drive->media == ide_cdrom)
+ return ide_cdrom_open (inode, filp, drive);
+#endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ if (drive->media == ide_tape)
+ return idetape_blkdev_open (inode, filp, drive);
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+ if (drive->removeable) {
+ byte door_lock[] = {WIN_DOORLOCK,0,0,0};
+ struct request rq;
+ check_disk_change(inode->i_rdev);
+ ide_init_drive_cmd (&rq);
+ rq.buffer = door_lock;
+ /*
+ * Ignore the return code from door_lock,
+ * since the open() has already succeeded,
+ * and the door_lock is irrelevant at this point.
+ */
+ (void) ide_do_drive_cmd(drive, &rq, ide_wait);
+ }
+ return 0;
+}
+
+/*
+ * Releasing a block device means we sync() it, so that it can safely
+ * be forgotten about...
+ */
+static void ide_release(struct inode * inode, struct file * file)
+{
+ ide_drive_t *drive;
+
+ if ((drive = get_info_ptr(inode->i_rdev)) != NULL) {
+ sync_dev(inode->i_rdev);
+ drive->usage--;
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (drive->media == ide_cdrom) {
+ ide_cdrom_release (inode, file, drive);
+ return;
+ }
+#endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ if (drive->media == ide_tape) {
+ idetape_blkdev_release (inode, file, drive);
+ return;
+ }
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+ if (drive->removeable) {
+ byte door_unlock[] = {WIN_DOORUNLOCK,0,0,0};
+ struct request rq;
+ invalidate_buffers(inode->i_rdev);
+ ide_init_drive_cmd (&rq);
+ rq.buffer = door_unlock;
+ (void) ide_do_drive_cmd(drive, &rq, ide_wait);
+ }
+ }
+}
+
+/*
+ * This routine is called to flush all partitions and partition tables
+ * for a changed disk, and then re-read the new partition table.
+ * If we are revalidating a disk because of a media change, then we
+ * enter with usage == 0. If we are using an ioctl, we automatically have
+ * usage == 1 (we need an open channel to use an ioctl :-), so this
+ * is our limit.
+ */
+static int revalidate_disk(kdev_t i_rdev)
+{
+ ide_drive_t *drive;
+ unsigned int p, major, minor;
+ long flags;
+
+ if ((drive = get_info_ptr(i_rdev)) == NULL)
+ return -ENODEV;
+
+ major = MAJOR(i_rdev);
+ minor = drive->select.b.unit << PARTN_BITS;
+ save_flags(flags);
+ cli();
+ if (drive->busy || (drive->usage > 1)) {
+ restore_flags(flags);
+ return -EBUSY;
+ };
+ drive->busy = 1;
+ restore_flags(flags);
+
+ for (p = 0; p < (1<<PARTN_BITS); ++p) {
+ if (drive->part[p].nr_sects > 0) {
+ kdev_t devp = MKDEV(major, minor+p);
+ sync_dev (devp);
+ invalidate_inodes (devp);
+ invalidate_buffers (devp);
+ }
+ drive->part[p].start_sect = 0;
+ drive->part[p].nr_sects = 0;
+ };
+
+ drive->part[0].nr_sects = current_capacity(drive);
+ if (drive->media == ide_disk)
+ resetup_one_dev(HWIF(drive)->gd, drive->select.b.unit);
+
+ drive->busy = 0;
+ wake_up(&drive->wqueue);
+ return 0;
+}
+
+static int write_fs_long (unsigned long useraddr, long value)
+{
+ int err;
+
+ if (NULL == (long *)useraddr)
+ return -EINVAL;
+ if ((err = verify_area(VERIFY_WRITE, (long *)useraddr, sizeof(long))))
+ return err;
+ put_user((unsigned)value, (long *) useraddr);
+ return 0;
+}
+
+static int ide_ioctl (struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct hd_geometry *loc = (struct hd_geometry *) arg;
+ int err;
+ ide_drive_t *drive;
+ unsigned long flags;
+ struct request rq;
+
+ ide_init_drive_cmd (&rq);
+ if (!inode || !(inode->i_rdev))
+ return -EINVAL;
+ if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
+ return -ENODEV;
+ switch (cmd) {
+ case HDIO_GETGEO:
+ if (!loc || drive->media != ide_disk) return -EINVAL;
+ err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
+ if (err) return err;
+ put_user(drive->bios_head, (byte *) &loc->heads);
+ put_user(drive->bios_sect, (byte *) &loc->sectors);
+ put_user(drive->bios_cyl, (unsigned short *) &loc->cylinders);
+ put_user((unsigned)drive->part[MINOR(inode->i_rdev)&PARTN_MASK].start_sect,
+ (unsigned long *) &loc->start);
+ return 0;
+
+ case BLKFLSBUF:
+ if(!suser()) return -EACCES;
+ fsync_dev(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ return 0;
+
+ case BLKRASET:
+ if(!suser()) return -EACCES;
+ if(arg > 0xff) return -EINVAL;
+ read_ahead[MAJOR(inode->i_rdev)] = arg;
+ return 0;
+
+ case BLKRAGET:
+ return write_fs_long(arg, read_ahead[MAJOR(inode->i_rdev)]);
+
+ case BLKGETSIZE: /* Return device size */
+ return write_fs_long(arg, drive->part[MINOR(inode->i_rdev)&PARTN_MASK].nr_sects);
+ case BLKRRPART: /* Re-read partition tables */
+ return revalidate_disk(inode->i_rdev);
+
+ case HDIO_GET_KEEPSETTINGS:
+ return write_fs_long(arg, drive->keep_settings);
+
+ case HDIO_GET_UNMASKINTR:
+ return write_fs_long(arg, drive->unmask);
+
+ case HDIO_GET_DMA:
+ return write_fs_long(arg, drive->using_dma);
+
+ case HDIO_GET_32BIT:
+ return write_fs_long(arg, drive->io_32bit);
+
+ case HDIO_GET_MULTCOUNT:
+ return write_fs_long(arg, drive->mult_count);
+
+ case HDIO_GET_IDENTITY:
+ if (!arg || (MINOR(inode->i_rdev) & PARTN_MASK))
+ return -EINVAL;
+ if (drive->id == NULL)
+ return -ENOMSG;
+ err = verify_area(VERIFY_WRITE, (char *)arg, sizeof(*drive->id));
+ if (!err)
+ memcpy_tofs((char *)arg, (char *)drive->id, sizeof(*drive->id));
+ return err;
+
+ case HDIO_GET_NOWERR:
+ return write_fs_long(arg, drive->bad_wstat == BAD_R_STAT);
+
+ case HDIO_SET_DMA:
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (drive->media == ide_cdrom)
+ return -EPERM;
+#endif /* CONFIG_BLK_DEV_IDECD */
+ if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc)
+ return -EPERM;
+ case HDIO_SET_KEEPSETTINGS:
+ case HDIO_SET_UNMASKINTR:
+ case HDIO_SET_NOWERR:
+ if (arg > 1)
+ return -EINVAL;
+ case HDIO_SET_32BIT:
+ if (!suser())
+ return -EACCES;
+ if ((MINOR(inode->i_rdev) & PARTN_MASK))
+ return -EINVAL;
+ save_flags(flags);
+ cli();
+ switch (cmd) {
+ case HDIO_SET_DMA:
+ if (!(HWIF(drive)->dmaproc)) {
+ restore_flags(flags);
+ return -EPERM;
+ }
+ drive->using_dma = arg;
+ break;
+ case HDIO_SET_KEEPSETTINGS:
+ drive->keep_settings = arg;
+ break;
+ case HDIO_SET_UNMASKINTR:
+ if (arg && HWIF(drive)->no_unmask) {
+ restore_flags(flags);
+ return -EPERM;
+ }
+ drive->unmask = arg;
+ break;
+ case HDIO_SET_NOWERR:
+ drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT;
+ break;
+ case HDIO_SET_32BIT:
+ if (arg > (1 + (SUPPORT_VLB_SYNC<<1)))
+ return -EINVAL;
+ drive->io_32bit = arg;
+#ifdef CONFIG_BLK_DEV_DTC2278
+ if (HWIF(drive)->chipset == ide_dtc2278)
+ HWIF(drive)->drives[!drive->select.b.unit].io_32bit = arg;
+#endif /* CONFIG_BLK_DEV_DTC2278 */
+ break;
+ }
+ restore_flags(flags);
+ return 0;
+
+ case HDIO_SET_MULTCOUNT:
+ if (!suser())
+ return -EACCES;
+ if (MINOR(inode->i_rdev) & PARTN_MASK)
+ return -EINVAL;
+ if (drive->id && arg > drive->id->max_multsect)
+ return -EINVAL;
+ save_flags(flags);
+ cli();
+ if (drive->special.b.set_multmode) {
+ restore_flags(flags);
+ return -EBUSY;
+ }
+ drive->mult_req = arg;
+ drive->special.b.set_multmode = 1;
+ restore_flags(flags);
+ (void) ide_do_drive_cmd (drive, &rq, ide_wait);
+ return (drive->mult_count == arg) ? 0 : -EIO;
+
+ case HDIO_DRIVE_CMD:
+ {
+ unsigned long args;
+
+ if (NULL == (long *) arg)
+ err = ide_do_drive_cmd(drive, &rq, ide_wait);
+ else {
+ if (!(err = verify_area(VERIFY_READ,(long *)arg,sizeof(long))))
+ {
+ args = get_user((long *)arg);
+ if (!(err = verify_area(VERIFY_WRITE,(long *)arg,sizeof(long)))) {
+ rq.buffer = (char *) &args;
+ err = ide_do_drive_cmd(drive, &rq, ide_wait);
+ put_user(args,(long *)arg);
+ }
+ }
+ }
+ return err;
+ }
+ case HDIO_SET_PIO_MODE:
+ if (!suser())
+ return -EACCES;
+ if (MINOR(inode->i_rdev) & PARTN_MASK)
+ return -EINVAL;
+ if (!HWIF(drive)->tuneproc)
+ return -ENOSYS;
+ save_flags(flags);
+ cli();
+ drive->pio_req = (int) arg;
+ drive->special.b.set_pio = 1;
+ restore_flags(flags);
+ return 0;
+
+ RO_IOCTLS(inode->i_rdev, arg);
+
+ default:
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (drive->media == ide_cdrom)
+ return ide_cdrom_ioctl(drive, inode, file, cmd, arg);
+#endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ if (drive->media == ide_tape)
+ return idetape_blkdev_ioctl(drive, inode, file, cmd, arg);
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+ return -EPERM;
+ }
+}
+
+static int ide_check_media_change (kdev_t i_rdev)
+{
+ ide_drive_t *drive;
+
+ if ((drive = get_info_ptr(i_rdev)) == NULL)
+ return -ENODEV;
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (drive->media == ide_cdrom)
+ return ide_cdrom_check_media_change (drive);
+#endif /* CONFIG_BLK_DEV_IDECD */
+ if (drive->removeable) /* for disks */
+ return 1; /* always assume it was changed */
+ return 0;
+}
+
+void ide_fixstring (byte *s, const int bytecount, const int byteswap)
+{
+ byte *p = s, *end = &s[bytecount & ~1]; /* bytecount must be even */
+
+ if (byteswap) {
+ /* convert from big-endian to host byte order */
+ for (p = end ; p != s;) {
+ unsigned short *pp = (unsigned short *) (p -= 2);
+ *pp = ntohs(*pp);
+ }
+ }
+
+ /* strip leading blanks */
+ while (s != end && *s == ' ')
+ ++s;
+
+ /* compress internal blanks and strip trailing blanks */
+ while (s != end && *s) {
+ if (*s++ != ' ' || (s != end && *s && *s != ' '))
+ *p++ = *(s-1);
+ }
+
+ /* wipe out trailing garbage */
+ while (p != end)
+ *p++ = '\0';
+}
+
+static inline void do_identify (ide_drive_t *drive, byte cmd)
+{
+ int bswap;
+ struct hd_driveid *id;
+ unsigned long capacity, check;
+
+ id = drive->id = kmalloc (SECTOR_WORDS*4, GFP_KERNEL);
+ ide_input_data(drive, id, SECTOR_WORDS); /* read 512 bytes of id info */
+ sti();
+
+ /*
+ * EATA SCSI controllers do a hardware ATA emulation: ignore them
+ */
+ if ((id->model[0] == 'P' && id->model[1] == 'M')
+ || (id->model[0] == 'S' && id->model[1] == 'K')) {
+ printk("%s: EATA SCSI HBA %.10s\n", drive->name, id->model);
+ drive->present = 0;
+ return;
+ }
+
+ /*
+ * WIN_IDENTIFY returns little-endian info,
+ * WIN_PIDENTIFY *usually* returns little-endian info.
+ */
+ bswap = 1;
+ if (cmd == WIN_PIDENTIFY) {
+ if ((id->model[0] == 'N' && id->model[1] == 'E') /* NEC */
+ || (id->model[0] == 'F' && id->model[1] == 'X') /* Mitsumi */
+ || (id->model[0] == 'P' && id->model[1] == 'i'))/* Pioneer */
+ bswap = 0; /* Vertos drives may still be weird */
+ }
+ ide_fixstring (id->model, sizeof(id->model), bswap);
+ ide_fixstring (id->fw_rev, sizeof(id->fw_rev), bswap);
+ ide_fixstring (id->serial_no, sizeof(id->serial_no), bswap);
+
+ /*
+ * Check for an ATAPI device
+ */
+
+ if (cmd == WIN_PIDENTIFY) {
+ byte type = (id->config >> 8) & 0x1f;
+ printk("%s: %s, ATAPI ", drive->name, id->model);
+ switch (type) {
+ case 0: /* Early cdrom models used zero */
+ case 5:
+#ifdef CONFIG_BLK_DEV_IDECD
+ printk ("CDROM drive\n");
+ drive->media = ide_cdrom;
+ drive->present = 1;
+ drive->removeable = 1;
+ return;
+#else
+ printk ("CDROM ");
+ break;
+#endif /* CONFIG_BLK_DEV_IDECD */
+ case 1:
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ printk ("TAPE drive");
+ if (idetape_identify_device (drive,id)) {
+ drive->media = ide_tape;
+ drive->present = 1;
+ drive->removeable = 1;
+ if (HWIF(drive)->dmaproc != NULL &&
+ !HWIF(drive)->dmaproc(ide_dma_check, drive))
+ printk(", DMA");
+ printk("\n");
+ }
+ else {
+ drive->present = 0;
+ printk ("\nide-tape: the tape is not supported by this version of the driver\n");
+ }
+ return;
+#else
+ printk ("TAPE ");
+ break;
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+ default:
+ drive->present = 0;
+ printk("Type %d - Unknown device\n", type);
+ return;
+ }
+ drive->present = 0;
+ printk("- not supported by this kernel\n");
+ return;
+ }
+
+ /* check for removeable disks (eg. SYQUEST), ignore 'WD' drives */
+ if (id->config & (1<<7)) { /* removeable disk ? */
+ if (id->model[0] != 'W' || id->model[1] != 'D')
+ drive->removeable = 1;
+ }
+
+ drive->media = ide_disk;
+ /* Extract geometry if we did not already have one for the drive */
+ if (!drive->present) {
+ drive->present = 1;
+ drive->cyl = drive->bios_cyl = id->cyls;
+ drive->head = drive->bios_head = id->heads;
+ drive->sect = drive->bios_sect = id->sectors;
+ }
+ /* Handle logical geometry translation by the drive */
+ if ((id->field_valid & 1) && id->cur_cyls && id->cur_heads
+ && (id->cur_heads <= 16) && id->cur_sectors)
+ {
+ /*
+ * Extract the physical drive geometry for our use.
+ * Note that we purposely do *not* update the bios info.
+ * This way, programs that use it (like fdisk) will
+ * still have the same logical view as the BIOS does,
+ * which keeps the partition table from being screwed.
+ *
+ * An exception to this is the cylinder count,
+ * which we reexamine later on to correct for 1024 limitations.
+ */
+ drive->cyl = id->cur_cyls;
+ drive->head = id->cur_heads;
+ drive->sect = id->cur_sectors;
+
+ /* check for word-swapped "capacity" field in id information */
+ capacity = drive->cyl * drive->head * drive->sect;
+ check = (id->cur_capacity0 << 16) | id->cur_capacity1;
+ if (check == capacity) { /* was it swapped? */
+ /* yes, bring it into little-endian order: */
+ id->cur_capacity0 = (capacity >> 0) & 0xffff;
+ id->cur_capacity1 = (capacity >> 16) & 0xffff;
+ }
+ }
+ /* Use physical geometry if what we have still makes no sense */
+ if ((!drive->head || drive->head > 16) && id->heads && id->heads <= 16) {
+ drive->cyl = id->cyls;
+ drive->head = id->heads;
+ drive->sect = id->sectors;
+ }
+ /* Correct the number of cyls if the bios value is too small */
+ if (drive->sect == drive->bios_sect && drive->head == drive->bios_head) {
+ if (drive->cyl > drive->bios_cyl)
+ drive->bios_cyl = drive->cyl;
+ }
+
+ (void) current_capacity (drive); /* initialize LBA selection */
+
+ printk ("%s: %.40s, %ldMB w/%dKB Cache, %sCHS=%d/%d/%d",
+ drive->name, id->model, current_capacity(drive)/2048L, id->buf_size/2,
+ drive->select.b.lba ? "LBA, " : "",
+ drive->bios_cyl, drive->bios_head, drive->bios_sect);
+
+ drive->mult_count = 0;
+ if (id->max_multsect) {
+ drive->mult_req = INITIAL_MULT_COUNT;
+ if (drive->mult_req > id->max_multsect)
+ drive->mult_req = id->max_multsect;
+ if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect))
+ drive->special.b.set_multmode = 1;
+ }
+ if (HWIF(drive)->dmaproc != NULL) { /* hwif supports DMA? */
+ if (!(HWIF(drive)->dmaproc(ide_dma_check, drive)))
+ printk(", DMA");
+ }
+ printk("\n");
+}
+
+/*
+ * Delay for *at least* 10ms. As we don't know how much time is left
+ * until the next tick occurs, we wait an extra tick to be safe.
+ * This is used only during the probing/polling for drives at boot time.
+ */
+static void delay_10ms (void)
+{
+ unsigned long timer = jiffies + (HZ + 99)/100 + 1;
+ while (timer > jiffies);
+}
+
+/*
+ * try_to_identify() sends an ATA(PI) IDENTIFY request to a drive
+ * and waits for a response. It also monitors irqs while this is
+ * happening, in hope of automatically determining which one is
+ * being used by the interface.
+ *
+ * Returns: 0 device was identified
+ * 1 device timed-out (no response to identify request)
+ * 2 device aborted the command (refused to identify itself)
+ */
+static int try_to_identify (ide_drive_t *drive, byte cmd)
+{
+ int hd_status, rc;
+ unsigned long timeout;
+ int irqs = 0;
+
+ if (!HWIF(drive)->irq) { /* already got an IRQ? */
+ probe_irq_off(probe_irq_on()); /* clear dangling irqs */
+ irqs = probe_irq_on(); /* start monitoring irqs */
+ OUT_BYTE(drive->ctl,IDE_CONTROL_REG); /* enable device irq */
+ }
+
+ delay_10ms(); /* take a deep breath */
+ if ((IN_BYTE(IDE_ALTSTATUS_REG) ^ IN_BYTE(IDE_STATUS_REG)) & ~INDEX_STAT) {
+ printk("%s: probing with STATUS instead of ALTSTATUS\n", drive->name);
+ hd_status = IDE_STATUS_REG; /* ancient Seagate drives */
+ } else
+ hd_status = IDE_ALTSTATUS_REG; /* use non-intrusive polling */
+
+ OUT_BYTE(cmd,IDE_COMMAND_REG); /* ask drive for ID */
+ timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
+ timeout += jiffies;
+ do {
+ if (jiffies > timeout) {
+ if (!HWIF(drive)->irq)
+ (void) probe_irq_off(irqs);
+ return 1; /* drive timed-out */
+ }
+ delay_10ms(); /* give drive a breather */
+ } while (IN_BYTE(hd_status) & BUSY_STAT);
+
+ delay_10ms(); /* wait for IRQ and DRQ_STAT */
+ if (OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT)) {
+ cli(); /* some systems need this */
+ do_identify(drive, cmd); /* drive returned ID */
+ if (drive->present && drive->media != ide_tape) {
+ ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
+ if (tuneproc != NULL && drive->autotune == 1)
+ tuneproc(drive, 255); /* auto-tune PIO mode */
+ }
+ rc = 0; /* drive responded with ID */
+ } else
+ rc = 2; /* drive refused ID */
+ if (!HWIF(drive)->irq) {
+ irqs = probe_irq_off(irqs); /* get irq number */
+ if (irqs > 0)
+ HWIF(drive)->irq = irqs;
+ else /* Mmmm.. multiple IRQs */
+ printk("%s: IRQ probe failed (%d)\n", drive->name, irqs);
+ }
+ return rc;
+}
+
+/*
+ * do_probe() has the difficult job of finding a drive if it exists,
+ * without getting hung up if it doesn't exist, without trampling on
+ * ethernet cards, and without leaving any IRQs dangling to haunt us later.
+ *
+ * If a drive is "known" to exist (from CMOS or kernel parameters),
+ * but does not respond right away, the probe will "hang in there"
+ * for the maximum wait time (about 30 seconds), otherwise it will
+ * exit much more quickly.
+ *
+ * Returns: 0 device was identified
+ * 1 device timed-out (no response to identify request)
+ * 2 device aborted the command (refused to identify itself)
+ * 3 bad status from device (possible for ATAPI drives)
+ * 4 probe was not attempted because failure was obvious
+ */
+static int do_probe (ide_drive_t *drive, byte cmd)
+{
+ int rc;
+#ifdef CONFIG_BLK_DEV_IDEATAPI
+ if (drive->present) { /* avoid waiting for inappropriate probes */
+ if ((drive->media != ide_disk) && (cmd == WIN_IDENTIFY))
+ return 4;
+ }
+#endif /* CONFIG_BLK_DEV_IDEATAPI */
+#ifdef DEBUG
+ printk("probing for %s: present=%d, media=%d, probetype=%s\n",
+ drive->name, drive->present, drive->media,
+ (cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI");
+#endif
+#ifdef CONFIG_BLK_DEV_HT6560B
+ if (HWIF(drive)->selectproc)
+ HWIF(drive)->selectproc (drive);
+#endif /* CONFIG_BLK_DEV_HT6560B */
+ OUT_BYTE(drive->select.all,IDE_SELECT_REG); /* select target drive */
+ delay_10ms(); /* wait for BUSY_STAT */
+ if (IN_BYTE(IDE_SELECT_REG) != drive->select.all && !drive->present) {
+ OUT_BYTE(0xa0,IDE_SELECT_REG); /* exit with drive0 selected */
+ return 3; /* no i/f present: avoid killing ethernet cards */
+ }
+
+ if (OK_STAT(GET_STAT(),READY_STAT,BUSY_STAT)
+ || drive->present || cmd == WIN_PIDENTIFY)
+ {
+ if ((rc = try_to_identify(drive,cmd))) /* send cmd and wait */
+ rc = try_to_identify(drive,cmd); /* failed: try again */
+ if (rc == 1)
+ printk("%s: no response (status = 0x%02x)\n", drive->name, GET_STAT());
+ (void) GET_STAT(); /* ensure drive irq is clear */
+ } else {
+ rc = 3; /* not present or maybe ATAPI */
+ }
+ if (drive->select.b.unit != 0) {
+ OUT_BYTE(0xa0,IDE_SELECT_REG); /* exit with drive0 selected */
+ delay_10ms();
+ (void) GET_STAT(); /* ensure drive irq is clear */
+ }
+ return rc;
+}
+
+/*
+ * probe_for_drive() tests for existance of a given drive using do_probe().
+ *
+ * Returns: 0 no device was found
+ * 1 device was found (note: drive->present might still be 0)
+ */
+static inline byte probe_for_drive (ide_drive_t *drive)
+{
+ if (drive->noprobe) /* skip probing? */
+ return drive->present;
+ if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */
+#ifdef CONFIG_BLK_DEV_IDEATAPI
+ (void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
+#endif /* CONFIG_BLK_DEV_IDEATAPI */
+ }
+ if (!drive->present)
+ return 0; /* drive not found */
+ if (drive->id == NULL) { /* identification failed? */
+ if (drive->media == ide_disk) {
+ printk ("%s: non-IDE drive, CHS=%d/%d/%d\n",
+ drive->name, drive->cyl, drive->head, drive->sect);
+ }
+#ifdef CONFIG_BLK_DEV_IDECD
+ else if (drive->media == ide_cdrom) {
+ printk("%s: ATAPI cdrom (?)\n", drive->name);
+ }
+#endif /* CONFIG_BLK_DEV_IDECD */
+ else {
+ drive->present = 0; /* nuke it */
+ return 1; /* drive was found */
+ }
+ }
+ if (drive->media == ide_disk && !drive->select.b.lba) {
+ if (!drive->head || drive->head > 16) {
+ printk("%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n",
+ drive->name, drive->head);
+ drive->present = 0;
+ }
+ }
+ return 1; /* drive was found */
+}
+
+/*
+ * This routine only knows how to look for drive units 0 and 1
+ * on an interface, so any setting of MAX_DRIVES > 2 won't work here.
+ */
+static void probe_for_drives (ide_hwif_t *hwif)
+{
+ unsigned int unit;
+
+ if (check_region(hwif->io_base,8) || check_region(hwif->ctl_port,1)) {
+ int msgout = 0;
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ if (drive->present) {
+ drive->present = 0;
+ printk("%s: ERROR, PORTS ALREADY IN USE\n", drive->name);
+ msgout = 1;
+ }
+ }
+ if (!msgout)
+ printk("%s: ports already in use, skipping probe\n", hwif->name);
+ } else {
+ unsigned long flags;
+ save_flags(flags);
+
+#if (MAX_DRIVES > 2)
+ printk("%s: probing for first 2 of %d possible drives\n", hwif->name, MAX_DRIVES);
+#endif
+ sti(); /* needed for jiffies and irq probing */
+ /*
+ * Second drive should only exist if first drive was found,
+ * but a lot of cdrom drives seem to be configured as slave-only
+ */
+ for (unit = 0; unit < 2; ++unit) { /* note the hardcoded '2' */
+ ide_drive_t *drive = &hwif->drives[unit];
+ (void) probe_for_drive (drive);
+ }
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ if (drive->present) {
+ hwif->present = 1;
+ request_region(hwif->io_base, 8, hwif->name);
+ request_region(hwif->ctl_port, 1, hwif->name);
+ break;
+ }
+ }
+ restore_flags(flags);
+ }
+}
+
+/*
+ * stridx() returns the offset of c within s,
+ * or -1 if c is '\0' or not found within s.
+ */
+static int stridx (const char *s, char c)
+{
+ char *i = strchr(s, c);
+ return (i && c) ? i - s : -1;
+}
+
+/*
+ * match_parm() does parsing for ide_setup():
+ *
+ * 1. the first char of s must be '='.
+ * 2. if the remainder matches one of the supplied keywords,
+ * the index (1 based) of the keyword is negated and returned.
+ * 3. if the remainder is a series of no more than max_vals numbers
+ * separated by commas, the numbers are saved in vals[] and a
+ * count of how many were saved is returned. Base10 is assumed,
+ * and base16 is allowed when prefixed with "0x".
+ * 4. otherwise, zero is returned.
+ */
+static int match_parm (char *s, const char *keywords[], int vals[], int max_vals)
+{
+ static const char *decimal = "0123456789";
+ static const char *hex = "0123456789abcdef";
+ int i, n;
+
+ if (*s++ == '=') {
+ /*
+ * Try matching against the supplied keywords,
+ * and return -(index+1) if we match one
+ */
+ for (i = 0; *keywords != NULL; ++i) {
+ if (!strcmp(s, *keywords++))
+ return -(i+1);
+ }
+ /*
+ * Look for a series of no more than "max_vals"
+ * numeric values separated by commas, in base10,
+ * or base16 when prefixed with "0x".
+ * Return a count of how many were found.
+ */
+ for (n = 0; (i = stridx(decimal, *s)) >= 0;) {
+ vals[n] = i;
+ while ((i = stridx(decimal, *++s)) >= 0)
+ vals[n] = (vals[n] * 10) + i;
+ if (*s == 'x' && !vals[n]) {
+ while ((i = stridx(hex, *++s)) >= 0)
+ vals[n] = (vals[n] * 0x10) + i;
+ }
+ if (++n == max_vals)
+ break;
+ if (*s == ',')
+ ++s;
+ }
+ if (!*s)
+ return n;
+ }
+ return 0; /* zero = nothing matched */
+}
+
+/*
+ * ide_setup() gets called VERY EARLY during initialization,
+ * to handle kernel "command line" strings beginning with "hdx="
+ * or "ide". Here is the complete set currently supported:
+ *
+ * "hdx=" is recognized for all "x" from "a" to "h", such as "hdc".
+ * "idex=" is recognized for all "x" from "0" to "3", such as "ide1".
+ *
+ * "hdx=noprobe" : drive may be present, but do not probe for it
+ * "hdx=nowerr" : ignore the WRERR_STAT bit on this drive
+ * "hdx=cdrom" : drive is present, and is a cdrom drive
+ * "hdx=cyl,head,sect" : disk drive is present, with specified geometry
+ * "hdx=autotune" : driver will attempt to tune interface speed
+ * to the fastest PIO mode supported,
+ * if possible for this drive only.
+ * Not fully supported by all chipset types,
+ * and quite likely to cause trouble with
+ * older/odd IDE drives.
+ *
+ * "idex=noprobe" : do not attempt to access/use this interface
+ * "idex=base" : probe for an interface at the addr specified,
+ * where "base" is usually 0x1f0 or 0x170
+ * and "ctl" is assumed to be "base"+0x206
+ * "idex=base,ctl" : specify both base and ctl
+ * "idex=base,ctl,irq" : specify base, ctl, and irq number
+ * "idex=autotune" : driver will attempt to tune interface speed
+ * to the fastest PIO mode supported,
+ * for all drives on this interface.
+ * Not fully supported by all chipset types,
+ * and quite likely to cause trouble with
+ * older/odd IDE drives.
+ * "idex=noautotune" : driver will NOT attempt to tune interface speed
+ * This is the default for most chipsets,
+ * except the cmd640.
+ *
+ * The following two are valid ONLY on ide0,
+ * and the defaults for the base,ctl ports must not be altered.
+ *
+ * "ide0=serialize" : do not overlap operations on ide0 and ide1.
+ * "ide0=dtc2278" : probe/support DTC2278 interface
+ * "ide0=ht6560b" : probe/support HT6560B interface
+ * "ide0=cmd640_vlb" : *REQUIRED* for VLB cards with the CMD640 chip
+ * (not for PCI -- automatically detected)
+ * "ide0=qd6580" : probe/support qd6580 interface
+ * "ide0=ali14xx" : probe/support ali14xx chipsets (ALI M1439, M1443, M1445)
+ * "ide0=umc8672" : probe/support umc8672 chipsets
+ */
+void ide_setup (char *s)
+{
+ int i, vals[3];
+ ide_hwif_t *hwif;
+ ide_drive_t *drive;
+ unsigned int hw, unit;
+ const char max_drive = 'a' + ((MAX_HWIFS * MAX_DRIVES) - 1);
+ const char max_hwif = '0' + (MAX_HWIFS - 1);
+
+ printk("ide_setup: %s", s);
+ init_ide_data ();
+
+ /*
+ * Look for drive options: "hdx="
+ */
+ if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) {
+ const char *hd_words[] = {"noprobe", "nowerr", "cdrom", "serialize",
+ "autotune", "noautotune", NULL};
+ unit = s[2] - 'a';
+ hw = unit / MAX_DRIVES;
+ unit = unit % MAX_DRIVES;
+ hwif = &ide_hwifs[hw];
+ drive = &hwif->drives[unit];
+ switch (match_parm(&s[3], hd_words, vals, 3)) {
+ case -1: /* "noprobe" */
+ drive->noprobe = 1;
+ goto done;
+ case -2: /* "nowerr" */
+ drive->bad_wstat = BAD_R_STAT;
+ hwif->noprobe = 0;
+ goto done;
+ case -3: /* "cdrom" */
+ drive->present = 1;
+ drive->media = ide_cdrom;
+ hwif->noprobe = 0;
+ goto done;
+ case -4: /* "serialize" */
+ printk(" -- USE \"ide%c=serialize\" INSTEAD", '0'+hw);
+ goto do_serialize;
+ case -5: /* "autotune" */
+ drive->autotune = 1;
+ goto done;
+ case -6: /* "noautotune" */
+ drive->autotune = 2;
+ goto done;
+ case 3: /* cyl,head,sect */
+ drive->media = ide_disk;
+ drive->cyl = drive->bios_cyl = vals[0];
+ drive->head = drive->bios_head = vals[1];
+ drive->sect = drive->bios_sect = vals[2];
+ drive->present = 1;
+ drive->forced_geom = 1;
+ hwif->noprobe = 0;
+ goto done;
+ default:
+ goto bad_option;
+ }
+ }
+ /*
+ * Look for interface options: "idex="
+ */
+ if (s[0] == 'i' && s[1] == 'd' && s[2] == 'e' && s[3] >= '0' && s[3] <= max_hwif) {
+ /*
+ * Be VERY CAREFUL changing this: note hardcoded indexes below
+ */
+ const char *ide_words[] = {"noprobe", "serialize", "autotune", "noautotune",
+ "qd6580", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", NULL};
+ hw = s[3] - '0';
+ hwif = &ide_hwifs[hw];
+ i = match_parm(&s[4], ide_words, vals, 3);
+
+ /*
+ * Cryptic check to ensure chipset not already set for hwif:
+ */
+ if (i != -1 && i != -2) {
+ if (hwif->chipset != ide_unknown)
+ goto bad_option;
+ if (i < 0 && ide_hwifs[1].chipset != ide_unknown)
+ goto bad_option;
+ }
+ /*
+ * Interface keywords work only for ide0:
+ */
+ if (i <= -6 && hw != 0)
+ goto bad_hwif;
+
+ switch (i) {
+#ifdef CONFIG_BLK_DEV_ALI14XX
+ case -10: /* "ali14xx" */
+ {
+ extern void init_ali14xx (void);
+ init_ali14xx();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_ALI14XX */
+#ifdef CONFIG_BLK_DEV_UMC8672
+ case -9: /* "umc8672" */
+ {
+ extern void init_umc8672 (void);
+ init_umc8672();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_UMC8672 */
+#ifdef CONFIG_BLK_DEV_DTC2278
+ case -8: /* "dtc2278" */
+ {
+ extern void init_dtc2278 (void);
+ init_dtc2278();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_DTC2278 */
+#ifdef CONFIG_BLK_DEV_CMD640
+ case -7: /* "cmd640_vlb" */
+ {
+ extern int cmd640_vlb; /* flag for cmd640.c */
+ cmd640_vlb = 1;
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_CMD640 */
+#ifdef CONFIG_BLK_DEV_HT6560B
+ case -6: /* "ht6560b" */
+ {
+ extern void init_ht6560b (void);
+ init_ht6560b();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_HT6560B */
+#if CONFIG_BLK_DEV_QD6580
+ case -5: /* "qd6580" (no secondary i/f) */
+ {
+ extern void init_qd6580 (void);
+ init_qd6580();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_QD6580 */
+ case -4: /* "noautotune" */
+ hwif->drives[0].autotune = 2;
+ hwif->drives[1].autotune = 2;
+ goto done;
+ case -3: /* "autotune" */
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
+ goto done;
+ case -2: /* "serialize" */
+ do_serialize:
+ if (hw > 1) goto bad_hwif;
+ ide_hwifs[0].serialized = 1;
+ goto done;
+
+ case -1: /* "noprobe" */
+ hwif->noprobe = 1;
+ goto done;
+
+ case 1: /* base */
+ vals[1] = vals[0] + 0x206; /* default ctl */
+ case 2: /* base,ctl */
+ vals[2] = 0; /* default irq = probe for it */
+ case 3: /* base,ctl,irq */
+ hwif->io_base = vals[0];
+ hwif->ctl_port = vals[1];
+ hwif->irq = vals[2];
+ hwif->noprobe = 0;
+ hwif->chipset = ide_generic;
+ goto done;
+
+ case 0: goto bad_option;
+ default:
+ printk(" -- SUPPORT NOT CONFIGURED IN THIS KERNEL\n");
+ return;
+ }
+ }
+bad_option:
+ printk(" -- BAD OPTION\n");
+ return;
+bad_hwif:
+ printk("-- NOT SUPPORTED ON ide%d", hw);
+done:
+ printk("\n");
+}
+
+/*
+ * This routine is called from the partition-table code in genhd.c
+ * to "convert" a drive to a logical geometry with fewer than 1024 cyls.
+ *
+ * The second parameter, "xparm", determines exactly how the translation
+ * will be handled:
+ * 0 = convert to CHS with fewer than 1024 cyls
+ * using the same method as Ontrack DiskManager.
+ * 1 = same as "0", plus offset everything by 63 sectors.
+ * -1 = similar to "0", plus redirect sector 0 to sector 1.
+ * >1 = convert to a CHS geometry with "xparm" heads.
+ *
+ * Returns 0 if the translation was not possible, if the device was not
+ * an IDE disk drive, or if a geometry was "forced" on the commandline.
+ * Returns 1 if the geometry translation was successful.
+ */
+int ide_xlate_1024 (kdev_t i_rdev, int xparm, const char *msg)
+{
+ ide_drive_t *drive;
+ static const byte head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0};
+ const byte *heads = head_vals;
+ unsigned long tracks;
+
+ if ((drive = get_info_ptr(i_rdev)) == NULL || drive->forced_geom)
+ return 0;
+
+ if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63)
+ return 0; /* we already have a translation */
+
+ printk("%s ", msg);
+
+ if (drive->id) {
+ drive->cyl = drive->id->cyls;
+ drive->head = drive->id->heads;
+ drive->sect = drive->id->sectors;
+ }
+ drive->bios_cyl = drive->cyl;
+ drive->bios_head = drive->head;
+ drive->bios_sect = drive->sect;
+ drive->special.b.set_geometry = 1;
+
+ tracks = drive->bios_cyl * drive->bios_head * drive->bios_sect / 63;
+ drive->bios_sect = 63;
+ if (xparm > 1) {
+ drive->bios_head = xparm;
+ drive->bios_cyl = tracks / drive->bios_head;
+ } else {
+ while (drive->bios_cyl >= 1024) {
+ drive->bios_head = *heads;
+ drive->bios_cyl = tracks / drive->bios_head;
+ if (0 == *++heads)
+ break;
+ }
+#if FAKE_FDISK_FOR_EZDRIVE
+ if (xparm == -1) {
+ drive->remap_0_to_1 = 1;
+ msg = "0->1";
+ } else
+#endif /* FAKE_FDISK_FOR_EZDRIVE */
+ if (xparm == 1) {
+ drive->sect0 = 63;
+ drive->bios_cyl = (tracks - 1) / drive->bios_head;
+ msg = "+63";
+ }
+ printk("[remap %s] ", msg);
+ }
+ drive->part[0].nr_sects = current_capacity(drive);
+ printk("[%d/%d/%d]", drive->bios_cyl, drive->bios_head, drive->bios_sect);
+ return 1;
+}
+
+/*
+ * We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc
+ * controller that is BIOS compatible with ST-506, and thus showing up in our
+ * BIOS table, but not register compatible, and therefore not present in CMOS.
+ *
+ * Furthermore, we will assume that our ST-506 drives <if any> are the primary
+ * drives in the system -- the ones reflected as drive 1 or 2. The first
+ * drive is stored in the high nibble of CMOS byte 0x12, the second in the low
+ * nibble. This will be either a 4 bit drive type or 0xf indicating use byte
+ * 0x19 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. A non-zero value
+ * means we have an AT controller hard disk for that drive.
+ *
+ * Of course, there is no guarantee that either drive is actually on the
+ * "primary" IDE interface, but we don't bother trying to sort that out here.
+ * If a drive is not actually on the primary interface, then these parameters
+ * will be ignored. This results in the user having to supply the logical
+ * drive geometry as a boot parameter for each drive not on the primary i/f.
+ *
+ * The only "perfect" way to handle this would be to modify the setup.[cS] code
+ * to do BIOS calls Int13h/Fn08h and Int13h/Fn48h to get all of the drive info
+ * for us during initialization. I have the necessary docs -- any takers? -ml
+ */
+
+static void probe_cmos_for_drives (ide_hwif_t *hwif)
+{
+#ifdef __i386__
+ extern struct drive_info_struct drive_info;
+ byte cmos_disks, *BIOS = (byte *) &drive_info;
+ int unit;
+
+ outb_p(0x12,0x70); /* specify CMOS address 0x12 */
+ cmos_disks = inb_p(0x71); /* read the data from 0x12 */
+ /* Extract drive geometry from CMOS+BIOS if not already setup */
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ if ((cmos_disks & (0xf0 >> (unit*4))) && !drive->present) {
+ drive->cyl = drive->bios_cyl = *(unsigned short *)BIOS;
+ drive->head = drive->bios_head = *(BIOS+2);
+ drive->sect = drive->bios_sect = *(BIOS+14);
+ drive->ctl = *(BIOS+8);
+ drive->present = 1;
+ }
+ BIOS += 16;
+ }
+#endif
+}
+
+/*
+ * This routine sets up the irq for an ide interface, and creates a new
+ * hwgroup for the irq/hwif if none was previously assigned.
+ *
+ * The SA_INTERRUPT in sa_flags means ide_intr() is always entered with
+ * interrupts completely disabled. This can be bad for interrupt latency,
+ * but anything else has led to problems on some machines. We re-enable
+ * interrupts as much as we can safely do in most places.
+ */
+static int init_irq (ide_hwif_t *hwif)
+{
+ unsigned long flags;
+ int irq = hwif->irq;
+ ide_hwgroup_t *hwgroup = irq_to_hwgroup[irq];
+
+ save_flags(flags);
+ cli();
+
+ /*
+ * Grab the irq if we don't already have it from a previous hwif
+ */
+ if (hwgroup == NULL) {
+ if (request_irq(irq, ide_intr, SA_INTERRUPT|SA_SAMPLE_RANDOM, hwif->name)) {
+ restore_flags(flags);
+ printk(" -- FAILED!");
+ return 1;
+ }
+ }
+ /*
+ * Check for serialization with ide1.
+ * This code depends on us having already taken care of ide1.
+ */
+ if (hwif->serialized && hwif->name[3] == '0' && ide_hwifs[1].present)
+ hwgroup = ide_hwifs[1].hwgroup;
+ /*
+ * If this is the first interface in a group,
+ * then we need to create the hwgroup structure
+ */
+ if (hwgroup == NULL) {
+ hwgroup = kmalloc (sizeof(ide_hwgroup_t), GFP_KERNEL);
+ hwgroup->hwif = hwif->next = hwif;
+ hwgroup->rq = NULL;
+ hwgroup->handler = NULL;
+ hwgroup->drive = &hwif->drives[0];
+ hwgroup->poll_timeout = 0;
+ init_timer(&hwgroup->timer);
+ hwgroup->timer.function = &timer_expiry;
+ hwgroup->timer.data = (unsigned long) hwgroup;
+ } else {
+ hwif->next = hwgroup->hwif->next;
+ hwgroup->hwif->next = hwif;
+ }
+ hwif->hwgroup = hwgroup;
+ irq_to_hwgroup[irq] = hwgroup;
+
+ restore_flags(flags); /* safe now that hwif->hwgroup is set up */
+
+ printk("%s at 0x%03x-0x%03x,0x%03x on irq %d", hwif->name,
+ hwif->io_base, hwif->io_base+7, hwif->ctl_port, irq);
+ if (hwgroup->hwif != hwif)
+ printk(" (serialized with %s)", hwgroup->hwif->name);
+ printk("\n");
+ return 0;
+}
+
+static struct file_operations ide_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ ide_ioctl, /* ioctl */
+ NULL, /* mmap */
+ ide_open, /* open */
+ ide_release, /* release */
+ block_fsync /* fsync */
+ ,NULL, /* fasync */
+ ide_check_media_change, /* check_media_change */
+ revalidate_disk /* revalidate */
+};
+
+#ifdef CONFIG_PCI
+#if defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON)
+
+typedef void (ide_pci_init_proc_t)(byte, byte);
+
+/*
+ * ide_probe_pci() scans PCI for a specific vendor/device function,
+ * and invokes the supplied init routine for each instance detected.
+ */
+static void ide_probe_pci (unsigned short vendor, unsigned short device, ide_pci_init_proc_t *init, int func_adj)
+{
+ unsigned long flags;
+ unsigned index;
+ byte fn, bus;
+
+ save_flags(flags);
+ cli();
+ for (index = 0; !pcibios_find_device (vendor, device, index, &bus, &fn); ++index) {
+ init (bus, fn + func_adj);
+ }
+ restore_flags(flags);
+}
+
+#endif /* defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON) */
+#endif /* CONFIG_PCI */
+
+/*
+ * ide_init_pci() finds/initializes "known" PCI IDE interfaces
+ *
+ * This routine should ideally be using pcibios_find_class() to find
+ * all IDE interfaces, but that function causes some systems to "go weird".
+ */
+static void probe_for_hwifs (void)
+{
+#ifdef CONFIG_PCI
+ /*
+ * Find/initialize PCI IDE interfaces
+ */
+ if (pcibios_present()) {
+#ifdef CONFIG_BLK_DEV_RZ1000
+ ide_pci_init_proc_t init_rz1000;
+ ide_probe_pci (PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, &init_rz1000, 0);
+#endif /* CONFIG_BLK_DEV_RZ1000 */
+#ifdef CONFIG_BLK_DEV_TRITON
+ /*
+ * Apparently the BIOS32 services on Intel motherboards are
+ * buggy and won't find the PCI_DEVICE_ID_INTEL_82371_1 for us.
+ * So instead, we search for PCI_DEVICE_ID_INTEL_82371_0,
+ * and then add 1.
+ */
+ ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371_0, &ide_init_triton, 1);
+#endif /* CONFIG_BLK_DEV_TRITON */
+ }
+#endif /* CONFIG_PCI */
+#ifdef CONFIG_BLK_DEV_CMD640
+ {
+ extern void ide_probe_for_cmd640x (void);
+ ide_probe_for_cmd640x();
+ }
+#endif
+}
+
+/*
+ * This is gets invoked once during initialization, to set *everything* up
+ */
+int ide_init (void)
+{
+ int h;
+
+ init_ide_data ();
+ /*
+ * Probe for special "known" interface chipsets
+ */
+ probe_for_hwifs ();
+
+ /*
+ * Probe for drives in the usual way.. CMOS/BIOS, then poke at ports
+ */
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ ide_hwif_t *hwif = &ide_hwifs[h];
+ if (!hwif->noprobe) {
+ if (hwif->io_base == HD_DATA)
+ probe_cmos_for_drives (hwif);
+ probe_for_drives (hwif);
+ }
+ if (hwif->present) {
+ if (!hwif->irq) {
+ if (!(hwif->irq = default_irqs[h])) {
+ printk("%s: DISABLED, NO IRQ\n", hwif->name);
+ hwif->present = 0;
+ continue;
+ }
+ }
+#ifdef CONFIG_BLK_DEV_HD
+ if (hwif->irq == HD_IRQ && hwif->io_base != HD_DATA) {
+ printk("%s: CANNOT SHARE IRQ WITH OLD HARDDISK DRIVER (hd.c)\n", hwif->name);
+ hwif->present = 0;
+ }
+#endif /* CONFIG_BLK_DEV_HD */
+ }
+ }
+
+ /*
+ * Now we try to set up irqs and major devices for what was found
+ */
+ for (h = MAX_HWIFS-1; h >= 0; --h) {
+ void (*rfn)(void);
+ ide_hwif_t *hwif = &ide_hwifs[h];
+ if (!hwif->present)
+ continue;
+ hwif->present = 0; /* we set it back to 1 if all is ok below */
+ switch (hwif->major) {
+ case IDE0_MAJOR: rfn = &do_ide0_request; break;
+ case IDE1_MAJOR: rfn = &do_ide1_request; break;
+ case IDE2_MAJOR: rfn = &do_ide2_request; break;
+ case IDE3_MAJOR: rfn = &do_ide3_request; break;
+ default:
+ printk("%s: request_fn NOT DEFINED\n", hwif->name);
+ continue;
+ }
+ if (register_blkdev (hwif->major, hwif->name, &ide_fops)) {
+ printk("%s: UNABLE TO GET MAJOR NUMBER %d\n", hwif->name, hwif->major);
+ } else if (init_irq (hwif)) {
+ printk("%s: UNABLE TO GET IRQ %d\n", hwif->name, hwif->irq);
+ (void) unregister_blkdev (hwif->major, hwif->name);
+ } else {
+ init_gendisk(hwif);
+ blk_dev[hwif->major].request_fn = rfn;
+ read_ahead[hwif->major] = 8; /* (4kB) */
+ hwif->present = 1; /* success */
+ }
+ }
+
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ idetape_register_chrdev(); /* Register character device interface to the ide tape */
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+
+ return 0;
+}
diff --git a/i386/i386at/gpl/linux/block/ide.h b/i386/i386at/gpl/linux/block/ide.h
new file mode 100644
index 0000000..b1ebc4c
--- /dev/null
+++ b/i386/i386at/gpl/linux/block/ide.h
@@ -0,0 +1,655 @@
+/*
+ * linux/drivers/block/ide.h
+ *
+ * Copyright (C) 1994, 1995 Linus Torvalds & authors
+ */
+
+#include <linux/config.h>
+
+/*
+ * This is the multiple IDE interface driver, as evolved from hd.c.
+ * It supports up to four IDE interfaces, on one or more IRQs (usually 14 & 15).
+ * There can be up to two drives per interface, as per the ATA-2 spec.
+ *
+ * Primary i/f: ide0: major=3; (hda) minor=0; (hdb) minor=64
+ * Secondary i/f: ide1: major=22; (hdc or hd1a) minor=0; (hdd or hd1b) minor=64
+ * Tertiary i/f: ide2: major=33; (hde) minor=0; (hdf) minor=64
+ * Quaternary i/f: ide3: major=34; (hdg) minor=0; (hdh) minor=64
+ */
+
+/******************************************************************************
+ * IDE driver configuration options (play with these as desired):
+ *
+ * REALLY_SLOW_IO can be defined in ide.c and ide-cd.c, if necessary
+ */
+#undef REALLY_FAST_IO /* define if ide ports are perfect */
+#define INITIAL_MULT_COUNT 0 /* off=0; on=2,4,8,16,32, etc.. */
+
+#ifndef SUPPORT_VLB_SYNC /* 1 to support weird 32-bit chips */
+#define SUPPORT_VLB_SYNC 1 /* 0 to reduce kernel size */
+#endif
+#ifndef DISK_RECOVERY_TIME /* off=0; on=access_delay_time */
+#define DISK_RECOVERY_TIME 0 /* for hardware that needs it */
+#endif
+#ifndef OK_TO_RESET_CONTROLLER /* 1 needed for good error recovery */
+#define OK_TO_RESET_CONTROLLER 1 /* 0 for use with AH2372A/B interface */
+#endif
+#ifndef FAKE_FDISK_FOR_EZDRIVE /* 1 to help linux fdisk with EZDRIVE */
+#define FAKE_FDISK_FOR_EZDRIVE 1 /* 0 to reduce kernel size */
+#endif
+#ifndef FANCY_STATUS_DUMPS /* 1 for human-readable drive errors */
+#define FANCY_STATUS_DUMPS 1 /* 0 to reduce kernel size */
+#endif
+
+#if defined(CONFIG_BLK_DEV_IDECD) || defined(CONFIG_BLK_DEV_IDETAPE)
+#define CONFIG_BLK_DEV_IDEATAPI 1
+#endif
+
+/*
+ * IDE_DRIVE_CMD is used to implement many features of the hdparm utility
+ */
+#define IDE_DRIVE_CMD 99 /* (magic) undef to reduce kernel size*/
+
+/*
+ * "No user-serviceable parts" beyond this point :)
+ *****************************************************************************/
+
+typedef unsigned char byte; /* used everywhere */
+
+/*
+ * Probably not wise to fiddle with these
+ */
+#define ERROR_MAX 8 /* Max read/write errors per sector */
+#define ERROR_RESET 3 /* Reset controller every 4th retry */
+#define ERROR_RECAL 1 /* Recalibrate every 2nd retry */
+
+/*
+ * Ensure that various configuration flags have compatible settings
+ */
+#ifdef REALLY_SLOW_IO
+#undef REALLY_FAST_IO
+#endif
+
+/*
+ * Definitions for accessing IDE controller registers
+ */
+
+#define HWIF(drive) ((ide_hwif_t *)drive->hwif)
+#define HWGROUP(drive) ((ide_hwgroup_t *)(HWIF(drive)->hwgroup))
+
+#define IDE_DATA_OFFSET (0)
+#define IDE_ERROR_OFFSET (1)
+#define IDE_NSECTOR_OFFSET (2)
+#define IDE_SECTOR_OFFSET (3)
+#define IDE_LCYL_OFFSET (4)
+#define IDE_HCYL_OFFSET (5)
+#define IDE_SELECT_OFFSET (6)
+#define IDE_STATUS_OFFSET (7)
+#define IDE_FEATURE_OFFSET IDE_ERROR_OFFSET
+#define IDE_COMMAND_OFFSET IDE_STATUS_OFFSET
+
+#define IDE_DATA_REG (HWIF(drive)->io_base+IDE_DATA_OFFSET)
+#define IDE_ERROR_REG (HWIF(drive)->io_base+IDE_ERROR_OFFSET)
+#define IDE_NSECTOR_REG (HWIF(drive)->io_base+IDE_NSECTOR_OFFSET)
+#define IDE_SECTOR_REG (HWIF(drive)->io_base+IDE_SECTOR_OFFSET)
+#define IDE_LCYL_REG (HWIF(drive)->io_base+IDE_LCYL_OFFSET)
+#define IDE_HCYL_REG (HWIF(drive)->io_base+IDE_HCYL_OFFSET)
+#define IDE_SELECT_REG (HWIF(drive)->io_base+IDE_SELECT_OFFSET)
+#define IDE_STATUS_REG (HWIF(drive)->io_base+IDE_STATUS_OFFSET)
+#define IDE_CONTROL_REG (HWIF(drive)->ctl_port)
+#define IDE_FEATURE_REG IDE_ERROR_REG
+#define IDE_COMMAND_REG IDE_STATUS_REG
+#define IDE_ALTSTATUS_REG IDE_CONTROL_REG
+
+#ifdef REALLY_FAST_IO
+#define OUT_BYTE(b,p) outb((b),p)
+#define IN_BYTE(p) (byte)inb(p)
+#else
+#define OUT_BYTE(b,p) outb_p((b),p)
+#define IN_BYTE(p) (byte)inb_p(p)
+#endif /* REALLY_FAST_IO */
+
+#define GET_ERR() IN_BYTE(IDE_ERROR_REG)
+#define GET_STAT() IN_BYTE(IDE_STATUS_REG)
+#define OK_STAT(stat,good,bad) (((stat)&((good)|(bad)))==(good))
+#define BAD_R_STAT (BUSY_STAT | ERR_STAT)
+#define BAD_W_STAT (BAD_R_STAT | WRERR_STAT)
+#define BAD_STAT (BAD_R_STAT | DRQ_STAT)
+#define DRIVE_READY (READY_STAT | SEEK_STAT)
+#define DATA_READY (DRIVE_READY | DRQ_STAT)
+
+/*
+ * Some more useful definitions
+ */
+#define IDE_MAJOR_NAME "ide" /* the same for all i/f; see also genhd.c */
+#define MAJOR_NAME IDE_MAJOR_NAME
+#define PARTN_BITS 6 /* number of minor dev bits for partitions */
+#define PARTN_MASK ((1<<PARTN_BITS)-1) /* a useful bit mask */
+#define MAX_DRIVES 2 /* per interface; 2 assumed by lots of code */
+#define MAX_HWIFS 4 /* an arbitrary, but realistic limit */
+#define SECTOR_WORDS (512 / 4) /* number of 32bit words per sector */
+
+/*
+ * Timeouts for various operations:
+ */
+#define WAIT_DRQ (5*HZ/100) /* 50msec - spec allows up to 20ms */
+#define WAIT_READY (3*HZ/100) /* 30msec - should be instantaneous */
+#define WAIT_PIDENTIFY (1*HZ) /* 1sec - should be less than 3ms (?) */
+#define WAIT_WORSTCASE (30*HZ) /* 30sec - worst case when spinning up */
+#define WAIT_CMD (10*HZ) /* 10sec - maximum wait for an IRQ to happen */
+
+#ifdef CONFIG_BLK_DEV_IDETAPE
+#include "ide-tape.h"
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+
+#ifdef CONFIG_BLK_DEV_IDECD
+
+struct atapi_request_sense {
+ unsigned char error_code : 7;
+ unsigned char valid : 1;
+ byte reserved1;
+ unsigned char sense_key : 4;
+ unsigned char reserved2 : 1;
+ unsigned char ili : 1;
+ unsigned char reserved3 : 2;
+ byte info[4];
+ byte sense_len;
+ byte command_info[4];
+ byte asc;
+ byte ascq;
+ byte fru;
+ byte sense_key_specific[3];
+};
+
+struct packet_command {
+ char *buffer;
+ int buflen;
+ int stat;
+ struct atapi_request_sense *sense_data;
+ unsigned char c[12];
+};
+
+/* Space to hold the disk TOC. */
+
+#define MAX_TRACKS 99
+struct atapi_toc_header {
+ unsigned short toc_length;
+ byte first_track;
+ byte last_track;
+};
+
+struct atapi_toc_entry {
+ byte reserved1;
+ unsigned control : 4;
+ unsigned adr : 4;
+ byte track;
+ byte reserved2;
+ union {
+ unsigned lba;
+ struct {
+ byte reserved3;
+ byte m;
+ byte s;
+ byte f;
+ } msf;
+ } addr;
+};
+
+struct atapi_toc {
+ int last_session_lba;
+ int xa_flag;
+ unsigned capacity;
+ struct atapi_toc_header hdr;
+ struct atapi_toc_entry ent[MAX_TRACKS+1]; /* One extra for the leadout. */
+};
+
+
+/* This structure is annoyingly close to, but not identical with,
+ the cdrom_subchnl structure from cdrom.h. */
+struct atapi_cdrom_subchnl
+{
+ u_char acdsc_reserved;
+ u_char acdsc_audiostatus;
+ u_short acdsc_length;
+ u_char acdsc_format;
+
+ u_char acdsc_adr: 4;
+ u_char acdsc_ctrl: 4;
+ u_char acdsc_trk;
+ u_char acdsc_ind;
+ union
+ {
+ struct
+ {
+ u_char minute;
+ u_char second;
+ u_char frame;
+ } msf;
+ int lba;
+ } acdsc_absaddr;
+ union
+ {
+ struct
+ {
+ u_char minute;
+ u_char second;
+ u_char frame;
+ } msf;
+ int lba;
+ } acdsc_reladdr;
+};
+
+
+/* Extra per-device info for cdrom drives. */
+struct cdrom_info {
+
+ /* Buffer for table of contents. NULL if we haven't allocated
+ a TOC buffer for this device yet. */
+
+ struct atapi_toc *toc;
+
+ /* Sector buffer. If a read request wants only the first part of a cdrom
+ block, we cache the rest of the block here, in the expectation that that
+ data is going to be wanted soon. SECTOR_BUFFERED is the number of the
+ first buffered sector, and NSECTORS_BUFFERED is the number of sectors
+ in the buffer. Before the buffer is allocated, we should have
+ SECTOR_BUFFER == NULL and NSECTORS_BUFFERED == 0. */
+
+ unsigned long sector_buffered;
+ unsigned long nsectors_buffered;
+ char *sector_buffer;
+
+ /* The result of the last successful request sense command
+ on this device. */
+ struct atapi_request_sense sense_data;
+};
+
+#endif /* CONFIG_BLK_DEV_IDECD */
+
+/*
+ * Now for the data we need to maintain per-drive: ide_drive_t
+ */
+
+typedef enum {ide_disk, ide_cdrom, ide_tape} ide_media_t;
+
+typedef union {
+ unsigned all : 8; /* all of the bits together */
+ struct {
+ unsigned set_geometry : 1; /* respecify drive geometry */
+ unsigned recalibrate : 1; /* seek to cyl 0 */
+ unsigned set_multmode : 1; /* set multmode count */
+ unsigned set_pio : 1; /* set pio mode */
+ unsigned reserved : 4; /* unused */
+ } b;
+ } special_t;
+
+typedef union {
+ unsigned all : 8; /* all of the bits together */
+ struct {
+ unsigned head : 4; /* always zeros here */
+ unsigned unit : 1; /* drive select number, 0 or 1 */
+ unsigned bit5 : 1; /* always 1 */
+ unsigned lba : 1; /* using LBA instead of CHS */
+ unsigned bit7 : 1; /* always 1 */
+ } b;
+ } select_t;
+
+typedef struct ide_drive_s {
+ special_t special; /* special action flags */
+ unsigned present : 1; /* drive is physically present */
+ unsigned noprobe : 1; /* from: hdx=noprobe */
+ unsigned keep_settings : 1; /* restore settings after drive reset */
+ unsigned busy : 1; /* currently doing revalidate_disk() */
+ unsigned removeable : 1; /* 1 if need to do check_media_change */
+ unsigned using_dma : 1; /* disk is using dma for read/write */
+ unsigned forced_geom : 1; /* 1 if hdx=c,h,s was given at boot */
+ unsigned unmask : 1; /* flag: okay to unmask other irqs */
+ unsigned autotune : 2; /* 1=autotune, 2=noautotune, 0=default */
+#if FAKE_FDISK_FOR_EZDRIVE
+ unsigned remap_0_to_1 : 1; /* flag: partitioned with ezdrive */
+#endif /* FAKE_FDISK_FOR_EZDRIVE */
+ ide_media_t media; /* disk, cdrom, tape */
+ select_t select; /* basic drive/head select reg value */
+ byte ctl; /* "normal" value for IDE_CONTROL_REG */
+ byte ready_stat; /* min status value for drive ready */
+ byte mult_count; /* current multiple sector setting */
+ byte mult_req; /* requested multiple sector setting */
+ byte pio_req; /* requested multiple sector setting */
+ byte io_32bit; /* 0=16-bit, 1=32-bit, 2/3=32bit+sync */
+ byte bad_wstat; /* used for ignoring WRERR_STAT */
+ byte sect0; /* offset of first sector for DM6:DDO */
+ byte usage; /* current "open()" count for drive */
+ byte head; /* "real" number of heads */
+ byte sect; /* "real" sectors per track */
+ byte bios_head; /* BIOS/fdisk/LILO number of heads */
+ byte bios_sect; /* BIOS/fdisk/LILO sectors per track */
+ unsigned short bios_cyl; /* BIOS/fdisk/LILO number of cyls */
+ unsigned short cyl; /* "real" number of cyls */
+ void *hwif; /* actually (ide_hwif_t *) */
+ struct wait_queue *wqueue; /* used to wait for drive in open() */
+ struct hd_driveid *id; /* drive model identification info */
+ struct hd_struct *part; /* drive partition table */
+ char name[4]; /* drive name, such as "hda" */
+#ifdef CONFIG_BLK_DEV_IDECD
+ struct cdrom_info cdrom_info; /* for ide-cd.c */
+#endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ idetape_tape_t tape; /* for ide-tape.c */
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+
+ } ide_drive_t;
+
+/*
+ * An ide_dmaproc_t() initiates/aborts DMA read/write operations on a drive.
+ *
+ * The caller is assumed to have selected the drive and programmed the drive's
+ * sector address using CHS or LBA. All that remains is to prepare for DMA
+ * and then issue the actual read/write DMA/PIO command to the drive.
+ *
+ * Returns 0 if all went well.
+ * Returns 1 if DMA read/write could not be started, in which case the caller
+ * should either try again later, or revert to PIO for the current request.
+ */
+typedef enum { ide_dma_read = 0, ide_dma_write = 1,
+ ide_dma_abort = 2, ide_dma_check = 3,
+ ide_dma_status_bad = 4, ide_dma_transferred = 5,
+ ide_dma_begin = 6 }
+ ide_dma_action_t;
+
+typedef int (ide_dmaproc_t)(ide_dma_action_t, ide_drive_t *);
+
+
+/*
+ * An ide_tuneproc_t() is used to set the speed of an IDE interface
+ * to a particular PIO mode. The "byte" parameter is used
+ * to select the PIO mode by number (0,1,2,3,4,5), and a value of 255
+ * indicates that the interface driver should "auto-tune" the PIO mode
+ * according to the drive capabilities in drive->id;
+ *
+ * Not all interface types support tuning, and not all of those
+ * support all possible PIO settings. They may silently ignore
+ * or round values as they see fit.
+ */
+typedef void (ide_tuneproc_t)(ide_drive_t *, byte);
+
+/*
+ * This is used to provide HT6560B interface support.
+ * It will probably also be used by the DC4030VL driver.
+ */
+typedef void (ide_selectproc_t) (ide_drive_t *);
+
+/*
+ * hwif_chipset_t is used to keep track of the specific hardware
+ * chipset used by each IDE interface, if known.
+ */
+typedef enum { ide_unknown, ide_generic, ide_triton,
+ ide_cmd640, ide_dtc2278, ide_ali14xx,
+ ide_qd6580, ide_umc8672, ide_ht6560b }
+ hwif_chipset_t;
+
+typedef struct hwif_s {
+ struct hwif_s *next; /* for linked-list in ide_hwgroup_t */
+ void *hwgroup; /* actually (ide_hwgroup_t *) */
+ unsigned short io_base; /* base io port addr */
+ unsigned short ctl_port; /* usually io_base+0x206 */
+ ide_drive_t drives[MAX_DRIVES]; /* drive info */
+ struct gendisk *gd; /* gendisk structure */
+ ide_tuneproc_t *tuneproc; /* routine to tune PIO mode for drives */
+#ifdef CONFIG_BLK_DEV_HT6560B
+ ide_selectproc_t *selectproc; /* tweaks hardware to select drive */
+#endif /* CONFIG_BLK_DEV_HT6560B */
+ ide_dmaproc_t *dmaproc; /* dma read/write/abort routine */
+ unsigned long *dmatable; /* dma physical region descriptor table */
+ unsigned short dma_base; /* base addr for dma ports (triton) */
+ byte irq; /* our irq number */
+ byte major; /* our major number */
+ char name[5]; /* name of interface, eg. "ide0" */
+ byte index; /* 0 for ide0; 1 for ide1; ... */
+ hwif_chipset_t chipset; /* sub-module for tuning.. */
+ unsigned noprobe : 1; /* don't probe for this interface */
+ unsigned present : 1; /* this interface exists */
+ unsigned serialized : 1; /* valid only for ide_hwifs[0] */
+ unsigned no_unmask : 1; /* disallow setting unmask bits */
+#if (DISK_RECOVERY_TIME > 0)
+ unsigned long last_time; /* time when previous rq was done */
+#endif
+#ifdef CONFIG_BLK_DEV_IDECD
+ struct request request_sense_request; /* from ide-cd.c */
+ struct packet_command request_sense_pc; /* from ide-cd.c */
+#endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ ide_drive_t *tape_drive; /* Pointer to the tape on this interface */
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+ } ide_hwif_t;
+
+/*
+ * internal ide interrupt handler type
+ */
+typedef void (ide_handler_t)(ide_drive_t *);
+
+typedef struct hwgroup_s {
+ ide_handler_t *handler;/* irq handler, if active */
+ ide_drive_t *drive; /* current drive */
+ ide_hwif_t *hwif; /* ptr to current hwif in linked-list */
+ struct request *rq; /* current request */
+ struct timer_list timer; /* failsafe timer */
+ struct request wrq; /* local copy of current write rq */
+ unsigned long poll_timeout; /* timeout value during long polls */
+ } ide_hwgroup_t;
+
+/*
+ * ide_hwifs[] is the master data structure used to keep track
+ * of just about everything in ide.c. Whenever possible, routines
+ * should be using pointers to a drive (ide_drive_t *) or
+ * pointers to a hwif (ide_hwif_t *), rather than indexing this
+ * structure directly (the allocation/layout may change!).
+ */
+#ifdef _IDE_C
+ ide_hwif_t ide_hwifs[MAX_HWIFS]; /* master data repository */
+#else
+extern ide_hwif_t ide_hwifs[];
+#endif
+
+/*
+ * One final include file, which references some of the data/defns from above
+ */
+#define IDE_DRIVER /* "parameter" for blk.h */
+#include <linux/blk.h>
+
+#if (DISK_RECOVERY_TIME > 0)
+void ide_set_recovery_timer (ide_hwif_t *);
+#define SET_RECOVERY_TIMER(drive) ide_set_recovery_timer (drive)
+#else
+#define SET_RECOVERY_TIMER(drive)
+#endif
+
+/*
+ * This is used for (nearly) all data transfers from the IDE interface
+ */
+void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
+
+/*
+ * This is used for (nearly) all data transfers to the IDE interface
+ */
+void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
+
+/*
+ * This is used on exit from the driver, to designate the next irq handler
+ * and also to start the safety timer.
+ */
+void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout);
+
+/*
+ * Error reporting, in human readable form (luxurious, but a memory hog).
+ */
+byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat);
+
+/*
+ * ide_error() takes action based on the error returned by the controller.
+ * The calling function must return afterwards, to restart the request.
+ */
+void ide_error (ide_drive_t *drive, const char *msg, byte stat);
+
+/*
+ * ide_fixstring() cleans up and (optionally) byte-swaps a text string,
+ * removing leading/trailing blanks and compressing internal blanks.
+ * It is primarily used to tidy up the model name/number fields as
+ * returned by the WIN_[P]IDENTIFY commands.
+ */
+void ide_fixstring (byte *s, const int bytecount, const int byteswap);
+
+/*
+ * This routine busy-waits for the drive status to be not "busy".
+ * It then checks the status for all of the "good" bits and none
+ * of the "bad" bits, and if all is okay it returns 0. All other
+ * cases return 1 after invoking ide_error() -- caller should return.
+ *
+ */
+int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout);
+
+/*
+ * This routine is called from the partition-table code in genhd.c
+ * to "convert" a drive to a logical geometry with fewer than 1024 cyls.
+ *
+ * The second parameter, "xparm", determines exactly how the translation
+ * will be handled:
+ * 0 = convert to CHS with fewer than 1024 cyls
+ * using the same method as Ontrack DiskManager.
+ * 1 = same as "0", plus offset everything by 63 sectors.
+ * -1 = similar to "0", plus redirect sector 0 to sector 1.
+ * >1 = convert to a CHS geometry with "xparm" heads.
+ *
+ * Returns 0 if the translation was not possible, if the device was not
+ * an IDE disk drive, or if a geometry was "forced" on the commandline.
+ * Returns 1 if the geometry translation was successful.
+ */
+int ide_xlate_1024 (kdev_t, int, const char *);
+
+/*
+ * Start a reset operation for an IDE interface.
+ * The caller should return immediately after invoking this.
+ */
+void ide_do_reset (ide_drive_t *);
+
+/*
+ * This function is intended to be used prior to invoking ide_do_drive_cmd().
+ */
+void ide_init_drive_cmd (struct request *rq);
+
+/*
+ * "action" parameter type for ide_do_drive_cmd() below.
+ */
+typedef enum
+ {ide_wait, /* insert rq at end of list, and wait for it */
+ ide_next, /* insert rq immediately after current request */
+ ide_preempt, /* insert rq in front of current request */
+ ide_end} /* insert rq at end of list, but don't wait for it */
+ ide_action_t;
+
+/*
+ * This function issues a special IDE device request
+ * onto the request queue.
+ *
+ * If action is ide_wait, then then rq is queued at the end of
+ * the request queue, and the function sleeps until it has been
+ * processed. This is for use when invoked from an ioctl handler.
+ *
+ * If action is ide_preempt, then the rq is queued at the head of
+ * the request queue, displacing the currently-being-processed
+ * request and this function returns immediately without waiting
+ * for the new rq to be completed. This is VERY DANGEROUS, and is
+ * intended for careful use by the ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_next, then the rq is queued immediately after
+ * the currently-being-processed-request (if any), and the function
+ * returns without waiting for the new rq to be completed. As above,
+ * This is VERY DANGEROUS, and is intended for careful use by the
+ * ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_end, then the rq is queued at the end of the
+ * request queue, and the function returns immediately without waiting
+ * for the new rq to be completed. This is again intended for careful
+ * use by the ATAPI tape/cdrom driver code. (Currently used by ide-tape.c,
+ * when operating in the pipelined operation mode).
+ */
+int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action);
+
+/*
+ * Clean up after success/failure of an explicit drive cmd.
+ * stat/err are used only when (HWGROUP(drive)->rq->cmd == IDE_DRIVE_CMD).
+ */
+void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err);
+
+#ifdef CONFIG_BLK_DEV_IDECD
+/*
+ * These are routines in ide-cd.c invoked from ide.c
+ */
+void ide_do_rw_cdrom (ide_drive_t *, unsigned long);
+int ide_cdrom_ioctl (ide_drive_t *, struct inode *, struct file *, unsigned int, unsigned long);
+int ide_cdrom_check_media_change (ide_drive_t *);
+int ide_cdrom_open (struct inode *, struct file *, ide_drive_t *);
+void ide_cdrom_release (struct inode *, struct file *, ide_drive_t *);
+void ide_cdrom_setup (ide_drive_t *);
+#endif /* CONFIG_BLK_DEV_IDECD */
+
+#ifdef CONFIG_BLK_DEV_IDETAPE
+
+/*
+ * Functions in ide-tape.c which are invoked from ide.c:
+ */
+
+/*
+ * idetape_identify_device is called during device probing stage to
+ * probe for an ide atapi tape drive and to initialize global variables
+ * in ide-tape.c which provide the link between the character device
+ * and the correspoding block device.
+ *
+ * Returns 1 if an ide tape was detected and is supported.
+ * Returns 0 otherwise.
+ */
+
+int idetape_identify_device (ide_drive_t *drive,struct hd_driveid *id);
+
+/*
+ * idetape_setup is called a bit later than idetape_identify_device,
+ * during the search for disk partitions, to initialize various tape
+ * state variables in ide_drive_t *drive.
+ */
+
+void idetape_setup (ide_drive_t *drive);
+
+/*
+ * idetape_do_request is our request function. It is called by ide.c
+ * to process a new request.
+ */
+
+void idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block);
+
+/*
+ * idetape_end_request is used to finish servicing a request, and to
+ * insert a pending pipeline request into the main device queue.
+ */
+
+void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup);
+
+/*
+ * Block device interface functions.
+ */
+
+int idetape_blkdev_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+int idetape_blkdev_open (struct inode *inode, struct file *filp, ide_drive_t *drive);
+void idetape_blkdev_release (struct inode *inode, struct file *filp, ide_drive_t *drive);
+
+/*
+ * idetape_register_chrdev initializes the character device interface to
+ * the ide tape drive.
+ */
+
+void idetape_register_chrdev (void);
+
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+
+#ifdef CONFIG_BLK_DEV_TRITON
+void ide_init_triton (byte, byte);
+#endif /* CONFIG_BLK_DEV_TRITON */
diff --git a/i386/i386at/gpl/linux/block/ide_modes.h b/i386/i386at/gpl/linux/block/ide_modes.h
new file mode 100644
index 0000000..e174d5d
--- /dev/null
+++ b/i386/i386at/gpl/linux/block/ide_modes.h
@@ -0,0 +1,142 @@
+#ifndef _IDE_MODES_H
+#define _IDE_MODES_H
+/*
+ * linux/drivers/block/ide_modes.h
+ *
+ * Copyright (C) 1996 Linus Torvalds, Igor Abramov, and Mark Lord
+ */
+
+/*
+ * Shared data/functions for determining best PIO mode for an IDE drive.
+ * Most of this stuff originally lived in cmd640.c, and changes to the
+ * ide_pio_blacklist[] table should be made with EXTREME CAUTION to avoid
+ * breaking the fragile cmd640.c support.
+ */
+
+#if defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS)
+
+#ifndef _IDE_C
+
+int ide_scan_pio_blacklist (char *model);
+unsigned int ide_get_best_pio_mode (ide_drive_t *drive);
+
+#else /* _IDE_C */
+
+/*
+ * Black list. Some drives incorrectly report their maximal PIO mode,
+ * at least in respect to CMD640. Here we keep info on some known drives.
+ */
+static struct ide_pio_info {
+ const char *name;
+ int pio;
+} ide_pio_blacklist [] = {
+/* { "Conner Peripherals 1275MB - CFS1275A", 4 }, */
+
+ { "WDC AC2700", 3 },
+ { "WDC AC2540", 3 },
+ { "WDC AC2420", 3 },
+ { "WDC AC2340", 3 },
+ { "WDC AC2250", 0 },
+ { "WDC AC2200", 0 },
+ { "WDC AC2120", 0 },
+ { "WDC AC2850", 3 },
+ { "WDC AC1270", 3 },
+ { "WDC AC1170", 3 },
+ { "WDC AC1210", 1 },
+ { "WDC AC280", 0 },
+/* { "WDC AC21000", 4 }, */
+ { "WDC AC31000", 3 },
+/* { "WDC AC21200", 4 }, */
+ { "WDC AC31200", 3 },
+/* { "WDC AC31600", 4 }, */
+
+ { "Maxtor 7131 AT", 1 },
+ { "Maxtor 7171 AT", 1 },
+ { "Maxtor 7213 AT", 1 },
+ { "Maxtor 7245 AT", 1 },
+ { "Maxtor 7345 AT", 1 },
+ { "Maxtor 7546 AT", 3 },
+ { "Maxtor 7540 AV", 3 },
+
+ { "SAMSUNG SHD-3121A", 1 },
+ { "SAMSUNG SHD-3122A", 1 },
+ { "SAMSUNG SHD-3172A", 1 },
+
+/* { "ST51080A", 4 },
+ * { "ST51270A", 4 },
+ * { "ST31220A", 4 },
+ * { "ST31640A", 4 },
+ * { "ST32140A", 4 },
+ * { "ST3780A", 4 },
+ */
+ { "ST5660A", 3 },
+ { "ST3660A", 3 },
+ { "ST3630A", 3 },
+ { "ST3655A", 3 },
+ { "ST3391A", 3 },
+ { "ST3390A", 1 },
+ { "ST3600A", 1 },
+ { "ST3290A", 0 },
+ { "ST3144A", 0 },
+
+ { "QUANTUM ELS127A", 0 },
+ { "QUANTUM ELS170A", 0 },
+ { "QUANTUM LPS240A", 0 },
+ { "QUANTUM LPS210A", 3 },
+ { "QUANTUM LPS270A", 3 },
+ { "QUANTUM LPS365A", 3 },
+ { "QUANTUM LPS540A", 3 },
+ { "QUANTUM FIREBALL", 3 }, /* For models 540/640/1080/1280 */
+ /* 1080A works fine in mode4 with triton */
+ { NULL, 0 }
+};
+
+/*
+ * This routine searches the ide_pio_blacklist for an entry
+ * matching the start/whole of the supplied model name.
+ *
+ * Returns -1 if no match found.
+ * Otherwise returns the recommended PIO mode from ide_pio_blacklist[].
+ */
+int ide_scan_pio_blacklist (char *model)
+{
+ struct ide_pio_info *p;
+
+ for (p = ide_pio_blacklist; p->name != NULL; p++) {
+ if (strncmp(p->name, model, strlen(p->name)) == 0)
+ return p->pio;
+ }
+ return -1;
+}
+
+/*
+ * This routine returns the recommended PIO mode for a given drive,
+ * based on the drive->id information and the ide_pio_blacklist[].
+ * This is used by most chipset support modules when "auto-tuning".
+ */
+unsigned int ide_get_best_pio_mode (ide_drive_t *drive)
+{
+ unsigned int pio = 0;
+ struct hd_driveid *id = drive->id;
+
+ if (id != NULL) {
+ if (HWIF(drive)->chipset != ide_cmd640 && !strcmp("QUANTUM FIREBALL1080A", id->model))
+ pio = 4;
+ else
+ pio = ide_scan_pio_blacklist(id->model);
+ if (pio == -1) {
+ pio = (id->tPIO < 2) ? id->tPIO : 2;
+ if (id->field_valid & 2) {
+ byte modes = id->eide_pio_modes;
+ if (modes & 4) pio = 5;
+ else if (modes & 2) pio = 4;
+ else if (modes & 1) pio = 3;
+ }
+ }
+ }
+ return pio;
+}
+
+#endif /* _IDE_C */
+#endif /* defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) */
+#endif /* _IDE_MODES_H */
diff --git a/i386/i386at/gpl/linux/block/rz1000.c b/i386/i386at/gpl/linux/block/rz1000.c
new file mode 100644
index 0000000..11f1dbd
--- /dev/null
+++ b/i386/i386at/gpl/linux/block/rz1000.c
@@ -0,0 +1,56 @@
+/*
+ * linux/drivers/block/rz1000.c Version 0.02 Feb 08, 1996
+ *
+ * Copyright (C) 1995-1996 Linus Torvalds & author (see below)
+ */
+
+/*
+ * Principal Author/Maintainer: mlord@bnr.ca (Mark Lord)
+ *
+ * This file provides support for disabling the buggy read-ahead
+ * mode of the RZ1000 IDE chipset, commonly used on Intel motherboards.
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <asm/io.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include "ide.h"
+
+static void ide_pci_access_error (int rc)
+{
+ printk("ide: pcibios access failed - %s\n", pcibios_strerror(rc));
+}
+
+void init_rz1000 (byte bus, byte fn)
+{
+ int rc;
+ unsigned short reg;
+
+ printk("ide: buggy RZ1000 interface: ");
+ if ((rc = pcibios_read_config_word (bus, fn, PCI_COMMAND, &reg))) {
+ ide_pci_access_error (rc);
+ } else if (!(reg & 1)) {
+ printk("not enabled\n");
+ } else {
+ if ((rc = pcibios_read_config_word(bus, fn, 0x40, &reg))
+ || (rc = pcibios_write_config_word(bus, fn, 0x40, reg & 0xdfff)))
+ {
+ ide_hwifs[0].no_unmask = 1;
+ ide_hwifs[1].no_unmask = 1;
+ ide_hwifs[0].serialized = 1;
+ ide_pci_access_error (rc);
+ printk("serialized, disabled unmasking\n");
+ } else
+ printk("disabled read-ahead\n");
+ }
+}
diff --git a/i386/i386at/gpl/linux/block/triton.c b/i386/i386at/gpl/linux/block/triton.c
new file mode 100644
index 0000000..5829661
--- /dev/null
+++ b/i386/i386at/gpl/linux/block/triton.c
@@ -0,0 +1,459 @@
+/*
+ * linux/drivers/block/triton.c Version 1.06 Feb 6, 1996
+ *
+ * Copyright (c) 1995-1996 Mark Lord
+ * May be copied or modified under the terms of the GNU General Public License
+ */
+
+/*
+ * This module provides support for the Bus Master IDE DMA function
+ * of the Intel PCI Triton chipset (82371FB).
+ *
+ * DMA is currently supported only for hard disk drives (not cdroms).
+ *
+ * Support for cdroms will likely be added at a later date,
+ * after broader experience has been obtained with hard disks.
+ *
+ * Up to four drives may be enabled for DMA, and the Triton chipset will
+ * (hopefully) arbitrate the PCI bus among them. Note that the 82371FB chip
+ * provides a single "line buffer" for the BM IDE function, so performance of
+ * multiple (two) drives doing DMA simultaneously will suffer somewhat,
+ * as they contest for that resource bottleneck. This is handled transparently
+ * inside the 82371FB chip.
+ *
+ * By default, DMA support is prepared for use, but is currently enabled only
+ * for drives which support multi-word DMA mode2 (mword2), or which are
+ * recognized as "good" (see table below). Drives with only mode0 or mode1
+ * (single or multi) DMA should also work with this chipset/driver (eg. MC2112A)
+ * but are not enabled by default. Use "hdparm -i" to view modes supported
+ * by a given drive.
+ *
+ * The hdparm-2.4 (or later) utility can be used for manually enabling/disabling
+ * DMA support, but must be (re-)compiled against this kernel version or later.
+ *
+ * To enable DMA, use "hdparm -d1 /dev/hd?" on a per-drive basis after booting.
+ * If problems arise, ide.c will disable DMA operation after a few retries.
+ * This error recovery mechanism works and has been extremely well exercised.
+ *
+ * IDE drives, depending on their vintage, may support several different modes
+ * of DMA operation. The boot-time modes are indicated with a "*" in
+ * the "hdparm -i" listing, and can be changed with *knowledgeable* use of
+ * the "hdparm -X" feature. There is seldom a need to do this, as drives
+ * normally power-up with their "best" PIO/DMA modes enabled.
+ *
+ * Testing was done with an ASUS P55TP4XE/100 system and the following drives:
+ *
+ * Quantum Fireball 1080A (1Gig w/83kB buffer), DMA mode2, PIO mode4.
+ * - DMA mode2 works well (7.4MB/sec), despite the tiny on-drive buffer.
+ * - This drive also does PIO mode4, at about the same speed as DMA mode2.
+ * An awesome drive for the price!
+ *
+ * Fujitsu M1606TA (1Gig w/256kB buffer), DMA mode2, PIO mode4.
+ * - DMA mode2 gives horrible performance (1.6MB/sec), despite the good
+ * size of the on-drive buffer and a boasted 10ms average access time.
+ * - PIO mode4 was better, but peaked at a mere 4.5MB/sec.
+ *
+ * Micropolis MC2112A (1Gig w/508kB buffer), drive pre-dates EIDE and ATA2.
+ * - DMA works fine (2.2MB/sec), probably due to the large on-drive buffer.
+ * - This older drive can also be tweaked for fastPIO (3.7MB/sec) by using
+ * maximum clock settings (5,4) and setting all flags except prefetch.
+ *
+ * Western Digital AC31000H (1Gig w/128kB buffer), DMA mode1, PIO mode3.
+ * - DMA does not work reliably. The drive appears to be somewhat tardy
+ * in deasserting DMARQ at the end of a sector. This is evident in
+ * the observation that WRITEs work most of the time, depending on
+ * cache-buffer occupancy, but multi-sector reads seldom work.
+ *
+ * Testing was done with a Gigabyte GA-586 ATE system and the following drive:
+ * (Uwe Bonnes - bon@elektron.ikp.physik.th-darmstadt.de)
+ *
+ * Western Digital AC31600H (1.6Gig w/128kB buffer), DMA mode2, PIO mode4.
+ * - much better than its 1Gig cousin, this drive is reported to work
+ * very well with DMA (7.3MB/sec).
+ *
+ * Other drives:
+ *
+ * Maxtor 7540AV (515Meg w/32kB buffer), DMA modes mword0/sword2, PIO mode3.
+ * - a budget drive, with budget performance, around 3MB/sec.
+ *
+ * Western Digital AC2850F (814Meg w/64kB buffer), DMA mode1, PIO mode3.
+ * - another "caviar" drive, similar to the AC31000, except that this one
+ * worked with DMA in at least one system. Throughput is about 3.8MB/sec
+ * for both DMA and PIO.
+ *
+ * Conner CFS850A (812Meg w/64kB buffer), DMA mode2, PIO mode4.
+ * - like most Conner models, this drive proves that even a fast interface
+ * cannot improve slow media. Both DMA and PIO peak around 3.5MB/sec.
+ *
+ * If you have any drive models to add, email your results to: mlord@bnr.ca
+ * Keep an eye on /var/adm/messages for "DMA disabled" messages.
+ *
+ * Some people have reported trouble with Intel Zappa motherboards.
+ * This can be fixed by upgrading the AMI BIOS to version 1.00.04.BS0,
+ * available from ftp://ftp.intel.com/pub/bios/10004bs0.exe
+ * (thanks to Glen Morrell <glen@spin.Stanford.edu> for researching this).
+ *
+ * And, yes, Intel Zappa boards really *do* use the Triton IDE ports.
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include "ide.h"
+
+/*
+ * good_dma_drives() lists the model names (from "hdparm -i")
+ * of drives which do not support mword2 DMA but which are
+ * known to work fine with this interface under Linux.
+ */
+const char *good_dma_drives[] = {"Micropolis 2112A",
+ "CONNER CTMA 4000"};
+
+/*
+ * Our Physical Region Descriptor (PRD) table should be large enough
+ * to handle the biggest I/O request we are likely to see. Since requests
+ * can have no more than 256 sectors, and since the typical blocksize is
+ * two sectors, we could get by with a limit of 128 entries here for the
+ * usual worst case. Most requests seem to include some contiguous blocks,
+ * further reducing the number of table entries required.
+ *
+ * The driver reverts to PIO mode for individual requests that exceed
+ * this limit (possible with 512 byte blocksizes, eg. MSDOS f/s), so handling
+ * 100% of all crazy scenarios here is not necessary.
+ *
+ * As it turns out though, we must allocate a full 4KB page for this,
+ * so the two PRD tables (ide0 & ide1) will each get half of that,
+ * allowing each to have about 256 entries (8 bytes each) from this.
+ */
+#define PRD_BYTES 8
+#define PRD_ENTRIES (PAGE_SIZE / (2 * PRD_BYTES))
+
+/*
+ * dma_intr() is the handler for disk read/write DMA interrupts
+ */
+static void dma_intr (ide_drive_t *drive)
+{
+ byte stat, dma_stat;
+ int i;
+ struct request *rq = HWGROUP(drive)->rq;
+ unsigned short dma_base = HWIF(drive)->dma_base;
+
+ dma_stat = inb(dma_base+2); /* get DMA status */
+ outb(inb(dma_base)&~1, dma_base); /* stop DMA operation */
+ stat = GET_STAT(); /* get drive status */
+ if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
+ if ((dma_stat & 7) == 4) { /* verify good DMA status */
+ rq = HWGROUP(drive)->rq;
+ for (i = rq->nr_sectors; i > 0;) {
+ i -= rq->current_nr_sectors;
+ ide_end_request(1, HWGROUP(drive));
+ }
+ return;
+ }
+ printk("%s: bad DMA status: 0x%02x\n", drive->name, dma_stat);
+ }
+ sti();
+ ide_error(drive, "dma_intr", stat);
+}
+
+/*
+ * build_dmatable() prepares a dma request.
+ * Returns 0 if all went okay, returns 1 otherwise.
+ */
+static int build_dmatable (ide_drive_t *drive)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ struct buffer_head *bh = rq->bh;
+ unsigned long size, addr, *table = HWIF(drive)->dmatable;
+ unsigned int count = 0;
+
+ do {
+ /*
+ * Determine addr and size of next buffer area. We assume that
+ * individual virtual buffers are always composed linearly in
+ * physical memory. For example, we assume that any 8kB buffer
+ * is always composed of two adjacent physical 4kB pages rather
+ * than two possibly non-adjacent physical 4kB pages.
+ */
+ if (bh == NULL) { /* paging and tape requests have (rq->bh == NULL) */
+ addr = virt_to_bus (rq->buffer);
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ if (drive->media == ide_tape)
+ size = drive->tape.pc->request_transfer;
+ else
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+ size = rq->nr_sectors << 9;
+ } else {
+ /* group sequential buffers into one large buffer */
+ addr = virt_to_bus (bh->b_data);
+ size = bh->b_size;
+ while ((bh = bh->b_reqnext) != NULL) {
+ if ((addr + size) != virt_to_bus (bh->b_data))
+ break;
+ size += bh->b_size;
+ }
+ }
+
+ /*
+ * Fill in the dma table, without crossing any 64kB boundaries.
+ * We assume 16-bit alignment of all blocks.
+ */
+ while (size) {
+ if (++count >= PRD_ENTRIES) {
+ printk("%s: DMA table too small\n", drive->name);
+ return 1; /* revert to PIO for this request */
+ } else {
+ unsigned long bcount = 0x10000 - (addr & 0xffff);
+ if (bcount > size)
+ bcount = size;
+ *table++ = addr;
+ *table++ = bcount;
+ addr += bcount;
+ size -= bcount;
+ }
+ }
+ } while (bh != NULL);
+ if (count) {
+ *--table |= 0x80000000; /* set End-Of-Table (EOT) bit */
+ return 0;
+ }
+ printk("%s: empty DMA table?\n", drive->name);
+ return 1; /* let the PIO routines handle this weirdness */
+}
+
+static int config_drive_for_dma (ide_drive_t *drive)
+{
+ const char **list;
+
+ struct hd_driveid *id = drive->id;
+ if (id && (id->capability & 1)) {
+ /* Enable DMA on any drive that supports mword2 DMA */
+ if ((id->field_valid & 2) && (id->dma_mword & 0x404) == 0x404) {
+ drive->using_dma = 1;
+ return 0; /* DMA enabled */
+ }
+ /* Consult the list of known "good" drives */
+ list = good_dma_drives;
+ while (*list) {
+ if (!strcmp(*list++,id->model)) {
+ drive->using_dma = 1;
+ return 0; /* DMA enabled */
+ }
+ }
+ }
+ return 1; /* DMA not enabled */
+}
+
+/*
+ * triton_dmaproc() initiates/aborts DMA read/write operations on a drive.
+ *
+ * The caller is assumed to have selected the drive and programmed the drive's
+ * sector address using CHS or LBA. All that remains is to prepare for DMA
+ * and then issue the actual read/write DMA/PIO command to the drive.
+ *
+ * For ATAPI devices, we just prepare for DMA and return. The caller should
+ * then issue the packet command to the drive and call us again with
+ * ide_dma_begin afterwards.
+ *
+ * Returns 0 if all went well.
+ * Returns 1 if DMA read/write could not be started, in which case
+ * the caller should revert to PIO for the current request.
+ */
+static int triton_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ unsigned long dma_base = HWIF(drive)->dma_base;
+ unsigned int reading = (1 << 3);
+
+ switch (func) {
+ case ide_dma_abort:
+ outb(inb(dma_base)&~1, dma_base); /* stop DMA */
+ return 0;
+ case ide_dma_check:
+ return config_drive_for_dma (drive);
+ case ide_dma_write:
+ reading = 0;
+ case ide_dma_read:
+ break;
+ case ide_dma_status_bad:
+ return ((inb(dma_base+2) & 7) != 4); /* verify good DMA status */
+ case ide_dma_transferred:
+#if 0
+ return (number of bytes actually transferred);
+#else
+ return (0);
+#endif
+ case ide_dma_begin:
+ outb(inb(dma_base)|1, dma_base); /* begin DMA */
+ return 0;
+ default:
+ printk("triton_dmaproc: unsupported func: %d\n", func);
+ return 1;
+ }
+ if (build_dmatable (drive))
+ return 1;
+ outl(virt_to_bus (HWIF(drive)->dmatable), dma_base + 4); /* PRD table */
+ outb(reading, dma_base); /* specify r/w */
+ outb(0x26, dma_base+2); /* clear status bits */
+#ifdef CONFIG_BLK_DEV_IDEATAPI
+ if (drive->media != ide_disk)
+ return 0;
+#endif /* CONFIG_BLK_DEV_IDEATAPI */
+ ide_set_handler(drive, &dma_intr, WAIT_CMD); /* issue cmd to drive */
+ OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
+ outb(inb(dma_base)|1, dma_base); /* begin DMA */
+ return 0;
+}
+
+/*
+ * print_triton_drive_flags() displays the currently programmed options
+ * in the Triton chipset for a given drive.
+ *
+ * If fastDMA is "no", then slow ISA timings are used for DMA data xfers.
+ * If fastPIO is "no", then slow ISA timings are used for PIO data xfers.
+ * If IORDY is "no", then IORDY is assumed to always be asserted.
+ * If PreFetch is "no", then data pre-fetch/post are not used.
+ *
+ * When "fastPIO" and/or "fastDMA" are "yes", then faster PCI timings and
+ * back-to-back 16-bit data transfers are enabled, using the sample_CLKs
+ * and recovery_CLKs (PCI clock cycles) timing parameters for that interface.
+ */
+static void print_triton_drive_flags (unsigned int unit, byte flags)
+{
+ printk(" %s ", unit ? "slave :" : "master:");
+ printk( "fastDMA=%s", (flags&9) ? "on " : "off");
+ printk(" PreFetch=%s", (flags&4) ? "on " : "off");
+ printk(" IORDY=%s", (flags&2) ? "on " : "off");
+ printk(" fastPIO=%s\n", ((flags&9)==1) ? "on " : "off");
+}
+
+static void init_triton_dma (ide_hwif_t *hwif, unsigned short base)
+{
+ static unsigned long dmatable = 0;
+
+ printk(" %s: BusMaster DMA at 0x%04x-0x%04x", hwif->name, base, base+7);
+ if (check_region(base, 8)) {
+ printk(" -- ERROR, PORTS ALREADY IN USE");
+ } else {
+ request_region(base, 8, "triton DMA");
+ hwif->dma_base = base;
+ if (!dmatable) {
+ /*
+ * Since we know we are on a PCI bus, we could
+ * actually use __get_free_pages() here instead
+ * of __get_dma_pages() -- no ISA limitations.
+ */
+ dmatable = __get_dma_pages(GFP_KERNEL, 0);
+ }
+ if (dmatable) {
+ hwif->dmatable = (unsigned long *) dmatable;
+ dmatable += (PRD_ENTRIES * PRD_BYTES);
+ outl(virt_to_bus(hwif->dmatable), base + 4);
+ hwif->dmaproc = &triton_dmaproc;
+ }
+ }
+ printk("\n");
+}
+
+/*
+ * calc_mode() returns the ATA PIO mode number, based on the number
+ * of cycle clks passed in. Assumes 33Mhz bus operation (30ns per clk).
+ */
+byte calc_mode (byte clks)
+{
+ if (clks == 3) return 5;
+ if (clks == 4) return 4;
+ if (clks < 6) return 3;
+ if (clks < 8) return 2;
+ if (clks < 13) return 1;
+ return 0;
+}
+
+/*
+ * ide_init_triton() prepares the IDE driver for DMA operation.
+ * This routine is called once, from ide.c during driver initialization,
+ * for each triton chipset which is found (unlikely to be more than one).
+ */
+void ide_init_triton (byte bus, byte fn)
+{
+ int rc = 0, h;
+ int dma_enabled = 0;
+ unsigned short bmiba, pcicmd;
+ unsigned int timings;
+
+ printk("ide: Triton BM-IDE on PCI bus %d function %d\n", bus, fn);
+ /*
+ * See if IDE and BM-DMA features are enabled:
+ */
+ if ((rc = pcibios_read_config_word(bus, fn, 0x04, &pcicmd)))
+ goto quit;
+ if ((pcicmd & 1) == 0) {
+ printk("ide: Triton IDE ports are not enabled\n");
+ goto quit;
+ }
+ if ((pcicmd & 4) == 0) {
+ printk("ide: Triton BM-DMA feature is not enabled -- upgrade your BIOS\n");
+ } else {
+ /*
+ * Get the bmiba base address
+ */
+ if ((rc = pcibios_read_config_word(bus, fn, 0x20, &bmiba)))
+ goto quit;
+ bmiba &= 0xfff0; /* extract port base address */
+ dma_enabled = 1;
+ }
+
+ /*
+ * See if ide port(s) are enabled
+ */
+ if ((rc = pcibios_read_config_dword(bus, fn, 0x40, &timings)))
+ goto quit;
+ if (!(timings & 0x80008000)) {
+ printk("ide: neither Triton IDE port is enabled\n");
+ goto quit;
+ }
+
+ /*
+ * Save the dma_base port addr for each interface
+ */
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ byte s_clks, r_clks;
+ ide_hwif_t *hwif = &ide_hwifs[h];
+ unsigned short time;
+ if (hwif->io_base == 0x1f0) {
+ time = timings & 0xffff;
+ if ((timings & 0x8000) == 0) /* interface enabled? */
+ continue;
+ hwif->chipset = ide_triton;
+ if (dma_enabled)
+ init_triton_dma(hwif, bmiba);
+ } else if (hwif->io_base == 0x170) {
+ time = timings >> 16;
+ if ((timings & 0x8000) == 0) /* interface enabled? */
+ continue;
+ hwif->chipset = ide_triton;
+ if (dma_enabled)
+ init_triton_dma(hwif, bmiba + 8);
+ } else
+ continue;
+ s_clks = ((~time >> 12) & 3) + 2;
+ r_clks = ((~time >> 8) & 3) + 1;
+ printk(" %s timing: (0x%04x) sample_CLKs=%d, recovery_CLKs=%d (PIO mode%d)\n",
+ hwif->name, time, s_clks, r_clks, calc_mode(s_clks+r_clks));
+ print_triton_drive_flags (0, time & 0xf);
+ print_triton_drive_flags (1, (time >> 4) & 0xf);
+ }
+
+quit: if (rc) printk("ide: pcibios access failed - %s\n", pcibios_strerror(rc));
+}
+