summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-12-16 06:06:25 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-12-16 06:06:25 +0000
commitaa944aa3453e47706685bc562711a9e87375941e (patch)
tree8fb37a65f205a90412917ca2b91c429263ef1790 /drivers
parent967c65a99059fd459b956c1588ce0ba227912c4e (diff)
Merge with Linux 2.1.72, part 2.
The new signal code with exception of the code for the rt signals. The definitions in <asm/siginfo.h> and <asm/ucontext.h> are currently just stolen from the Alpha and will need to be overhauled.
Diffstat (limited to 'drivers')
-rw-r--r--drivers/block/ide-dma.c678
-rw-r--r--drivers/block/pdc4030.c365
-rw-r--r--drivers/block/pdc4030.h44
-rw-r--r--drivers/block/promise.c0
-rw-r--r--drivers/block/promise.h0
-rw-r--r--drivers/block/triton.c0
-rw-r--r--drivers/block/trm290.c227
-rw-r--r--drivers/cdrom/aztcd.h162
-rw-r--r--drivers/cdrom/cdi.c0
-rw-r--r--drivers/cdrom/cdu31a.h411
-rw-r--r--drivers/cdrom/cm206.h171
-rw-r--r--drivers/cdrom/gscd.h110
-rw-r--r--drivers/cdrom/isp16.h75
-rw-r--r--drivers/cdrom/mcd.h128
-rw-r--r--drivers/cdrom/mcdx.h184
-rw-r--r--drivers/cdrom/optcd.h52
-rw-r--r--drivers/cdrom/sbpcd.h858
-rw-r--r--drivers/cdrom/sjcd.h181
-rw-r--r--drivers/cdrom/sonycd535.h183
-rw-r--r--drivers/char/cd1865.h263
-rw-r--r--drivers/char/specialix.c2333
-rw-r--r--drivers/char/specialix_io8.h146
-rw-r--r--drivers/misc/parport_ax.c554
-rw-r--r--drivers/net/baycom.c0
-rw-r--r--drivers/net/bpqether.c0
-rw-r--r--drivers/net/dmascc.c1260
-rw-r--r--drivers/net/hdlcdrv.c0
-rw-r--r--drivers/net/mkiss.c0
-rw-r--r--drivers/net/mkiss.h0
-rw-r--r--drivers/net/pi2.c0
-rw-r--r--drivers/net/pt.c0
-rw-r--r--drivers/net/scc.c0
-rw-r--r--drivers/net/soundmodem/.cvsignore0
-rw-r--r--drivers/net/soundmodem/Makefile0
-rw-r--r--drivers/net/soundmodem/gentbl.c0
-rw-r--r--drivers/net/soundmodem/sm.c0
-rw-r--r--drivers/net/soundmodem/sm.h0
-rw-r--r--drivers/net/soundmodem/sm_afsk1200.c0
-rw-r--r--drivers/net/soundmodem/sm_afsk2400_7.c0
-rw-r--r--drivers/net/soundmodem/sm_afsk2400_8.c0
-rw-r--r--drivers/net/soundmodem/sm_fsk9600.c0
-rw-r--r--drivers/net/soundmodem/sm_hapn4800.c0
-rw-r--r--drivers/net/soundmodem/sm_sbc.c0
-rw-r--r--drivers/net/soundmodem/sm_wss.c0
-rw-r--r--drivers/net/soundmodem/smdma.h0
-rw-r--r--drivers/net/z8530.h0
-rw-r--r--drivers/sgi/char/Config.in12
47 files changed, 8397 insertions, 0 deletions
diff --git a/drivers/block/ide-dma.c b/drivers/block/ide-dma.c
new file mode 100644
index 000000000..6a2886f78
--- /dev/null
+++ b/drivers/block/ide-dma.c
@@ -0,0 +1,678 @@
+/*
+ * linux/drivers/block/ide-dma.c Version 4.06 December 3, 1997
+ *
+ * Copyright (c) 1995-1998 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 functions
+ * of various PCI chipsets, including the Intel PIIX (i82371FB for
+ * the 430 FX chipset), the PIIX3 (i82371SB for the 430 HX/VX and
+ * 440 chipsets), and the PIIX4 (i82371AB for the 430 TX chipset)
+ * ("PIIX" stands for "PCI ISA IDE Xcellerator").
+ *
+ * Pretty much the same code works for other IDE PCI bus-mastering chipsets.
+ *
+ * DMA is supported for all IDE devices (disk drives, cdroms, tapes, floppies).
+ *
+ * By default, DMA support is prepared for use, but is currently enabled only
+ * for drives which already have DMA enabled (UltraDMA or mode 2 multi/single),
+ * or which are recognized as "good" (see table below). Drives with only mode0
+ * or mode1 (multi/single) 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 has been done with a rather extensive number of drives,
+ * with Quantum & Western Digital models generally outperforming the pack,
+ * and Fujitsu & Conner (and some Seagate which are really Conner) drives
+ * showing more lackluster throughput.
+ *
+ * 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).
+ *
+ * Thanks to "Christopher J. Reimer" <reimer@doe.carleton.ca> for fixing the
+ * problem with some (all?) ACER motherboards/BIOSs. Hopefully the fix
+ * still works here (?).
+ *
+ * Thanks to "Benoit Poulot-Cazajous" <poulot@chorus.fr> for testing
+ * "TX" chipset compatibility and for providing patches for the "TX" chipset.
+ *
+ * Thanks to Christian Brunner <chb@muc.de> for taking a good first crack
+ * at generic DMA -- his patches were referred to when preparing this code.
+ *
+ * Most importantly, thanks to Robert Bringman <rob@mars.trion.com>
+ * for supplying a Promise UDMA board & WD UDMA drive for this work!
+ *
+ * And, yes, Intel Zappa boards really *do* use both PIIX 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 <linux/init.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 mode2 DMA but which are
+ * known to work fine with this interface under Linux.
+ */
+const char *good_dma_drives[] = {"Micropolis 2112A",
+ "CONNER CTMA 4000",
+ NULL};
+
+/*
+ * 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 or more 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))
+
+static int config_drive_for_dma (ide_drive_t *);
+
+/*
+ * 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);
+}
+
+/*
+ * ide_build_dmatable() prepares a dma request.
+ * Returns 0 if all went okay, returns 1 otherwise.
+ * May also be invoked from trm290.c
+ */
+int ide_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;
+#ifdef CONFIG_BLK_DEV_TRM290
+ unsigned int is_trm290_chipset = (HWIF(drive)->chipset == ide_trm290);
+#else
+ const int is_trm290_chipset = 0;
+#endif
+ 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 requests have (rq->bh == NULL) */
+ addr = virt_to_bus (rq->buffer);
+ 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.
+ * The hardware requires 16-bit alignment of all blocks
+ * (trm290 requires 32-bit alignment).
+ */
+ if ((addr & 3)) {
+ printk("%s: misaligned DMA buffer\n", drive->name);
+ return 0;
+ }
+ while (size) {
+ if (++count >= PRD_ENTRIES) {
+ printk("%s: DMA table too small\n", drive->name);
+ return 0; /* revert to PIO for this request */
+ } else {
+ unsigned long xcount, bcount = 0x10000 - (addr & 0xffff);
+ if (bcount > size)
+ bcount = size;
+ *table++ = addr;
+ xcount = bcount & 0xffff;
+ if (is_trm290_chipset)
+ xcount = ((xcount >> 2) - 1) << 16;
+ *table++ = xcount;
+ addr += bcount;
+ size -= bcount;
+ }
+ }
+ } while (bh != NULL);
+ if (count) {
+ if (!is_trm290_chipset)
+ *--table |= 0x80000000; /* set End-Of-Table (EOT) bit */
+ return count;
+ }
+ printk("%s: empty DMA table?\n", drive->name);
+ return 0;
+}
+
+/*
+ * ide_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.
+ * May also be invoked from trm290.c
+ */
+int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ unsigned long dma_base = hwif->dma_base;
+ unsigned int count, reading = 0;
+
+ switch (func) {
+ case ide_dma_off:
+ printk("%s: DMA disabled\n", drive->name);
+ case ide_dma_off_quietly:
+ case ide_dma_on:
+ drive->using_dma = (func == ide_dma_on);
+ return 0;
+ 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_status_bad:
+ return ((inb(dma_base+2) & 7) != 4); /* verify good DMA status */
+ case ide_dma_transferred:
+ return 0; /* NOT IMPLEMENTED: number of bytes actually transferred */
+ case ide_dma_begin:
+ outb(inb(dma_base)|1, dma_base); /* begin DMA */
+ return 0;
+ default:
+ printk("ide_dmaproc: unsupported func: %d\n", func);
+ return 1;
+ case ide_dma_read:
+ reading = 1 << 3;
+ case ide_dma_write:
+ if (!(count = ide_build_dmatable(drive)))
+ return 1; /* try PIO instead of DMA */
+ outl(virt_to_bus(hwif->dmatable), dma_base + 4); /* PRD table */
+ outb(reading, dma_base); /* specify r/w */
+ outb(inb(dma_base+2)|0x06, dma_base+2); /* clear status bits */
+ if (drive->media != ide_disk)
+ return 0;
+ 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;
+ }
+}
+
+static int config_drive_for_dma (ide_drive_t *drive)
+{
+ const char **list;
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+
+ if (id && (id->capability & 1)) {
+ /* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */
+ if (id->field_valid & 4) /* UltraDMA */
+ if ((id->dma_ultra & (id->dma_ultra >> 8) & 7))
+ return hwif->dmaproc(ide_dma_on, drive);
+ /* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */
+ if (id->field_valid & 2) /* regular DMA */
+ if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404)
+ return hwif->dmaproc(ide_dma_on, drive);
+ /* Consult the list of known "good" drives */
+ list = good_dma_drives;
+ while (*list) {
+ if (!strcmp(*list++,id->model))
+ return hwif->dmaproc(ide_dma_on, drive);
+ }
+ }
+ return hwif->dmaproc(ide_dma_off_quietly, drive);
+}
+
+void ide_setup_dma (ide_hwif_t *hwif, unsigned short dmabase, unsigned int num_ports)
+{
+ static unsigned long dmatable = 0;
+ static unsigned leftover = 0;
+
+ printk(" %s: BM-DMA at 0x%04x-0x%04x", hwif->name, dmabase, dmabase + num_ports - 1);
+ if (check_region(dmabase, num_ports)) {
+ printk(" -- ERROR, PORT ADDRESSES ALREADY IN USE\n");
+ return;
+ }
+ request_region(dmabase, num_ports, hwif->name);
+ hwif->dma_base = dmabase;
+ if (leftover < (PRD_ENTRIES * PRD_BYTES)) {
+ /*
+ * The BM-DMA uses full 32bit addr, so we can
+ * safely use __get_free_page() here instead
+ * of __get_dma_pages() -- no ISA limitations.
+ */
+ dmatable = __get_free_pages(GFP_KERNEL,1,0);
+ leftover = dmatable ? PAGE_SIZE : 0;
+ }
+ if (!dmatable) {
+ printk(" -- ERROR, UNABLE TO ALLOCATE PRD TABLE\n");
+ } else {
+ hwif->dmatable = (unsigned long *) dmatable;
+ dmatable += (PRD_ENTRIES * PRD_BYTES);
+ leftover -= (PRD_ENTRIES * PRD_BYTES);
+ hwif->dmaproc = &ide_dmaproc;
+ if (hwif->chipset != ide_trm290) {
+ byte dma_stat = inb(dmabase+2);
+ printk(", BIOS DMA settings: %s:%s %s:%s",
+ hwif->drives[0].name, (dma_stat & 0x20) ? "yes" : "no ",
+ hwif->drives[1].name, (dma_stat & 0x40) ? "yes" : "no");
+ }
+ printk("\n");
+ }
+}
+
+#ifdef CONFIG_BLK_DEV_TRM290
+extern void ide_init_trm290(byte, byte, ide_hwif_t *);
+#define INIT_TRM290 (&ide_init_trm290)
+#else
+#define INIT_TRM290 (NULL)
+#endif /* CONFIG_BLK_DEV_TRM290 */
+
+#ifdef CONFIG_BLK_DEV_OPTI621
+extern void ide_init_opti621(byte, byte, ide_hwif_t *);
+#define INIT_OPTI (&ide_init_opti621)
+#else
+#define INIT_OPTI (NULL)
+#endif /* CONFIG_BLK_DEV_OPTI621 */
+
+#define DEVID_PIIX (PCI_VENDOR_ID_INTEL |(PCI_DEVICE_ID_INTEL_82371_1 <<16))
+#define DEVID_PIIX3 (PCI_VENDOR_ID_INTEL |(PCI_DEVICE_ID_INTEL_82371SB_1 <<16))
+#define DEVID_PIIX4 (PCI_VENDOR_ID_INTEL |(PCI_DEVICE_ID_INTEL_82371AB <<16))
+#define DEVID_VP_IDE (PCI_VENDOR_ID_VIA |(PCI_DEVICE_ID_VIA_82C586_1 <<16))
+#define DEVID_PDC20246 (PCI_VENDOR_ID_PROMISE|(PCI_DEVICE_ID_PROMISE_20246 <<16))
+#define DEVID_RZ1000 (PCI_VENDOR_ID_PCTECH |(PCI_DEVICE_ID_PCTECH_RZ1000 <<16))
+#define DEVID_RZ1001 (PCI_VENDOR_ID_PCTECH |(PCI_DEVICE_ID_PCTECH_RZ1001 <<16))
+#define DEVID_CMD640 (PCI_VENDOR_ID_CMD |(PCI_DEVICE_ID_CMD_640 <<16))
+#define DEVID_CMD646 (PCI_VENDOR_ID_CMD |(PCI_DEVICE_ID_CMD_646 <<16))
+#define DEVID_SIS5513 (PCI_VENDOR_ID_SI |(PCI_DEVICE_ID_SI_5513 <<16))
+#define DEVID_OPTI (PCI_VENDOR_ID_OPTI |(PCI_DEVICE_ID_OPTI_82C621 <<16))
+#define DEVID_OPTI2 (PCI_VENDOR_ID_OPTI |(0xd568 /* from datasheets */ <<16))
+#define DEVID_TRM290 (PCI_VENDOR_ID_TEKRAM |(PCI_DEVICE_ID_TEKRAM_DC290 <<16))
+#define DEVID_NS87410 (PCI_VENDOR_ID_NS |(PCI_DEVICE_ID_NS_87410 <<16))
+#define DEVID_HT6565 (PCI_VENDOR_ID_HOLTEK |(PCI_DEVICE_ID_HOLTEK_6565 <<16))
+
+typedef struct ide_pci_enablebit_s {
+ byte reg; /* byte pci reg holding the enable-bit */
+ byte mask; /* mask to isolate the enable-bit */
+ byte val; /* value of masked reg when "enabled" */
+} ide_pci_enablebit_t;
+
+typedef struct ide_pci_device_s {
+ unsigned int id;
+ const char *name;
+ void (*init_hwif)(byte bus, byte fn, ide_hwif_t *hwif);
+ ide_pci_enablebit_t enablebits[2];
+} ide_pci_device_t;
+
+static ide_pci_device_t ide_pci_chipsets[] = {
+ {DEVID_PIIX, "PIIX", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
+ {DEVID_PIIX3, "PIIX3", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
+ {DEVID_PIIX4, "PIIX4", NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
+ {DEVID_VP_IDE, "VP_IDE", NULL, {{0x40,0x02,0x02}, {0x40,0x01,0x01}} },
+ {DEVID_PDC20246,"PDC20246", NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}} },
+ {DEVID_RZ1000, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
+ {DEVID_RZ1001, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
+ {DEVID_CMD640, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
+ {DEVID_OPTI, "OPTI", INIT_OPTI, {{0x45,0x80,0x00}, {0x40,0x08,0x00}} },
+ {DEVID_OPTI2, "OPTI2", INIT_OPTI, {{0x45,0x80,0x00}, {0x40,0x08,0x00}} },
+ {DEVID_SIS5513, "SIS5513", NULL, {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}} },
+ {DEVID_CMD646, "CMD646", NULL, {{0x00,0x00,0x00}, {0x51,0x80,0x80}} },
+ {DEVID_TRM290, "TRM290", INIT_TRM290, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
+ {DEVID_NS87410, "NS87410", NULL, {{0x43,0x08,0x08}, {0x47,0x08,0x08}} },
+ {DEVID_HT6565, "HT6565", NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} },
+ {0, "PCI_IDE", NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}} }};
+
+__initfunc(static ide_pci_device_t *lookup_devid(unsigned int devid))
+{
+ ide_pci_device_t *d = ide_pci_chipsets;
+ while (d->id && d->id != devid)
+ ++d;
+ return d;
+}
+
+/* The next two functions were stolen from cmd640.c, with
+ a few modifications */
+
+__initfunc(static void write_pcicfg_dword (byte fn, unsigned short reg, long val))
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outl_p((reg & 0xfc) | ((fn * 0x100) + 0x80000000), 0xcf8);
+ outl_p(val, (reg & 3) | 0xcfc);
+ restore_flags(flags);
+}
+
+__initfunc(static long read_pcicfg_dword (byte fn, unsigned short reg))
+{
+ long b;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outl_p((reg & 0xfc) | ((fn * 0x100) + 0x80000000), 0xcf8);
+ b = inl_p((reg & 3) | 0xcfc);
+ restore_flags(flags);
+ return b;
+}
+
+/*
+ * Search for an (apparently) unused block of I/O space
+ * of "size" bytes in length.
+ */
+__initfunc(static short find_free_region (unsigned short size))
+{
+ unsigned short i, base = 0xe800;
+ for (base = 0xe800; base > 0; base -= 0x800) {
+ if (!check_region(base,size)) {
+ for (i = 0; i < size; i++) {
+ if (inb(base+i) != 0xff)
+ goto next;
+ }
+ return base; /* success */
+ }
+ next:
+ }
+ return 0; /* failure */
+}
+
+/*
+ * Fetch the Bus-Master I/O Base-Address (BMIBA) from PCI space:
+ */
+__initfunc(static unsigned int ide_get_or_set_bmiba (byte bus, byte fn, const char *name))
+{
+ unsigned int bmiba = 0;
+ unsigned short base;
+ int rc;
+
+ if ((rc = pcibios_read_config_dword(bus, fn, 0x20, &bmiba))) {
+ printk("%s: failed to read BMIBA\n", name);
+ } else if ((bmiba &= 0xfff0) == 0) {
+ printk("%s: BMIBA is invalid (0x%04x, BIOS problem)\n", name, bmiba);
+ base = find_free_region(16);
+ if (base) {
+ printk("%s: setting BMIBA to 0x%04x\n", name, base);
+ pcibios_write_config_dword(bus, fn, 0x20, base | 1);
+ pcibios_read_config_dword(bus, fn, 0x20, &bmiba);
+ bmiba &= 0xfff0;
+ if (bmiba != base) {
+ if (bus == 0) {
+ printk("%s: operation failed, bypassing BIOS to try again\n", name);
+ write_pcicfg_dword(fn, 0x20, base | 1);
+ bmiba = read_pcicfg_dword(fn, 0x20) & 0xfff0;
+ }
+ if (bmiba != base) {
+ printk("%s: operation failed, DMA disabled\n", name);
+ bmiba = 0;
+ }
+ }
+ }
+ }
+ return bmiba;
+}
+
+/*
+ * Match a PCI IDE port against an entry in ide_hwifs[],
+ * based on io_base port if possible.
+ */
+__initfunc(ide_hwif_t *ide_match_hwif (unsigned int io_base))
+{
+ int h;
+ ide_hwif_t *hwif;
+
+ /*
+ * Look for a hwif with matching io_base specified using
+ * parameters to ide_setup().
+ */
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ hwif = &ide_hwifs[h];
+ if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) {
+ if (hwif->chipset == ide_generic)
+ return hwif; /* a perfect match */
+ }
+ }
+ /*
+ * Look for a hwif with matching io_base default value.
+ * If chipset is "ide_unknown", then claim that hwif slot.
+ * Otherwise, some other chipset has already claimed it.. :(
+ */
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ hwif = &ide_hwifs[h];
+ if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) {
+ if (hwif->chipset == ide_unknown)
+ return hwif; /* match */
+ return NULL; /* already claimed */
+ }
+ }
+ /*
+ * Okay, there is no hwif matching our io_base,
+ * so we'll just claim an unassigned slot.
+ * Give preference to claiming ide2/ide3 before ide0/ide1,
+ * just in case there's another interface yet-to-be-scanned
+ * which uses ports 1f0/170 (the ide0/ide1 defaults).
+ */
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ int hwifs[] = {2,3,1,0}; /* assign 3rd/4th before 1st/2nd */
+ hwif = &ide_hwifs[hwifs[h]];
+ if (hwif->chipset == ide_unknown)
+ return hwif; /* pick an unused entry */
+ }
+ return NULL;
+}
+
+/*
+ * ide_setup_pci_device() looks at the primary/secondary interfaces
+ * on a PCI IDE device and, if they are enabled, prepares the IDE driver
+ * for use with them. This generic code works for most PCI chipsets.
+ *
+ * One thing that is not standardized is the location of the
+ * primary/secondary interface "enable/disable" bits. For chipsets that
+ * we "know" about, this information is in the ide_pci_device_t struct;
+ * for all other chipsets, we just assume both interfaces are enabled.
+ */
+__initfunc(static void ide_setup_pci_device (byte bus, byte fn, unsigned int bmiba, ide_pci_device_t *d))
+{
+ unsigned int port, at_least_one_hwif_enabled = 0;
+ unsigned short base = 0, ctl = 0;
+ byte tmp = 0;
+ ide_hwif_t *hwif, *mate = NULL;
+
+ for (port = 0; port <= 1; ++port) {
+ ide_pci_enablebit_t *e = &(d->enablebits[port]);
+ if (e->reg) {
+ if (pcibios_read_config_byte(bus, fn, e->reg, &tmp)) {
+ printk("%s: unable to read pci reg 0x%x\n", d->name, e->reg);
+ } else if ((tmp & e->mask) != e->val)
+ continue; /* port not enabled */
+ }
+ if (pcibios_read_config_word(bus, fn, 0x14+(port*8), &ctl))
+ ctl = 0;
+ if ((ctl &= 0xfffc) == 0)
+ ctl = 0x3f4 ^ (port << 7);
+ if (pcibios_read_config_word(bus, fn, 0x10+(port*8), &base))
+ base = 0;
+ if ((base &= 0xfff8) == 0)
+ base = 0x1F0 ^ (port << 7);
+ if ((hwif = ide_match_hwif(base)) == NULL) {
+ printk("%s: no room in hwif table for port %d\n", d->name, port);
+ continue;
+ }
+ hwif->chipset = ide_pci;
+ hwif->pci_port = port;
+ if (mate) {
+ hwif->mate = mate;
+ mate->mate = hwif;
+ }
+ mate = hwif; /* for next iteration */
+ if (hwif->io_ports[IDE_DATA_OFFSET] != base) {
+ ide_init_hwif_ports(hwif->io_ports, base, NULL);
+ hwif->io_ports[IDE_CONTROL_OFFSET] = ctl + 2;
+ }
+ if (bmiba) {
+ if ((inb(bmiba+2) & 0x80)) { /* simplex DMA only? */
+ printk("%s: simplex device: DMA disabled\n", d->name);
+ } else { /* supports simultaneous DMA on both channels */
+ ide_setup_dma(hwif, bmiba + (8 * port), 8);
+ }
+ }
+ if (d->id) { /* For "known" chipsets, allow other irqs during i/o */
+ hwif->drives[0].unmask = 1;
+ hwif->drives[1].unmask = 1;
+ }
+ if (d->init_hwif) /* Call chipset-specific routine for each enabled hwif */
+ d->init_hwif(bus, fn, hwif);
+ at_least_one_hwif_enabled = 1;
+ }
+ if (!at_least_one_hwif_enabled)
+ printk("%s: neither IDE port enabled (BIOS)\n", d->name);
+}
+
+/*
+ * ide_scan_pci_device() examines all functions of a PCI device,
+ * looking for IDE interfaces and/or devices in ide_pci_chipsets[].
+ */
+__initfunc(static inline void ide_scan_pci_device (unsigned int bus, unsigned int fn))
+{
+ unsigned int devid, ccode;
+ unsigned short pcicmd, class;
+ ide_pci_device_t *d;
+ byte hedt, progif;
+
+ if (pcibios_read_config_byte(bus, fn, 0x0e, &hedt))
+ hedt = 0;
+ do {
+ if (pcibios_read_config_dword(bus, fn, 0x00, &devid)
+ || devid == 0xffffffff
+ || pcibios_read_config_dword(bus, fn, 0x08, &ccode))
+ return;
+ d = lookup_devid(devid);
+ if (d->name == NULL) /* some chips (cmd640, rz1000) are handled elsewhere */
+ continue;
+ progif = (ccode >> 8) & 0xff;
+ class = ccode >> 16;
+ if (d->id || class == PCI_CLASS_STORAGE_IDE) {
+ if (d->id)
+ printk("%s: IDE device on PCI bus %d function %d\n", d->name, bus, fn);
+ else
+ printk("%s: unknown IDE device on PCI bus %d function %d, VID=%04x, DID=%04x\n",
+ d->name, bus, fn, devid & 0xffff, devid >> 16);
+ /*
+ * See if IDE ports are enabled
+ */
+ if (pcibios_read_config_word(bus, fn, 0x04, &pcicmd)) {
+ printk("%s: error accessing PCICMD\n", d->name);
+ } else if ((pcicmd & 1) == 0) {
+ printk("%s: device disabled (BIOS)\n", d->name);
+ } else {
+ unsigned int bmiba = 0;
+ /*
+ * Check for Bus-Master DMA capability
+ */
+ if (d->id == DEVID_PDC20246 || (class == PCI_CLASS_STORAGE_IDE && (progif & 0x80))) {
+ if ((!(pcicmd & 4) || !(bmiba = ide_get_or_set_bmiba(bus, fn, d->name)))) {
+ printk("%s: Bus-Master DMA disabled (BIOS), pcicmd=0x%04x, progif=0x%02x, bmiba=0x%04x\n", d->name, pcicmd, progif, bmiba);
+ }
+ }
+ /*
+ * Setup the IDE ports
+ */
+ ide_setup_pci_device(bus, fn, bmiba, d);
+ }
+ }
+ } while (hedt == 0x80 && (++fn & 7));
+}
+
+/*
+ * ide_scan_pcibus() gets invoked at boot time from ide.c
+ */
+__initfunc(void ide_scan_pcibus (void))
+{
+ unsigned int bus, dev;
+
+ if (!pcibios_present())
+ return;
+ for (bus = 0; bus <= 255; ++bus) {
+ for (dev = 0; dev <= 31; ++dev) {
+ ide_scan_pci_device(bus, dev << 3);
+ }
+ }
+}
+
diff --git a/drivers/block/pdc4030.c b/drivers/block/pdc4030.c
new file mode 100644
index 000000000..c161e58b6
--- /dev/null
+++ b/drivers/block/pdc4030.c
@@ -0,0 +1,365 @@
+/* -*- linux-c -*-
+ * linux/drivers/block/pdc4030.c Version 0.08 Nov 30, 1997
+ *
+ * Copyright (C) 1995-1998 Linus Torvalds & authors (see below)
+ */
+
+/*
+ * Principal Author/Maintainer: peterd@pnd-pc.demon.co.uk
+ *
+ * This file provides support for the second port and cache of Promise
+ * IDE interfaces, e.g. DC4030, DC5030.
+ *
+ * Thanks are due to Mark Lord for advice and patiently answering stupid
+ * questions, and all those mugs^H^H^H^Hbrave souls who've tested this.
+ *
+ * Version 0.01 Initial version, #include'd in ide.c rather than
+ * compiled separately.
+ * Reads use Promise commands, writes as before. Drives
+ * on second channel are read-only.
+ * Version 0.02 Writes working on second channel, reads on both
+ * channels. Writes fail under high load. Suspect
+ * transfers of >127 sectors don't work.
+ * Version 0.03 Brought into line with ide.c version 5.27.
+ * Other minor changes.
+ * Version 0.04 Updated for ide.c version 5.30
+ * Changed initialization strategy
+ * Version 0.05 Kernel integration. -ml
+ * Version 0.06 Ooops. Add hwgroup to direct call of ide_intr() -ml
+ * Version 0.07 Added support for DC4030 variants
+ * Secondary interface autodetection
+ * Version 0.08 Renamed to pdc4030.c
+ */
+
+/*
+ * Once you've compiled it in, you'll have to also enable the interface
+ * setup routine from the kernel command line, as in
+ *
+ * 'linux ide0=dc4030'
+ *
+ * As before, it seems that somewhere around 3Megs when writing, bad things
+ * start to happen [timeouts/retries -ml]. If anyone can give me more feedback,
+ * I'd really appreciate it. [email: peterd@pnd-pc.demon.co.uk]
+ *
+ */
+
+
+#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 <asm/irq.h>
+#include "ide.h"
+#include "pdc4030.h"
+
+/* This is needed as the controller may not interrupt if the required data is
+available in the cache. We have to simulate an interrupt. Ugh! */
+
+extern void ide_intr(int, void *dev_id, struct pt_regs*);
+
+/*
+ * promise_selectproc() is invoked by ide.c
+ * in preparation for access to the specified drive.
+ */
+static void promise_selectproc (ide_drive_t *drive)
+{
+ unsigned int number;
+
+ OUT_BYTE(drive->select.all,IDE_SELECT_REG);
+ udelay(1); /* paranoia */
+ number = ((HWIF(drive)->is_pdc4030_2)<<1) + drive->select.b.unit;
+ OUT_BYTE(number,IDE_FEATURE_REG);
+}
+
+/*
+ * pdc4030_cmd handles the set of vendor specific commands that are initiated
+ * by command F0. They all have the same success/failure notification.
+ */
+int pdc4030_cmd(ide_drive_t *drive, byte cmd)
+{
+ unsigned long timeout, timer;
+ byte status_val;
+
+ promise_selectproc(drive); /* redundant? */
+ OUT_BYTE(0xF3,IDE_SECTOR_REG);
+ OUT_BYTE(cmd,IDE_SELECT_REG);
+ OUT_BYTE(PROMISE_EXTENDED_COMMAND,IDE_COMMAND_REG);
+ timeout = HZ * 10;
+ timeout += jiffies;
+ do {
+ if(jiffies > timeout) {
+ return 2; /* device timed out */
+ }
+ /* This is out of delay_10ms() */
+ /* Delays at least 10ms to give interface a chance */
+ timer = jiffies + (HZ + 99)/100 + 1;
+ while (timer > jiffies);
+ status_val = IN_BYTE(IDE_SECTOR_REG);
+ } while (status_val != 0x50 && status_val != 0x70);
+
+ if(status_val == 0x50)
+ return 0; /* device returned success */
+ else
+ return 1; /* device returned failure */
+}
+
+ide_hwif_t *hwif_required = NULL;
+
+void setup_pdc4030 (ide_hwif_t *hwif)
+{
+ hwif_required = hwif;
+}
+
+/*
+init_pdc4030: Test for presence of a Promise caching controller card.
+Returns: 0 if no Promise card present at this io_base
+ 1 if Promise card found
+*/
+int init_pdc4030 (void)
+{
+ ide_hwif_t *hwif = hwif_required;
+ ide_drive_t *drive;
+ ide_hwif_t *second_hwif;
+ struct dc_ident ident;
+ int i;
+
+ if (!hwif) return 0;
+
+ drive = &hwif->drives[0];
+ second_hwif = &ide_hwifs[hwif->index+1];
+ if(hwif->is_pdc4030_2) /* we've already been found ! */
+ return 1;
+
+ if(IN_BYTE(IDE_NSECTOR_REG) == 0xFF || IN_BYTE(IDE_SECTOR_REG) == 0xFF)
+ {
+ return 0;
+ }
+ OUT_BYTE(0x08,IDE_CONTROL_REG);
+ if(pdc4030_cmd(drive,PROMISE_GET_CONFIG)) {
+ return 0;
+ }
+ if(ide_wait_stat(drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) {
+ printk("%s: Failed Promise read config!\n",hwif->name);
+ return 0;
+ }
+ ide_input_data(drive,&ident,SECTOR_WORDS);
+ if(ident.id[1] != 'P' || ident.id[0] != 'T') {
+ return 0;
+ }
+ printk("%s: Promise caching controller, ",hwif->name);
+ switch(ident.type) {
+ case 0x43: printk("DC4030VL-2, "); break;
+ case 0x41: printk("DC4030VL-1, "); break;
+ case 0x40: printk("DC4030VL, "); break;
+ default: printk("unknown - type 0x%02x - please report!\n"
+ ,ident.type);
+ return 0;
+ }
+ printk("%dKB cache, ",(int)ident.cache_mem);
+ switch(ident.irq) {
+ case 0x00: hwif->irq = 14; break;
+ case 0x01: hwif->irq = 12; break;
+ default: hwif->irq = 15; break;
+ }
+ printk("on IRQ %d\n",hwif->irq);
+ hwif->chipset = second_hwif->chipset = ide_pdc4030;
+ hwif->mate = second_hwif;
+ second_hwif->mate = hwif;
+ hwif->selectproc = second_hwif->selectproc = &promise_selectproc;
+/* Shift the remaining interfaces down by one */
+ for (i=MAX_HWIFS-1 ; i > hwif->index+1 ; i--) {
+ ide_hwif_t *h = &ide_hwifs[i];
+
+ printk("Shifting i/f %d values to i/f %d\n",i-1,i);
+ ide_init_hwif_ports(h->io_ports, (h-1)->io_ports[IDE_DATA_OFFSET], NULL);
+ h->io_ports[IDE_CONTROL_OFFSET] = (h-1)->io_ports[IDE_CONTROL_OFFSET];
+ h->noprobe = (h-1)->noprobe;
+ }
+ second_hwif->is_pdc4030_2 = 1;
+ ide_init_hwif_ports(second_hwif->io_ports, hwif->io_ports[IDE_DATA_OFFSET], NULL);
+ second_hwif->io_ports[IDE_CONTROL_OFFSET] = hwif->io_ports[IDE_CONTROL_OFFSET];
+ second_hwif->irq = hwif->irq;
+ for (i=0; i<2 ; i++) {
+ hwif->drives[i].io_32bit = 3;
+ second_hwif->drives[i].io_32bit = 3;
+ if(!ident.current_tm[i+2].cyl) second_hwif->drives[i].noprobe=1;
+ }
+ return 1;
+}
+
+/*
+ * promise_read_intr() is the handler for disk read/multread interrupts
+ */
+static void promise_read_intr (ide_drive_t *drive)
+{
+ byte stat;
+ int i;
+ unsigned int sectors_left, sectors_avail, nsect;
+ struct request *rq;
+
+ if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
+ ide_error(drive, "promise_read_intr", stat);
+ return;
+ }
+
+read_again:
+ do {
+ sectors_left = IN_BYTE(IDE_NSECTOR_REG);
+ IN_BYTE(IDE_SECTOR_REG);
+ } while (IN_BYTE(IDE_NSECTOR_REG) != sectors_left);
+ rq = HWGROUP(drive)->rq;
+ sectors_avail = rq->nr_sectors - sectors_left;
+
+read_next:
+ rq = HWGROUP(drive)->rq;
+ if ((nsect = rq->current_nr_sectors) > sectors_avail)
+ nsect = sectors_avail;
+ sectors_avail -= nsect;
+ ide_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
+#ifdef DEBUG
+ printk("%s: promise_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 (sectors_avail)
+ goto read_next;
+ stat = GET_STAT();
+ if(stat & DRQ_STAT)
+ goto read_again;
+ if(stat & BUSY_STAT) {
+ ide_set_handler (drive, &promise_read_intr, WAIT_CMD);
+ return;
+ }
+ printk("Ah! promise read intr: sectors left !DRQ !BUSY\n");
+ ide_error(drive, "promise read intr", stat);
+ }
+}
+
+/*
+ * promise_write_pollfunc() is the handler for disk write completion polling.
+ */
+static void promise_write_pollfunc (ide_drive_t *drive)
+{
+ int i;
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ struct request *rq;
+
+ if (IN_BYTE(IDE_NSECTOR_REG) != 0) {
+ if (jiffies < hwgroup->poll_timeout) {
+ ide_set_handler (drive, &promise_write_pollfunc, 1);
+ return; /* continue polling... */
+ }
+ printk("%s: write timed-out!\n",drive->name);
+ ide_error (drive, "write timeout", GET_STAT());
+ return;
+ }
+
+ ide_multwrite(drive, 4);
+ rq = hwgroup->rq;
+ for (i = rq->nr_sectors; i > 0;) {
+ i -= rq->current_nr_sectors;
+ ide_end_request(1, hwgroup);
+ }
+ return;
+}
+
+/*
+ * promise_write() transfers a block of one or more sectors of data to a
+ * drive as part of a disk write operation. All but 4 sectors are transfered
+ * in the first attempt, then the interface is polled (nicely!) for completion
+ * before the final 4 sectors are transfered. Don't ask me why, but this is
+ * how it's done in the drivers for other O/Ses. There is no interrupt
+ * generated on writes, which is why we have to do it like this.
+ */
+static void promise_write (ide_drive_t *drive)
+{
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ struct request *rq = &hwgroup->wrq;
+ int i;
+
+ if (rq->nr_sectors > 4) {
+ ide_multwrite(drive, rq->nr_sectors - 4);
+ hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+ ide_set_handler (drive, &promise_write_pollfunc, 1);
+ return;
+ } else {
+ ide_multwrite(drive, rq->nr_sectors);
+ rq = hwgroup->rq;
+ for (i = rq->nr_sectors; i > 0;) {
+ i -= rq->current_nr_sectors;
+ ide_end_request(1, hwgroup);
+ }
+ }
+}
+
+/*
+ * do_pdc4030_io() is called from do_rw_disk, having had the block number
+ * already set up. It issues a READ or WRITE command to the Promise
+ * controller, assuming LBA has been used to set up the block number.
+ */
+void do_pdc4030_io (ide_drive_t *drive, struct request *rq)
+{
+ unsigned long timeout;
+ byte stat;
+
+ if (rq->cmd == READ) {
+ ide_set_handler(drive, &promise_read_intr, WAIT_CMD);
+ OUT_BYTE(PROMISE_READ, IDE_COMMAND_REG);
+/* The card's behaviour is odd at this point. If the data is
+ available, DRQ will be true, and no interrupt will be
+ generated by the card. If this is the case, we need to simulate
+ an interrupt. Ugh! Otherwise, if an interrupt will occur, bit0
+ of the SELECT register will be high, so we can just return and
+ be interrupted.*/
+ timeout = jiffies + HZ/20; /* 50ms wait */
+ do {
+ stat=GET_STAT();
+ if(stat & DRQ_STAT) {
+/* unsigned long flags;
+ save_flags(flags);
+ cli();
+ disable_irq(HWIF(drive)->irq);
+*/
+ ide_intr(HWIF(drive)->irq,HWGROUP(drive),NULL);
+/* enable_irq(HWIF(drive)->irq);
+ restore_flags(flags);
+*/
+ return;
+ }
+ if(IN_BYTE(IDE_SELECT_REG) & 0x01)
+ return;
+ udelay(1);
+ } while (jiffies < timeout);
+ printk("%s: reading: No DRQ and not waiting - Odd!\n",
+ drive->name);
+ return;
+ }
+ if (rq->cmd == WRITE) {
+ OUT_BYTE(PROMISE_WRITE, IDE_COMMAND_REG);
+ if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
+ printk("%s: no DRQ after issuing PROMISE_WRITE\n", drive->name);
+ return;
+ }
+ if (!drive->unmask)
+ cli();
+ HWGROUP(drive)->wrq = *rq; /* scratchpad */
+ promise_write(drive);
+ return;
+ }
+ printk("%s: bad command: %d\n", drive->name, rq->cmd);
+ ide_end_request(0, HWGROUP(drive));
+}
diff --git a/drivers/block/pdc4030.h b/drivers/block/pdc4030.h
new file mode 100644
index 000000000..9f08da5aa
--- /dev/null
+++ b/drivers/block/pdc4030.h
@@ -0,0 +1,44 @@
+/*
+ * linux/drivers/block/pdc4030.h
+ *
+ * Copyright (C) 1995-1998 Linus Torvalds & authors
+ */
+
+/*
+ * Principal author: Peter Denison <peterd@pnd-pc.demon.co.uk>
+ */
+
+#ifndef IDE_PROMISE_H
+#define IDE_PROMISE_H
+
+#define PROMISE_EXTENDED_COMMAND 0xF0
+#define PROMISE_READ 0xF2
+#define PROMISE_WRITE 0xF3
+/* Extended commands - main command code = 0xf0 */
+#define PROMISE_GET_CONFIG 0x10
+#define PROMISE_IDENTIFY 0x20
+
+struct translation_mode {
+ u16 cyl;
+ u8 head;
+ u8 sect;
+};
+
+struct dc_ident {
+ u8 type;
+ u8 unknown1;
+ u8 hw_revision;
+ u8 firmware_major;
+ u8 firmware_minor;
+ u8 bios_address;
+ u8 irq;
+ u8 unknown2;
+ u16 cache_mem;
+ u16 unknown3;
+ u8 id[2];
+ u16 info;
+ struct translation_mode current_tm[4];
+ u8 pad[SECTOR_WORDS*4 - 32];
+};
+
+#endif IDE_PROMISE_H
diff --git a/drivers/block/promise.c b/drivers/block/promise.c
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/block/promise.c
+++ /dev/null
diff --git a/drivers/block/promise.h b/drivers/block/promise.h
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/block/promise.h
+++ /dev/null
diff --git a/drivers/block/triton.c b/drivers/block/triton.c
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/block/triton.c
+++ /dev/null
diff --git a/drivers/block/trm290.c b/drivers/block/trm290.c
new file mode 100644
index 000000000..7b8fb308c
--- /dev/null
+++ b/drivers/block/trm290.c
@@ -0,0 +1,227 @@
+/*
+ * linux/drivers/block/trm290.c Version 1.00 December 3, 1997
+ *
+ * Copyright (c) 1997-1998 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 Tekram TRM290 chip, used on a variety of PCI IDE add-on boards,
+ * including a "Precision Instruments" board. The TRM290 pre-dates
+ * the sff-8038 standard (ide-dma.c) by a few months, and differs
+ * significantly enough to warrant separate routines for some functions,
+ * while re-using others from ide-dma.c.
+ *
+ * EXPERIMENTAL! It works for me (a sample of one).
+ *
+ * Works reliably for me in DMA mode (READs only),
+ * DMA WRITEs are disabled by default (see #define below);
+ *
+ * DMA is not enabled automatically for this chipset,
+ * but can be turned on manually (with "hdparm -d1") at run time.
+ *
+ * I need volunteers with "spare" drives for further testing
+ * and development, and maybe to help figure out the peculiarities.
+ * Even knowing the registers (below), some things behave strangely.
+ */
+
+#define TRM290_NO_DMA_WRITES /* DMA writes seem unreliable sometimes */
+
+/*
+ * TRM-290 PCI-IDE2 Bus Master Chip
+ * ================================
+ * The configuration registers are addressed in normal I/O port space
+ * and are used as follows:
+ *
+ * 0x3df2 when WRITTEN: chiptest register (byte, write-only)
+ * bit7 must always be written as "1"
+ * bits6-2 undefined
+ * bit1 1=legacy_compatible_mode, 0=native_pci_mode
+ * bit0 1=test_mode, 0=normal(default)
+ *
+ * 0x3df2 when READ: status register (byte, read-only)
+ * bits7-2 undefined
+ * bit1 channel0 busmaster interrupt status 0=none, 1=asserted
+ * bit0 channel0 interrupt status 0=none, 1=asserted
+ *
+ * 0x3df3 Interrupt mask register
+ * bits7-5 undefined
+ * bit4 legacy_header: 1=present, 0=absent
+ * bit3 channel1 busmaster interrupt status 0=none, 1=asserted (read only)
+ * bit2 channel1 interrupt status 0=none, 1=asserted (read only)
+ * bit1 channel1 interrupt mask: 1=masked, 0=unmasked(default)
+ * bit0 channel0 interrupt mask: 1=masked, 0=unmasked(default)
+ *
+ * 0x3df1 "CPR" Config Pointer Register (byte)
+ * bit7 1=autoincrement CPR bits 2-0 after each access of CDR
+ * bit6 1=min. 1 wait-state posted write cycle (default), 0=0 wait-state
+ * bit5 0=enabled master burst access (default), 1=disable (write only)
+ * bit4 PCI DEVSEL# timing select: 1=medium(default), 0=fast
+ * bit3 0=primary IDE channel, 1=secondary IDE channel
+ * bits2-0 register index for accesses through CDR port
+ *
+ * 0x3df0 "CDR" Config Data Register (word)
+ * two sets of seven config registers,
+ * selected by CPR bit 3 (channel) and CPR bits 2-0 (index 0 to 6),
+ * each index defined below:
+ *
+ * Index-0 Base address register for command block (word)
+ * defaults: 0x1f0 for primary, 0x170 for secondary
+ *
+ * Index-1 general config register (byte)
+ * bit7 1=DMA enable, 0=DMA disable
+ * bit6 1=activate IDE_RESET, 0=no action (default)
+ * bit5 1=enable IORDY, 0=disable IORDY (default)
+ * bit4 0=16-bit data port(default), 1=8-bit (XT) data port
+ * bit3 interrupt polarity: 1=active_low, 0=active_high(default)
+ * bit2 power-saving-mode(?): 1=enable, 0=disable(default) (write only)
+ * bit1 bus_master_mode(?): 1=enable, 0=disable(default)
+ * bit0 enable_io_ports: 1=enable(default), 0=disable
+ *
+ * Index-2 read-ahead counter preload bits 0-7 (byte, write only)
+ * bits7-0 bits7-0 of readahead count
+ *
+ * Index-3 read-ahead config register (byte, write only)
+ * bit7 1=enable_readahead, 0=disable_readahead(default)
+ * bit6 1=clear_FIFO, 0=no_action
+ * bit5 undefined
+ * bit4 mode4 timing control: 1=enable, 0=disable(default)
+ * bit3 undefined
+ * bit2 undefined
+ * bits1-0 bits9-8 of read-ahead count
+ *
+ * Index-4 base address register for control block (word)
+ * defaults: 0x3f6 for primary, 0x376 for secondary
+ *
+ * Index-5 data port timings (shared by both drives) (byte)
+ * standard PCI "clk" (clock) counts, default value = 0xf5
+ *
+ * bits7-6 setup time: 00=1clk, 01=2clk, 10=3clk, 11=4clk
+ * bits5-3 hold time: 000=1clk, 001=2clk, 010=3clk,
+ * 011=4clk, 100=5clk, 101=6clk,
+ * 110=8clk, 111=12clk
+ * bits2-0 active time: 000=2clk, 001=3clk, 010=4clk,
+ * 011=5clk, 100=6clk, 101=8clk,
+ * 110=12clk, 111=16clk
+ *
+ * Index-6 command/control port timings (shared by both drives) (byte)
+ * same layout as Index-5, default value = 0xde
+ *
+ * Suggested CDR programming for PIO mode0 (600ns):
+ * 0x01f0,0x21,0xff,0x80,0x03f6,0xf5,0xde ; primary
+ * 0x0170,0x21,0xff,0x80,0x0376,0xf5,0xde ; secondary
+ *
+ * Suggested CDR programming for PIO mode3 (180ns):
+ * 0x01f0,0x21,0xff,0x80,0x03f6,0x09,0xde ; primary
+ * 0x0170,0x21,0xff,0x80,0x0376,0x09,0xde ; secondary
+ *
+ * Suggested CDR programming for PIO mode4 (120ns):
+ * 0x01f0,0x21,0xff,0x80,0x03f6,0x00,0xde ; primary
+ * 0x0170,0x21,0xff,0x80,0x0376,0x00,0xde ; secondary
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#include <linux/hdreg.h>
+#include <asm/io.h>
+
+#include "ide.h"
+
+static void select_dma_or_pio(ide_hwif_t *hwif, int dma)
+{
+ static int previous[2] = {-1,-1};
+ unsigned long flags;
+
+ if (previous[hwif->pci_port] != dma) {
+ unsigned short cfg1 = dma ? 0xa3 : 0x21;
+ previous[hwif->pci_port] = dma;
+ save_flags(flags);
+ cli();
+ outb(0x51|(hwif->pci_port<<3),0x3df1);
+ outw(cfg1,0x3df0);
+ restore_flags(flags);
+ }
+}
+
+/*
+ * trm290_dma_intr() is the handler for trm290 disk read/write DMA interrupts
+ */
+static void trm290_dma_intr (ide_drive_t *drive)
+{
+ byte stat;
+ int i;
+ struct request *rq = HWGROUP(drive)->rq;
+
+ stat = GET_STAT(); /* get drive status */
+ if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
+ unsigned short dma_stat = inw(HWIF(drive)->dma_base + 2);
+ if (dma_stat == 0x00ff) {
+ 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 trm290 DMA status: 0x%04x\n", drive->name, dma_stat);
+ }
+ sti();
+ ide_error(drive, "dma_intr", stat);
+}
+
+static int trm290_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ unsigned int count, reading = 1 << 1;
+
+ if (drive->media == ide_disk) {
+ switch (func) {
+ case ide_dma_write:
+ reading = 0;
+#ifdef TRM290_NO_DMA_WRITES
+ break; /* always use PIO for writes */
+#endif
+ case ide_dma_read:
+ if (!(count = ide_build_dmatable(drive)))
+ break; /* try PIO instead of DMA */
+ select_dma_or_pio(hwif, 1); /* select DMA mode */
+ outl_p(virt_to_bus(hwif->dmatable)|reading, hwif->dma_base);
+ outw((count * 2) - 1, hwif->dma_base+2); /* start DMA */
+ ide_set_handler(drive, &trm290_dma_intr, WAIT_CMD);
+ OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
+ return 0;
+ default:
+ return ide_dmaproc(func, drive);
+ }
+ }
+ select_dma_or_pio(hwif, 0); /* force PIO mode for this operation */
+ return 1;
+}
+
+static void trm290_selectproc (ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+
+ select_dma_or_pio(hwif, drive->using_dma);
+ OUT_BYTE(drive->select.all, hwif->io_ports[IDE_SELECT_OFFSET]);
+}
+
+/*
+ * Invoked from ide-dma.c at boot time.
+ */
+__initfunc(void ide_init_trm290 (byte bus, byte fn, ide_hwif_t *hwif))
+{
+ hwif->chipset = ide_trm290;
+ hwif->selectproc = trm290_selectproc;
+ hwif->drives[0].autotune = 2; /* play it safe */
+ hwif->drives[1].autotune = 2; /* play it safe */
+ ide_setup_dma(hwif, hwif->pci_port ? 0x3d74 : 0x3df4, 2);
+ hwif->dmaproc = &trm290_dmaproc;
+}
diff --git a/drivers/cdrom/aztcd.h b/drivers/cdrom/aztcd.h
new file mode 100644
index 000000000..057501e31
--- /dev/null
+++ b/drivers/cdrom/aztcd.h
@@ -0,0 +1,162 @@
+/* $Id: aztcd.h,v 2.60 1997/11/29 09:51:22 root Exp root $
+ *
+ * Definitions for a AztechCD268 CD-ROM interface
+ * Copyright (C) 1994-98 Werner Zimmermann
+ *
+ * based on Mitsumi CDROM driver by Martin Harriss
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * History: W.Zimmermann adaption to Aztech CD268-01A Version 1.3
+ * October 1994 Email: Werner.Zimmermann@fht-esslingen.de
+ */
+
+/* *** change this to set the I/O port address of your CD-ROM drive,
+ set to '-1', if you want autoprobing */
+#define AZT_BASE_ADDR -1
+
+/* list of autoprobing addresses (not more than 15), last value must be 0x000
+ Note: Autoprobing is only enabled, if AZT_BASE_ADDR is set to '-1' ! */
+#define AZT_BASE_AUTO { 0x320, 0x300, 0x310, 0x330, 0x000 }
+
+/* Uncomment this, if your CDROM is connected to a Soundwave32-soundcard
+ and configure AZT_BASE_ADDR and AZT_SW32_BASE_ADDR */
+/*#define AZT_SW32 1
+*/
+
+#ifdef AZT_SW32
+#define AZT_SW32_BASE_ADDR 0x220 /*I/O port base address of your soundcard*/
+#endif
+
+/* Set this to 1, if you want your tray to be locked, set to 0 to prevent tray
+ from locking */
+#define AZT_ALLOW_TRAY_LOCK 1
+
+/*Set this to 1 to allow auto-eject when unmounting a disk, set to 0, if you
+ don't want the auto-eject feature*/
+#define AZT_AUTO_EJECT 0
+
+/*Set this to 1, if you want to use incompatible ioctls for reading in raw and
+ cooked mode */
+#define AZT_PRIVATE_IOCTLS 1
+
+/*Set this to 1, if you want multisession support by the ISO fs. Even if you set
+ this value to '0' you can use multisession CDs. In that case the drive's firm-
+ ware will do the appropriate redirection automatically. The CD will then look
+ like a single session CD (but nevertheless all data may be read). Please read
+ chapter '5.1 Multisession support' in README.aztcd for details. Normally it's
+ uncritical to leave this setting untouched */
+#define AZT_MULTISESSION 1
+
+/*Uncomment this, if you are using a linux kernel version prior to 2.1.0 */
+/*#define AZT_KERNEL_PRIOR_2_1 */
+
+/*---------------------------------------------------------------------------*/
+/*-----nothing to be configured for normal applications below this line------*/
+
+
+/* Increase this if you get lots of timeouts; if you get kernel panic, replace
+ STEN_LOW_WAIT by STEN_LOW in the source code */
+#define AZT_STATUS_DELAY 400 /*for timer wait, STEN_LOW_WAIT*/
+#define AZT_TIMEOUT 8000000 /*for busy wait STEN_LOW, DTEN_LOW*/
+#define AZT_FAST_TIMEOUT 10000 /*for reading the version string*/
+
+/* number of times to retry a command before giving up */
+#define AZT_RETRY_ATTEMPTS 3
+
+/* port access macros */
+#define CMD_PORT azt_port
+#define DATA_PORT azt_port
+#define STATUS_PORT azt_port+1
+#define MODE_PORT azt_port+2
+#ifdef AZT_SW32
+ #define AZT_SW32_INIT (unsigned int) (0xFF00 & (AZT_BASE_ADDR*16))
+ #define AZT_SW32_CONFIG_REG AZT_SW32_BASE_ADDR+0x16 /*Soundwave32 Config. Register*/
+ #define AZT_SW32_ID_REG AZT_SW32_BASE_ADDR+0x04 /*Soundwave32 ID Version Register*/
+#endif
+
+/* status bits */
+#define AST_CMD_CHECK 0x80 /* 1 = command error */
+#define AST_DOOR_OPEN 0x40 /* 1 = door is open */
+#define AST_NOT_READY 0x20 /* 1 = no disk in the drive */
+#define AST_DSK_CHG 0x02 /* 1 = disk removed or changed */
+#define AST_MODE 0x01 /* 0=MODE1, 1=MODE2 */
+#define AST_MODE_BITS 0x1C /* Mode Bits */
+#define AST_INITIAL 0x0C /* initial, only valid ... */
+#define AST_BUSY 0x04 /* now playing, only valid
+ in combination with mode
+ bits */
+/* flag bits */
+#define AFL_DATA 0x02 /* data available if low */
+#define AFL_STATUS 0x04 /* status available if low */
+#define AFL_OP_OK 0x01 /* OP_OK command correct*/
+#define AFL_PA_OK 0x02 /* PA_OK parameter correct*/
+#define AFL_OP_ERR 0x05 /* error in command*/
+#define AFL_PA_ERR 0x06 /* error in parameters*/
+#define POLLED 0x04 /* polled mode */
+
+/* commands */
+#define ACMD_SOFT_RESET 0x10 /* reset drive */
+#define ACMD_PLAY_READ 0x20 /* read data track in cooked mode */
+#define ACMD_PLAY_READ_RAW 0x21 /* reading in raw mode*/
+#define ACMD_SEEK 0x30 /* seek msf address*/
+#define ACMD_SEEK_TO_LEADIN 0x31 /* seek to leadin track*/
+#define ACMD_GET_ERROR 0x40 /* get error code */
+#define ACMD_GET_STATUS 0x41 /* get status */
+#define ACMD_GET_Q_CHANNEL 0x50 /* read info from q channel */
+#define ACMD_EJECT 0x60 /* eject/open tray */
+#define ACMD_CLOSE 0x61 /* close tray */
+#define ACMD_LOCK 0x71 /* lock tray closed */
+#define ACMD_UNLOCK 0x72 /* unlock tray */
+#define ACMD_PAUSE 0x80 /* pause */
+#define ACMD_STOP 0x81 /* stop play */
+#define ACMD_PLAY_AUDIO 0x90 /* play audio track */
+#define ACMD_SET_VOLUME 0x93 /* set audio level */
+#define ACMD_GET_VERSION 0xA0 /* get firmware version */
+#define ACMD_SET_DISK_TYPE 0xA1 /* set disk data mode */
+
+#define MAX_TRACKS 104
+
+struct msf {
+ unsigned char min;
+ unsigned char sec;
+ unsigned char frame;
+};
+
+struct azt_Play_msf {
+ struct msf start;
+ struct msf end;
+};
+
+struct azt_DiskInfo {
+ unsigned char first;
+ unsigned char next;
+ unsigned char last;
+ struct msf diskLength;
+ struct msf firstTrack;
+ unsigned char multi;
+ struct msf nextSession;
+ struct msf lastSession;
+ unsigned char xa;
+ unsigned char audio;
+};
+
+struct azt_Toc {
+ unsigned char ctrl_addr;
+ unsigned char track;
+ unsigned char pointIndex;
+ struct msf trackTime;
+ struct msf diskTime;
+};
diff --git a/drivers/cdrom/cdi.c b/drivers/cdrom/cdi.c
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/cdrom/cdi.c
+++ /dev/null
diff --git a/drivers/cdrom/cdu31a.h b/drivers/cdrom/cdu31a.h
new file mode 100644
index 000000000..a04d0b3a2
--- /dev/null
+++ b/drivers/cdrom/cdu31a.h
@@ -0,0 +1,411 @@
+/*
+ * Definitions for a Sony interface CDROM drive.
+ *
+ * Corey Minyard (minyard@wf-rch.cirr.com)
+ *
+ * Copyright (C) 1993 Corey Minyard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * General defines.
+ */
+#define SONY_XA_DISK_TYPE 0x20
+
+/*
+ * Offsets (from the base address) and bits for the various write registers
+ * of the drive.
+ */
+#define SONY_CMD_REG_OFFSET 0
+#define SONY_PARAM_REG_OFFSET 1
+#define SONY_WRITE_REG_OFFSET 2
+#define SONY_CONTROL_REG_OFFSET 3
+# define SONY_ATTN_CLR_BIT 0x01
+# define SONY_RES_RDY_CLR_BIT 0x02
+# define SONY_DATA_RDY_CLR_BIT 0x04
+# define SONY_ATTN_INT_EN_BIT 0x08
+# define SONY_RES_RDY_INT_EN_BIT 0x10
+# define SONY_DATA_RDY_INT_EN_BIT 0x20
+# define SONY_PARAM_CLR_BIT 0x40
+# define SONY_DRIVE_RESET_BIT 0x80
+
+/*
+ * Offsets (from the base address) and bits for the various read registers
+ * of the drive.
+ */
+#define SONY_STATUS_REG_OFFSET 0
+# define SONY_ATTN_BIT 0x01
+# define SONY_RES_RDY_BIT 0x02
+# define SONY_DATA_RDY_BIT 0x04
+# define SONY_ATTN_INT_ST_BIT 0x08
+# define SONY_RES_RDY_INT_ST_BIT 0x10
+# define SONY_DATA_RDY_INT_ST_BIT 0x20
+# define SONY_DATA_REQUEST_BIT 0x40
+# define SONY_BUSY_BIT 0x80
+#define SONY_RESULT_REG_OFFSET 1
+#define SONY_READ_REG_OFFSET 2
+#define SONY_FIFOST_REG_OFFSET 3
+# define SONY_PARAM_WRITE_RDY_BIT 0x01
+# define SONY_PARAM_REG_EMPTY_BIT 0x02
+# define SONY_RES_REG_NOT_EMP_BIT 0x04
+# define SONY_RES_REG_FULL_BIT 0x08
+
+#define LOG_START_OFFSET 150 /* Offset of first logical sector */
+
+#define SONY_DETECT_TIMEOUT (8*HZ/10) /* Maximum amount of time
+ that drive detection code
+ will wait for response
+ from drive (in 1/100th's
+ of seconds). */
+
+#define SONY_JIFFIES_TIMEOUT 1000 /* Maximum number of times the
+ drive will wait/try for an
+ operation */
+#define SONY_RESET_TIMEOUT 100 /* Maximum number of times the
+ drive will wait/try a reset
+ operation */
+#define SONY_READY_RETRIES 20000 /* How many times to retry a
+ spin waiting for a register
+ to come ready */
+
+#define MAX_CDU31A_RETRIES 3 /* How many times to retry an
+ operation */
+
+/* Commands to request or set drive control parameters and disc information */
+#define SONY_REQ_DRIVE_CONFIG_CMD 0x00 /* Returns s_sony_drive_config */
+#define SONY_REQ_DRIVE_MODE_CMD 0x01
+#define SONY_REQ_DRIVE_PARAM_CMD 0x02
+#define SONY_REQ_MECH_STATUS_CMD 0x03
+#define SONY_REQ_AUDIO_STATUS_CMD 0x04
+#define SONY_SET_DRIVE_PARAM_CMD 0x10
+#define SONY_REQ_TOC_DATA_CMD 0x20 /* Returns s_sony_toc */
+#define SONY_REQ_SUBCODE_ADDRESS_CMD 0x21 /* Returns s_sony_subcode */
+#define SONY_REQ_UPC_EAN_CMD 0x22
+#define SONY_REQ_ISRC_CMD 0x23
+#define SONY_REQ_TOC_DATA_SPEC_CMD 0x24 /* Returns s_sony_session_toc */
+
+/* Commands to request information from the drive */
+#define SONY_READ_TOC_CMD 0x30 /* let the drive firmware grab the TOC */
+#define SONY_SEEK_CMD 0x31
+#define SONY_READ_CMD 0x32
+#define SONY_READ_BLKERR_STAT_CMD 0x34
+#define SONY_ABORT_CMD 0x35
+#define SONY_READ_TOC_SPEC_CMD 0x36
+
+/* Commands to control audio */
+#define SONY_AUDIO_PLAYBACK_CMD 0x40
+#define SONY_AUDIO_STOP_CMD 0x41
+#define SONY_AUDIO_SCAN_CMD 0x42
+
+/* Miscellaneous control commands */
+#define SONY_EJECT_CMD 0x50
+#define SONY_SPIN_UP_CMD 0x51
+#define SONY_SPIN_DOWN_CMD 0x52
+
+/* Diagnostic commands */
+#define SONY_WRITE_BUFFER_CMD 0x60
+#define SONY_READ_BUFFER_CMD 0x61
+#define SONY_DIAGNOSTICS_CMD 0x62
+
+
+/*
+ * The following are command parameters for the set drive parameter command
+ */
+#define SONY_SD_DECODE_PARAM 0x00
+#define SONY_SD_INTERFACE_PARAM 0x01
+#define SONY_SD_BUFFERING_PARAM 0x02
+#define SONY_SD_AUDIO_PARAM 0x03
+#define SONY_SD_AUDIO_VOLUME 0x04
+#define SONY_SD_MECH_CONTROL 0x05
+#define SONY_SD_AUTO_SPIN_DOWN_TIME 0x06
+
+/*
+ * The following are parameter bits for the mechanical control command
+ */
+#define SONY_AUTO_SPIN_UP_BIT 0x01
+#define SONY_AUTO_EJECT_BIT 0x02
+#define SONY_DOUBLE_SPEED_BIT 0x04
+
+/*
+ * The following extract information from the drive configuration about
+ * the drive itself.
+ */
+#define SONY_HWC_GET_LOAD_MECH(c) (c.hw_config[0] & 0x03)
+#define SONY_HWC_EJECT(c) (c.hw_config[0] & 0x04)
+#define SONY_HWC_LED_SUPPORT(c) (c.hw_config[0] & 0x08)
+#define SONY_HWC_DOUBLE_SPEED(c) (c.hw_config[0] & 0x10)
+#define SONY_HWC_GET_BUF_MEM_SIZE(c) ((c.hw_config[0] & 0xc0) >> 6)
+#define SONY_HWC_AUDIO_PLAYBACK(c) (c.hw_config[1] & 0x01)
+#define SONY_HWC_ELECTRIC_VOLUME(c) (c.hw_config[1] & 0x02)
+#define SONY_HWC_ELECTRIC_VOLUME_CTL(c) (c.hw_config[1] & 0x04)
+
+#define SONY_HWC_CADDY_LOAD_MECH 0x00
+#define SONY_HWC_TRAY_LOAD_MECH 0x01
+#define SONY_HWC_POPUP_LOAD_MECH 0x02
+#define SONY_HWC_UNKWN_LOAD_MECH 0x03
+
+#define SONY_HWC_8KB_BUFFER 0x00
+#define SONY_HWC_32KB_BUFFER 0x01
+#define SONY_HWC_64KB_BUFFER 0x02
+#define SONY_HWC_UNKWN_BUFFER 0x03
+
+/*
+ * This is the complete status returned from the drive configuration request
+ * command.
+ */
+struct s_sony_drive_config
+{
+ unsigned char exec_status[2];
+ char vendor_id[8];
+ char product_id[16];
+ char product_rev_level[8];
+ unsigned char hw_config[2];
+};
+
+/* The following is returned from the request subcode address command */
+struct s_sony_subcode
+{
+ unsigned char exec_status[2];
+ unsigned char address :4;
+ unsigned char control :4;
+ unsigned char track_num;
+ unsigned char index_num;
+ unsigned char rel_msf[3];
+ unsigned char reserved1;
+ unsigned char abs_msf[3];
+};
+
+#define MAX_TRACKS 100 /* The maximum tracks a disk may have. */
+/*
+ * The following is returned from the request TOC (Table Of Contents) command.
+ * (last_track_num-first_track_num+1) values are valid in tracks.
+ */
+struct s_sony_toc
+{
+ unsigned char exec_status[2];
+ unsigned char address0 :4;
+ unsigned char control0 :4;
+ unsigned char point0;
+ unsigned char first_track_num;
+ unsigned char disk_type;
+ unsigned char dummy0;
+ unsigned char address1 :4;
+ unsigned char control1 :4;
+ unsigned char point1;
+ unsigned char last_track_num;
+ unsigned char dummy1;
+ unsigned char dummy2;
+ unsigned char address2 :4;
+ unsigned char control2 :4;
+ unsigned char point2;
+ unsigned char lead_out_start_msf[3];
+ struct
+ {
+ unsigned char address :4;
+ unsigned char control :4;
+ unsigned char track;
+ unsigned char track_start_msf[3];
+ } tracks[MAX_TRACKS];
+
+ unsigned int lead_out_start_lba;
+};
+
+struct s_sony_session_toc
+{
+ unsigned char exec_status[2];
+ unsigned char session_number;
+ unsigned char address0 :4;
+ unsigned char control0 :4;
+ unsigned char point0;
+ unsigned char first_track_num;
+ unsigned char disk_type;
+ unsigned char dummy0;
+ unsigned char address1 :4;
+ unsigned char control1 :4;
+ unsigned char point1;
+ unsigned char last_track_num;
+ unsigned char dummy1;
+ unsigned char dummy2;
+ unsigned char address2 :4;
+ unsigned char control2 :4;
+ unsigned char point2;
+ unsigned char lead_out_start_msf[3];
+ unsigned char addressb0 :4;
+ unsigned char controlb0 :4;
+ unsigned char pointb0;
+ unsigned char next_poss_prog_area_msf[3];
+ unsigned char num_mode_5_pointers;
+ unsigned char max_start_outer_leadout_msf[3];
+ unsigned char addressb1 :4;
+ unsigned char controlb1 :4;
+ unsigned char pointb1;
+ unsigned char dummyb0_1[4];
+ unsigned char num_skip_interval_pointers;
+ unsigned char num_skip_track_assignments;
+ unsigned char dummyb0_2;
+ unsigned char addressb2 :4;
+ unsigned char controlb2 :4;
+ unsigned char pointb2;
+ unsigned char tracksb2[7];
+ unsigned char addressb3 :4;
+ unsigned char controlb3 :4;
+ unsigned char pointb3;
+ unsigned char tracksb3[7];
+ unsigned char addressb4 :4;
+ unsigned char controlb4 :4;
+ unsigned char pointb4;
+ unsigned char tracksb4[7];
+ unsigned char addressc0 :4;
+ unsigned char controlc0 :4;
+ unsigned char pointc0;
+ unsigned char dummyc0[7];
+ struct
+ {
+ unsigned char address :4;
+ unsigned char control :4;
+ unsigned char track;
+ unsigned char track_start_msf[3];
+ } tracks[MAX_TRACKS];
+
+ unsigned int start_track_lba;
+ unsigned int lead_out_start_lba;
+ unsigned int mint;
+ unsigned int maxt;
+};
+
+struct s_all_sessions_toc
+{
+ unsigned char sessions;
+ unsigned int track_entries;
+ unsigned char first_track_num;
+ unsigned char last_track_num;
+ unsigned char disk_type;
+ unsigned char lead_out_start_msf[3];
+ struct
+ {
+ unsigned char address :4;
+ unsigned char control :4;
+ unsigned char track;
+ unsigned char track_start_msf[3];
+ } tracks[MAX_TRACKS];
+
+ unsigned int start_track_lba;
+ unsigned int lead_out_start_lba;
+};
+
+
+/*
+ * The following are errors returned from the drive.
+ */
+
+/* Command error group */
+#define SONY_ILL_CMD_ERR 0x10
+#define SONY_ILL_PARAM_ERR 0x11
+
+/* Mechanism group */
+#define SONY_NOT_LOAD_ERR 0x20
+#define SONY_NO_DISK_ERR 0x21
+#define SONY_NOT_SPIN_ERR 0x22
+#define SONY_SPIN_ERR 0x23
+#define SONY_SPINDLE_SERVO_ERR 0x25
+#define SONY_FOCUS_SERVO_ERR 0x26
+#define SONY_EJECT_MECH_ERR 0x29
+#define SONY_AUDIO_PLAYING_ERR 0x2a
+#define SONY_EMERGENCY_EJECT_ERR 0x2c
+
+/* Seek error group */
+#define SONY_FOCUS_ERR 0x30
+#define SONY_FRAME_SYNC_ERR 0x31
+#define SONY_SUBCODE_ADDR_ERR 0x32
+#define SONY_BLOCK_SYNC_ERR 0x33
+#define SONY_HEADER_ADDR_ERR 0x34
+
+/* Read error group */
+#define SONY_ILL_TRACK_R_ERR 0x40
+#define SONY_MODE_0_R_ERR 0x41
+#define SONY_ILL_MODE_R_ERR 0x42
+#define SONY_ILL_BLOCK_SIZE_R_ERR 0x43
+#define SONY_MODE_R_ERR 0x44
+#define SONY_FORM_R_ERR 0x45
+#define SONY_LEAD_OUT_R_ERR 0x46
+#define SONY_BUFFER_OVERRUN_R_ERR 0x47
+
+/* Data error group */
+#define SONY_UNREC_CIRC_ERR 0x53
+#define SONY_UNREC_LECC_ERR 0x57
+
+/* Subcode error group */
+#define SONY_NO_TOC_ERR 0x60
+#define SONY_SUBCODE_DATA_NVAL_ERR 0x61
+#define SONY_FOCUS_ON_TOC_READ_ERR 0x63
+#define SONY_FRAME_SYNC_ON_TOC_READ_ERR 0x64
+#define SONY_TOC_DATA_ERR 0x65
+
+/* Hardware failure group */
+#define SONY_HW_FAILURE_ERR 0x70
+#define SONY_LEAD_IN_A_ERR 0x91
+#define SONY_LEAD_OUT_A_ERR 0x92
+#define SONY_DATA_TRACK_A_ERR 0x93
+
+/*
+ * The following are returned from the Read With Block Error Status command.
+ * They are not errors but information (Errors from the 0x5x group above may
+ * also be returned
+ */
+#define SONY_NO_CIRC_ERR_BLK_STAT 0x50
+#define SONY_NO_LECC_ERR_BLK_STAT 0x54
+#define SONY_RECOV_LECC_ERR_BLK_STAT 0x55
+#define SONY_NO_ERR_DETECTION_STAT 0x59
+
+/*
+ * The following is not an error returned by the drive, but by the code
+ * that talks to the drive. It is returned because of a timeout.
+ */
+#define SONY_TIMEOUT_OP_ERR 0x01
+#define SONY_SIGNAL_OP_ERR 0x02
+#define SONY_BAD_DATA_ERR 0x03
+
+
+/*
+ * The following are attention code for asynchronous events from the drive.
+ */
+
+/* Standard attention group */
+#define SONY_EMER_EJECT_ATTN 0x2c
+#define SONY_HW_FAILURE_ATTN 0x70
+#define SONY_MECH_LOADED_ATTN 0x80
+#define SONY_EJECT_PUSHED_ATTN 0x81
+
+/* Audio attention group */
+#define SONY_AUDIO_PLAY_DONE_ATTN 0x90
+#define SONY_LEAD_IN_ERR_ATTN 0x91
+#define SONY_LEAD_OUT_ERR_ATTN 0x92
+#define SONY_DATA_TRACK_ERR_ATTN 0x93
+#define SONY_AUDIO_PLAYBACK_ERR_ATTN 0x94
+
+/* Auto spin up group */
+#define SONY_SPIN_UP_COMPLETE_ATTN 0x24
+#define SONY_SPINDLE_SERVO_ERR_ATTN 0x25
+#define SONY_FOCUS_SERVO_ERR_ATTN 0x26
+#define SONY_TOC_READ_DONE_ATTN 0x62
+#define SONY_FOCUS_ON_TOC_READ_ERR_ATTN 0x63
+#define SONY_SYNC_ON_TOC_READ_ERR_ATTN 0x65
+
+/* Auto eject group */
+#define SONY_SPIN_DOWN_COMPLETE_ATTN 0x27
+#define SONY_EJECT_COMPLETE_ATTN 0x28
+#define SONY_EJECT_MECH_ERR_ATTN 0x29
diff --git a/drivers/cdrom/cm206.h b/drivers/cdrom/cm206.h
new file mode 100644
index 000000000..e5ba8ff20
--- /dev/null
+++ b/drivers/cdrom/cm206.h
@@ -0,0 +1,171 @@
+/* cm206.h Header file for cm206.c.
+ Copyright (c) 1995 David van Leeuwen
+*/
+
+#ifndef LINUX_CM206_H
+#define LINUX_CM206_H
+
+#include <linux/ioctl.h>
+
+/* First, the cm260 stuff */
+/* The ports and irq used. Although CM206_BASE and CM206_IRQ are defined
+ below, the values are not used unless autoprobing is turned off and
+ no LILO boot options or module command line options are given. Change
+ these values to your own as last resort if autoprobing and options
+ don't work. */
+
+#define CM206_BASE 0x340
+#define CM206_IRQ 11
+
+#define r_data_status (cm206_base)
+#define r_uart_receive (cm206_base+0x2)
+#define r_fifo_output_buffer (cm206_base+0x4)
+#define r_line_status (cm206_base+0x6)
+#define r_data_control (cm206_base+0x8)
+#define r_uart_transmit (cm206_base+0xa)
+#define r_test_clock (cm206_base+0xc)
+#define r_test_control (cm206_base+0xe)
+
+/* the data_status flags */
+#define ds_ram_size 0x4000
+#define ds_toc_ready 0x2000
+#define ds_fifo_empty 0x1000
+#define ds_sync_error 0x800
+#define ds_crc_error 0x400
+#define ds_data_error 0x200
+#define ds_fifo_overflow 0x100
+#define ds_data_ready 0x80
+
+/* the line_status flags */
+#define ls_attention 0x10
+#define ls_parity_error 0x8
+#define ls_overrun 0x4
+#define ls_receive_buffer_full 0x2
+#define ls_transmitter_buffer_empty 0x1
+
+/* the data control register flags */
+#define dc_read_q_channel 0x4000
+#define dc_mask_sync_error 0x2000
+#define dc_toc_enable 0x1000
+#define dc_no_stop_on_error 0x800
+#define dc_break 0x400
+#define dc_initialize 0x200
+#define dc_mask_transmit_ready 0x100
+#define dc_flag_enable 0x80
+
+/* Define the default data control register flags here */
+#define dc_normal (dc_mask_sync_error | dc_no_stop_on_error | \
+ dc_mask_transmit_ready)
+
+/* now some constants related to the cm206 */
+/* another drive status byte, echoed by the cm206 on most commands */
+
+#define dsb_error_condition 0x1
+#define dsb_play_in_progress 0x4
+#define dsb_possible_media_change 0x8
+#define dsb_disc_present 0x10
+#define dsb_drive_not_ready 0x20
+#define dsb_tray_locked 0x40
+#define dsb_tray_not_closed 0x80
+
+#define dsb_not_useful (dsb_drive_not_ready | dsb_tray_not_closed)
+
+/* the cm206 command set */
+
+#define c_close_tray 0
+#define c_lock_tray 0x01
+#define c_unlock_tray 0x04
+#define c_open_tray 0x05
+#define c_seek 0x10
+#define c_read_data 0x20
+#define c_force_1x 0x21
+#define c_force_2x 0x22
+#define c_auto_mode 0x23
+#define c_play 0x30
+#define c_set_audio_mode 0x31
+#define c_read_current_q 0x41
+#define c_stream_q 0x42
+#define c_drive_status 0x50
+#define c_disc_status 0x51
+#define c_audio_status 0x52
+#define c_drive_configuration 0x53
+#define c_read_upc 0x60
+#define c_stop 0x70
+#define c_calc_checksum 0xe5
+
+#define c_gimme 0xf8
+
+/* finally, the (error) condition that the drive can be in *
+ * OK, this is not always an error, but let's prefix it with e_ */
+
+#define e_none 0
+#define e_illegal_command 0x01
+#define e_sync 0x02
+#define e_seek 0x03
+#define e_parity 0x04
+#define e_focus 0x05
+#define e_header_sync 0x06
+#define e_code_incompatibility 0x07
+#define e_reset_done 0x08
+#define e_bad_parameter 0x09
+#define e_radial 0x0a
+#define e_sub_code 0x0b
+#define e_no_data_track 0x0c
+#define e_scan 0x0d
+#define e_tray_open 0x0f
+#define e_no_disc 0x10
+#define e_tray stalled 0x11
+
+/* drive configuration masks */
+
+#define dcf_revision_code 0x7
+#define dcf_transfer_rate 0x60
+#define dcf_motorized_tray 0x80
+
+/* disc status byte */
+
+#define cds_multi_session 0x2
+#define cds_all_audio 0x8
+#define cds_xa_mode 0xf0
+
+/* finally some ioctls for the driver */
+
+#define CM206CTL_GET_STAT _IO( 0x20, 0 )
+#define CM206CTL_GET_LAST_STAT _IO( 0x20, 1 )
+
+#ifdef STATISTICS
+
+/* This is an ugly way to guarantee that the names of the statistics
+ * are the same in the code and in the diagnostics program. */
+
+#ifdef __KERNEL__
+#define x(a) st_ ## a
+#define y enum
+#else
+#define x(a) #a
+#define y char * stats_name[] =
+#endif
+
+y {x(interrupt), x(data_ready), x(fifo_overflow), x(data_error),
+ x(crc_error), x(sync_error), x(lost_intr), x(echo),
+ x(write_timeout), x(receive_timeout), x(read_timeout),
+ x(dsb_timeout), x(stop_0xff), x(back_read_timeout),
+ x(sector_transferred), x(read_restarted), x(read_background),
+ x(bh), x(open), x(ioctl_multisession), x(attention)
+#ifdef __KERNEL__
+ , x(last_entry)
+#endif
+ };
+
+#ifdef __KERNEL__
+#define NR_STATS st_last_entry
+#else
+#define NR_STATS (sizeof(stats_name)/sizeof(char*))
+#endif
+
+#undef y
+#undef x
+
+#endif STATISTICS
+
+#endif LINUX_CM206_H
diff --git a/drivers/cdrom/gscd.h b/drivers/cdrom/gscd.h
new file mode 100644
index 000000000..fccc742a5
--- /dev/null
+++ b/drivers/cdrom/gscd.h
@@ -0,0 +1,110 @@
+/*
+ * Definitions for a GoldStar R420 CD-ROM interface
+ *
+ * Copyright (C) 1995 Oliver Raupach <raupach@nwfs1.rz.fh-hannover.de>
+ * Eberhard Moenkeberg <emoenke@gwdg.de>
+ *
+ * Published under the GPL.
+ *
+ */
+
+
+/* The Interface Card default address is 0x340. This will work for most
+ applications. Address selection is accomplished by jumpers PN801-1 to
+ PN801-4 on the GoldStar Interface Card.
+ Appropriate settings are: 0x300, 0x310, 0x320, 0x330, 0x340, 0x350, 0x360
+ 0x370, 0x380, 0x390, 0x3A0, 0x3B0, 0x3C0, 0x3D0, 0x3E0, 0x3F0 */
+
+/* insert here the I/O port address */
+#define GSCD_BASE_ADDR 0x340
+
+/* change this to set the dma-channel */
+#define GSCD_DMA_CHANNEL 3 /* not used */
+
+/************** nothing to set up below here *********************/
+
+/* port access macro */
+#define GSCDPORT(x) (gscd_port + (x))
+
+/*
+ * commands
+ * the lower nibble holds the command length
+ */
+#define CMD_STATUS 0x01
+#define CMD_READSUBQ 0x02 /* 1: ?, 2: UPC, 5: ? */
+#define CMD_SEEK 0x05 /* read_mode M-S-F */
+#define CMD_READ 0x07 /* read_mode M-S-F nsec_h nsec_l */
+#define CMD_RESET 0x11
+#define CMD_SETMODE 0x15
+#define CMD_PLAY 0x17 /* M-S-F M-S-F */
+#define CMD_LOCK_CTL 0x22 /* 0: unlock, 1: lock */
+#define CMD_IDENT 0x31
+#define CMD_SETSPEED 0x32 /* 0: auto */ /* ??? */
+#define CMD_GETMODE 0x41
+#define CMD_PAUSE 0x51
+#define CMD_READTOC 0x61
+#define CMD_DISKINFO 0x71
+#define CMD_TRAY_CTL 0x81
+
+/*
+ * disk_state:
+ */
+#define ST_PLAYING 0x80
+#define ST_UNLOCKED 0x40
+#define ST_NO_DISK 0x20
+#define ST_DOOR_OPEN 0x10
+#define ST_x08 0x08
+#define ST_x04 0x04
+#define ST_INVALID 0x02
+#define ST_x01 0x01
+
+/*
+ * cmd_type:
+ */
+#define TYPE_INFO 0x01
+#define TYPE_DATA 0x02
+
+/*
+ * read_mode:
+ */
+#define MOD_POLLED 0x80
+#define MOD_x08 0x08
+#define MOD_RAW 0x04
+
+#define READ_DATA(port, buf, nr) insb(port, buf, nr)
+
+#define SET_TIMER(func, jifs) \
+ ((timer_table[GSCD_TIMER].expires = jiffies + jifs), \
+ (timer_table[GSCD_TIMER].fn = func), \
+ (timer_active |= 1<<GSCD_TIMER))
+
+#define CLEAR_TIMER timer_active &= ~(1<<GSCD_TIMER)
+
+#define MAX_TRACKS 104
+
+struct msf {
+ unsigned char min;
+ unsigned char sec;
+ unsigned char frame;
+};
+
+struct gscd_Play_msf {
+ struct msf start;
+ struct msf end;
+};
+
+struct gscd_DiskInfo {
+ unsigned char first;
+ unsigned char last;
+ struct msf diskLength;
+ struct msf firstTrack;
+};
+
+struct gscd_Toc {
+ unsigned char ctrl_addr;
+ unsigned char track;
+ unsigned char pointIndex;
+ struct msf trackTime;
+ struct msf diskTime;
+};
+
diff --git a/drivers/cdrom/isp16.h b/drivers/cdrom/isp16.h
new file mode 100644
index 000000000..9945bb34c
--- /dev/null
+++ b/drivers/cdrom/isp16.h
@@ -0,0 +1,75 @@
+/* -- isp16.h
+ *
+ * Header for detection and initialisation of cdrom interface (only) on
+ * ISP16 (MAD16, Mozart) sound card.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* These are the default values */
+#define ISP16_CDROM_TYPE "Sanyo"
+#define ISP16_CDROM_IO_BASE 0x340
+#define ISP16_CDROM_IRQ 0
+#define ISP16_CDROM_DMA 0
+
+/* Some (Media)Magic */
+/* define types of drive the interface on an ISP16 card may be looking at */
+#define ISP16_DRIVE_X 0x00
+#define ISP16_SONY 0x02
+#define ISP16_PANASONIC0 0x02
+#define ISP16_SANYO0 0x02
+#define ISP16_MITSUMI 0x04
+#define ISP16_PANASONIC1 0x06
+#define ISP16_SANYO1 0x06
+#define ISP16_DRIVE_NOT_USED 0x08 /* not used */
+#define ISP16_DRIVE_SET_MASK 0xF1 /* don't change 0-bit or 4-7-bits*/
+/* ...for port */
+#define ISP16_DRIVE_SET_PORT 0xF8D
+/* set io parameters */
+#define ISP16_BASE_340 0x00
+#define ISP16_BASE_330 0x40
+#define ISP16_BASE_360 0x80
+#define ISP16_BASE_320 0xC0
+#define ISP16_IRQ_X 0x00
+#define ISP16_IRQ_5 0x04 /* shouldn't be used to avoid sound card conflicts */
+#define ISP16_IRQ_7 0x08 /* shouldn't be used to avoid sound card conflicts */
+#define ISP16_IRQ_3 0x0C
+#define ISP16_IRQ_9 0x10
+#define ISP16_IRQ_10 0x14
+#define ISP16_IRQ_11 0x18
+#define ISP16_DMA_X 0x03
+#define ISP16_DMA_3 0x00
+#define ISP16_DMA_5 0x00
+#define ISP16_DMA_6 0x01
+#define ISP16_DMA_7 0x02
+#define ISP16_IO_SET_MASK 0x20 /* don't change 5-bit */
+/* ...for port */
+#define ISP16_IO_SET_PORT 0xF8E
+/* enable the card */
+#define ISP16_C928__ENABLE_PORT 0xF90 /* ISP16 with OPTi 82C928 chip */
+#define ISP16_C929__ENABLE_PORT 0xF91 /* ISP16 with OPTi 82C929 chip */
+#define ISP16_ENABLE_CDROM 0x80 /* seven bit */
+
+/* the magic stuff */
+#define ISP16_CTRL_PORT 0xF8F
+#define ISP16_C928__CTRL 0xE2 /* ISP16 with OPTi 82C928 chip */
+#define ISP16_C929__CTRL 0xE3 /* ISP16 with OPTi 82C929 chip */
+
+#define ISP16_IO_BASE 0xF8D
+#define ISP16_IO_SIZE 5 /* ports used from 0xF8D up to 0xF91 */
+
+void isp16_setup(char *str, int *ints);
+int isp16_init(void);
diff --git a/drivers/cdrom/mcd.h b/drivers/cdrom/mcd.h
new file mode 100644
index 000000000..9997f4390
--- /dev/null
+++ b/drivers/cdrom/mcd.h
@@ -0,0 +1,128 @@
+/*
+ * Definitions for a Mitsumi CD-ROM interface
+ *
+ * Copyright (C) 1992 Martin Harriss
+ *
+ * martin@bdsi.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* *** change this to set the I/O port address */
+#define MCD_BASE_ADDR 0x300
+
+/* *** change this to set the interrupt number */
+#define MCD_INTR_NR 11
+
+/* *** make the following line uncommented, if you're sure,
+ * *** all configuration is done */
+
+/* #define I_WAS_HERE */
+
+
+
+
+/* Increase this if you get lots of timeouts */
+#define MCD_STATUS_DELAY 1000
+
+/* number of times to retry a command before giving up */
+#define MCD_RETRY_ATTEMPTS 10
+
+/* port access macro */
+#define MCDPORT(x) (mcd_port + (x))
+
+/* How many sectors to read at 1x when an error at 2x speed occurs. */
+/* You can change this to anything from 2 to 32767, but 30 seems to */
+/* work best for me. I have found that when the drive has problems */
+/* reading one sector, it will have troubles reading the next few. */
+#define SINGLE_HOLD_SECTORS 30
+
+#define MCMD_2X_READ 0xC1 /* Double Speed Read DON'T TOUCH! */
+
+/* status bits */
+
+#define MST_CMD_CHECK 0x01 /* command error */
+#define MST_BUSY 0x02 /* now playing */
+#define MST_READ_ERR 0x04 /* read error */
+#define MST_DSK_TYPE 0x08
+#define MST_SERVO_CHECK 0x10
+#define MST_DSK_CHG 0x20 /* disk removed or changed */
+#define MST_READY 0x40 /* disk in the drive */
+#define MST_DOOR_OPEN 0x80 /* door is open */
+
+/* flag bits */
+
+#define MFL_DATA 0x02 /* data available */
+#define MFL_STATUS 0x04 /* status available */
+
+/* commands */
+
+#define MCMD_GET_DISK_INFO 0x10 /* read info from disk */
+#define MCMD_GET_Q_CHANNEL 0x20 /* read info from q channel */
+#define MCMD_GET_STATUS 0x40
+#define MCMD_SET_MODE 0x50
+#define MCMD_SOFT_RESET 0x60
+#define MCMD_STOP 0x70 /* stop play */
+#define MCMD_CONFIG_DRIVE 0x90
+#define MCMD_SET_VOLUME 0xAE /* set audio level */
+#define MCMD_PLAY_READ 0xC0 /* play or read data */
+#define MCMD_GET_VERSION 0xDC
+#define MCMD_EJECT 0xF6 /* eject (FX drive) */
+
+/* borrowed from hd.c */
+
+#define READ_DATA(port, buf, nr) \
+insb(port, buf, nr)
+
+#define SET_TIMER(func, jifs) \
+ ((timer_table[MCD_TIMER].expires = jiffies + jifs), \
+ (timer_table[MCD_TIMER].fn = func), \
+ (timer_active |= 1<<MCD_TIMER))
+
+#define CLEAR_TIMER timer_active &= ~(1<<MCD_TIMER)
+
+#define MAX_TRACKS 104
+
+struct msf {
+ unsigned char min;
+ unsigned char sec;
+ unsigned char frame;
+};
+
+struct mcd_Play_msf {
+ struct msf start;
+ struct msf end;
+};
+
+struct mcd_DiskInfo {
+ unsigned char first;
+ unsigned char last;
+ struct msf diskLength;
+ struct msf firstTrack;
+};
+
+struct mcd_Toc {
+ unsigned char ctrl_addr;
+ unsigned char track;
+ unsigned char pointIndex;
+ struct msf trackTime;
+ struct msf diskTime;
+};
+
+#ifndef I_WAS_HERE
+#warning You have not edited mcd.h
+#warning Perhaps irq and i/o settings are wrong.
+#endif
diff --git a/drivers/cdrom/mcdx.h b/drivers/cdrom/mcdx.h
new file mode 100644
index 000000000..b1b431326
--- /dev/null
+++ b/drivers/cdrom/mcdx.h
@@ -0,0 +1,184 @@
+/*
+ * Definitions for the Mitsumi CDROM interface
+ * Copyright (C) 1995 1996 Heiko Schlittermann <heiko@lotte.sax.de>
+ * VERSION: @VERSION@
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Thanks to
+ * The Linux Community at all and ...
+ * Martin Harris (he wrote the first Mitsumi Driver)
+ * Eberhard Moenkeberg (he gave me much support and the initial kick)
+ * Bernd Huebner, Ruediger Helsch (Unifix-Software Gmbh, they
+ * improved the original driver)
+ * Jon Tombs, Bjorn Ekwall (module support)
+ * Daniel v. Mosnenck (he sent me the Technical and Programming Reference)
+ * Gerd Knorr (he lent me his PhotoCD)
+ * Nils Faerber and Roger E. Wolff (extensively tested the LU portion)
+ * Andreas Kies (testing the mysterious hang up's)
+ * ... somebody forgotten?
+ * Marcin Dalecki
+ *
+ */
+
+/*
+ * The following lines are for user configuration
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * {0|1} -- 1 if you want the driver detect your drive, may crash and
+ * needs a long time to seek. The higher the address the longer the
+ * seek.
+ *
+ * WARNING: AUTOPROBE doesn't work.
+ */
+#define MCDX_AUTOPROBE 0
+
+/*
+ * Drive specific settings according to the jumpers on the controller
+ * board(s).
+ * o MCDX_NDRIVES : number of used entries of the following table
+ * o MCDX_DRIVEMAP : table of {i/o base, irq} per controller
+ *
+ * NOTE: I didn't get a drive at irq 9(2) working. Not even alone.
+ */
+#if MCDX_AUTOPROBE == 0
+ #define MCDX_NDRIVES 1
+ #define MCDX_DRIVEMAP { \
+ {0x300, 11}, \
+ {0x304, 05}, \
+ {0x000, 00}, \
+ {0x000, 00}, \
+ {0x000, 00}, \
+ }
+#else
+ #error Autoprobing is not implemented yet.
+#endif
+
+#ifndef MCDX_QUIET
+#define MCDX_QUIET 1
+#endif
+
+#ifndef MCDX_DEBUG
+#define MCDX_DEBUG 0
+#endif
+
+/* *** make the following line uncommented, if you're sure,
+ * *** all configuration is done */
+/* #define I_WAS_HERE */
+#define I_WAS_HERE /* delete this line, it's for heiko only */
+
+/* The name of the device */
+#define MCDX "mcdx"
+
+/* Flags for DEBUGGING */
+#define INIT 0
+#define MALLOC 0
+#define IOCTL 0
+#define PLAYTRK 0
+#define SUBCHNL 0
+#define TOCHDR 0
+#define MS 0
+#define PLAYMSF 0
+#define READTOC 0
+#define OPENCLOSE 0
+#define HW 0
+#define TALK 0
+#define IRQ 0
+#define XFER 0
+#define REQUEST 0
+#define SLEEP 0
+
+/* The following addresses are taken from the Mitsumi Reference
+ * and describe the possible i/o range for the controller.
+ */
+#define MCDX_IO_BEGIN ((char*) 0x300) /* first base of i/o addr */
+#define MCDX_IO_END ((char*) 0x3fc) /* last base of i/o addr */
+
+/* Per controller 4 bytes i/o are needed. */
+#define MCDX_IO_SIZE 4
+
+/*
+ * Bits
+ */
+
+/* The status byte, returned from every command, set if
+ * the description is true */
+#define MCDX_RBIT_OPEN 0x80 /* door is open */
+#define MCDX_RBIT_DISKSET 0x40 /* disk set (recognised) */
+#define MCDX_RBIT_CHANGED 0x20 /* disk was changed */
+#define MCDX_RBIT_CHECK 0x10 /* disk rotates, servo is on */
+#define MCDX_RBIT_AUDIOTR 0x08 /* current track is audio */
+#define MCDX_RBIT_RDERR 0x04 /* read error, refer SENSE KEY */
+#define MCDX_RBIT_AUDIOBS 0x02 /* currently playing audio */
+#define MCDX_RBIT_CMDERR 0x01 /* command, param or format error */
+
+/* The I/O Register holding the h/w status of the drive,
+ * can be read at i/o base + 1 */
+#define MCDX_RBIT_DOOR 0x10 /* door is open */
+#define MCDX_RBIT_STEN 0x04 /* if 0, i/o base contains drive status */
+#define MCDX_RBIT_DTEN 0x02 /* if 0, i/o base contains data */
+
+/*
+ * The commands.
+ */
+
+#define OPCODE 1 /* offset of opcode */
+#define MCDX_CMD_REQUEST_TOC 1, 0x10
+#define MCDX_CMD_REQUEST_STATUS 1, 0x40
+#define MCDX_CMD_RESET 1, 0x60
+#define MCDX_CMD_REQUEST_DRIVE_MODE 1, 0xc2
+#define MCDX_CMD_SET_INTERLEAVE 2, 0xc8, 0
+#define MCDX_CMD_DATAMODE_SET 2, 0xa0, 0
+ #define MCDX_DATAMODE1 0x01
+ #define MCDX_DATAMODE2 0x02
+#define MCDX_CMD_LOCK_DOOR 2, 0xfe, 0
+
+#define READ_AHEAD 4 /* 8 Sectors (4K) */
+
+/* Useful macros */
+#define e_door(x) ((x) & MCDX_RBIT_OPEN)
+#define e_check(x) (~(x) & MCDX_RBIT_CHECK)
+#define e_notset(x) (~(x) & MCDX_RBIT_DISKSET)
+#define e_changed(x) ((x) & MCDX_RBIT_CHANGED)
+#define e_audio(x) ((x) & MCDX_RBIT_AUDIOTR)
+#define e_audiobusy(x) ((x) & MCDX_RBIT_AUDIOBS)
+#define e_cmderr(x) ((x) & MCDX_RBIT_CMDERR)
+#define e_readerr(x) ((x) & MCDX_RBIT_RDERR)
+
+/** no drive specific */
+#define MCDX_CDBLK 2048 /* 2048 cooked data each blk */
+
+#define MCDX_DATA_TIMEOUT (HZ/10) /* 0.1 second */
+
+/*
+ * Access to the msf array
+ */
+#define MSF_MIN 0 /* minute */
+#define MSF_SEC 1 /* second */
+#define MSF_FRM 2 /* frame */
+
+/*
+ * Errors
+ */
+#define MCDX_E 1 /* unspec error */
+#define MCDX_ST_EOM 0x0100 /* end of media */
+#define MCDX_ST_DRV 0x00ff /* mask to query the drive status */
+
+#ifndef I_WAS_HERE
+#warning You have not edited mcdx.h
+#warning Perhaps irq and i/o settings are wrong.
+#endif
+
+/* ex:set ts=4 sw=4: */
diff --git a/drivers/cdrom/optcd.h b/drivers/cdrom/optcd.h
new file mode 100644
index 000000000..00dd3aed8
--- /dev/null
+++ b/drivers/cdrom/optcd.h
@@ -0,0 +1,52 @@
+/* linux/include/linux/optcd.h - Optics Storage 8000 AT CDROM driver
+ $Id: optcd.h,v 1.2 1996/01/15 18:43:44 root Exp root $
+
+ Copyright (C) 1995 Leo Spiekman (spiekman@dutette.et.tudelft.nl)
+
+
+ Configuration file for linux/drivers/cdrom/optcd.c
+*/
+
+#ifndef _LINUX_OPTCD_H
+#define _LINUX_OPTCD_H
+
+
+/* I/O base of drive. Drive uses base to base+2.
+ This setting can be overridden with the kernel or insmod command
+ line option 'optcd=<portbase>'. Use address of 0 to disable driver. */
+#define OPTCD_PORTBASE 0x340
+
+
+/* enable / disable parts of driver by define / undef */
+#define MULTISESSION /* multisession support (ALPHA) */
+
+
+/* Change 0 to 1 to debug various parts of the driver */
+#define DEBUG_DRIVE_IF 0 /* Low level drive interface */
+#define DEBUG_CONV 0 /* Address conversions */
+#define DEBUG_BUFFERS 0 /* Buffering and block size conversion */
+#define DEBUG_REQUEST 0 /* Request mechanism */
+#define DEBUG_STATE 0 /* State machine */
+#define DEBUG_TOC 0 /* Q-channel and Table of Contents */
+#define DEBUG_MULTIS 0 /* Multisession code */
+#define DEBUG_VFS 0 /* VFS interface */
+
+
+/* Don't touch these unless you know what you're doing. */
+
+/* Various timeout loop repetition counts. */
+#define BUSY_TIMEOUT 10000000 /* for busy wait */
+#define FAST_TIMEOUT 100000 /* ibid. for probing */
+#define SLEEP_TIMEOUT 6000 /* for timer wait */
+#define MULTI_SEEK_TIMEOUT 1000 /* for timer wait */
+#define READ_TIMEOUT 6000 /* for poll wait */
+#define STOP_TIMEOUT 2000 /* for poll wait */
+#define RESET_WAIT 5000 /* busy wait at drive reset */
+
+/* # of buffers for block size conversion. 6 is optimal for my setup (P75),
+ giving 280 kb/s, with 0.4% CPU usage. Experiment to find your optimal
+ setting */
+#define N_BUFS 6
+
+
+#endif _LINUX_OPTCD_H
diff --git a/drivers/cdrom/sbpcd.h b/drivers/cdrom/sbpcd.h
new file mode 100644
index 000000000..e9021316d
--- /dev/null
+++ b/drivers/cdrom/sbpcd.h
@@ -0,0 +1,858 @@
+/*
+ * sbpcd.h Specify interface address and interface type here.
+ */
+
+/*
+ * Attention! This file contains user-serviceable parts!
+ * I recommend to make use of it...
+ * If you feel helpless, look into linux/Documentation/cdrom/sbpcd
+ * (good idea anyway, at least before mailing me).
+ *
+ * The definitions for the first controller can get overridden by
+ * the kernel command line ("lilo boot option").
+ * Examples:
+ * sbpcd=0x300,LaserMate
+ * or
+ * sbpcd=0x230,SoundBlaster
+ * or
+ * sbpcd=0x338,SoundScape
+ * or
+ * sbpcd=0x2C0,Teac16bit
+ *
+ * If sbpcd gets used as a module, you can load it with
+ * insmod sbpcd.o sbpcd=0x300,0
+ * or
+ * insmod sbpcd.o sbpcd=0x230,1
+ * or
+ * insmod sbpcd.o sbpcd=0x338,2
+ * or
+ * insmod sbpcd.o sbpcd=0x2C0,3
+ * respective to override the configured address and type.
+ */
+
+/*
+ * define your CDROM port base address as CDROM_PORT
+ * and specify the type of your interface card as SBPRO.
+ *
+ * address:
+ * ========
+ * SBPRO type addresses typically are 0x0230 (=0x220+0x10), 0x0250, ...
+ * LASERMATE type (CI-101P, WDH-7001C) addresses typically are 0x0300, ...
+ * SOUNDSCAPE addresses are from the LASERMATE type and range. You have to
+ * specify the REAL address here, not the configuration port address. Look
+ * at the CDROM driver's invoking line within your DOS CONFIG.SYS, or let
+ * sbpcd auto-probe, if you are not firm with the address.
+ * There are some soundcards on the market with 0x0630, 0x0650, ...; their
+ * type is not obvious (both types are possible).
+ *
+ * example: if your SBPRO audio address is 0x220, specify 0x230 and SBPRO 1.
+ * if your soundcard has its CDROM port above 0x300, specify
+ * that address and try SBPRO 0 first.
+ * if your SoundScape configuration port is at 0x330, specify
+ * 0x338 and SBPRO 2.
+ *
+ * interface type:
+ * ===============
+ * set SBPRO to 1 for "true" SoundBlaster card
+ * set SBPRO to 0 for "compatible" soundcards and
+ * for "poor" (no sound) interface cards.
+ * set SBPRO to 2 for Ensonic SoundScape or SPEA Media FX cards
+ * set SBPRO to 3 for Teac 16bit interface cards
+ *
+ * Almost all "compatible" sound boards need to set SBPRO to 0.
+ * If SBPRO is set wrong, the drives will get found - but any
+ * data access will give errors (audio access will work).
+ * The "OmniCD" no-sound interface card from CreativeLabs and most Teac
+ * interface cards need SBPRO 1.
+ *
+ * sound base:
+ * ===========
+ * The SOUND_BASE definition tells if we should try to turn the CD sound
+ * channels on. It will only be of use regarding soundcards with a SbPro
+ * compatible mixer.
+ *
+ * Example: #define SOUND_BASE 0x220 enables the sound card's CD channels
+ * #define SOUND_BASE 0 leaves the soundcard untouched
+ */
+#if !(SBPCD_ISSUE-1) /* first (or if you have only one) interface board: */
+#define CDROM_PORT 0x340 /* <-----------<< port address */
+#define SBPRO 0 /* <-----------<< interface type */
+#define MAX_DRIVES 4 /* set to 1 if the card does not use "drive select" */
+#define SOUND_BASE 0x220 /* <-----------<< sound address of this card or 0 */
+#endif
+#if !(SBPCD_ISSUE-2) /* ==================== second interface board: === */
+#define CDROM_PORT 0x344 /* <-----------<< port address */
+#define SBPRO 0 /* <-----------<< interface type */
+#define MAX_DRIVES 4 /* set to 1 if the card does not use "drive select" */
+#define SOUND_BASE 0x000 /* <-----------<< sound address of this card or 0 */
+#endif
+#if !(SBPCD_ISSUE-3) /* ===================== third interface board: === */
+#define CDROM_PORT 0x630 /* <-----------<< port address */
+#define SBPRO 1 /* <-----------<< interface type */
+#define MAX_DRIVES 4 /* set to 1 if the card does not use "drive select" */
+#define SOUND_BASE 0x240 /* <-----------<< sound address of this card or 0 */
+#endif
+#if !(SBPCD_ISSUE-4) /* ==================== fourth interface board: === */
+#define CDROM_PORT 0x634 /* <-----------<< port address */
+#define SBPRO 0 /* <-----------<< interface type */
+#define MAX_DRIVES 4 /* set to 1 if the card does not use "drive select" */
+#define SOUND_BASE 0x000 /* <-----------<< sound address of this card or 0 */
+#endif
+
+/*
+ * some more or less user dependent definitions - service them!
+ */
+
+/* Set this to 0 once you have configured your interface definitions right. */
+#define DISTRIBUTION 1
+
+/*
+ * Time to wait after giving a message.
+ * This gets important if you enable non-standard DBG_xxx flags.
+ * You will see what happens if you omit the pause or make it
+ * too short. Be warned!
+ */
+#define KLOGD_PAUSE 1
+
+/* tray control: eject tray if no disk is in */
+#if DISTRIBUTION
+#define JUKEBOX 0
+#else
+#define JUKEBOX 1
+#endif DISTRIBUTION
+
+/* tray control: eject tray after last use */
+#if DISTRIBUTION
+#define EJECT 0
+#else
+#define EJECT 1
+#endif DISTRIBUTION
+
+/* max. number of audio frames to read with one */
+/* request (allocates n* 2352 bytes kernel memory!) */
+/* may be freely adjusted, f.e. 75 (= 1 sec.), at */
+/* runtime by use of the CDROMAUDIOBUFSIZ ioctl. */
+#define READ_AUDIO 0
+
+/* Optimizations for the Teac CD-55A drive read performance.
+ * SBP_TEAC_SPEED can be changed here, or one can set the
+ * variable "teac" when loading as a module.
+ * Valid settings are:
+ * 0 - very slow - the recommended "DISTRIBUTION 1" setup.
+ * 1 - 2x performance with little overhead. No busy waiting.
+ * 2 - 4x performance with 5ms overhead per read. Busy wait.
+ *
+ * Setting SBP_TEAC_SPEED or the variable 'teac' to anything
+ * other than 0 may cause problems. If you run into them, first
+ * change SBP_TEAC_SPEED back to 0 and see if your drive responds
+ * normally. If yes, you are "allowed" to report your case - to help
+ * me with the driver, not to solve your hassle. Don´t mail if you
+ * simply are stuck into your own "tuning" experiments, you know?
+ */
+#define SBP_TEAC_SPEED 1
+
+/*==========================================================================*/
+/*==========================================================================*/
+/*
+ * nothing to change below here if you are not fully aware what you're doing
+ */
+#ifndef _LINUX_SBPCD_H
+
+#define _LINUX_SBPCD_H
+/*==========================================================================*/
+/*==========================================================================*/
+/*
+ * driver's own read_ahead, data mode
+ */
+#define SBP_BUFFER_FRAMES 8
+
+#define LONG_TIMING 0 /* test against timeouts with "gold" CDs on CR-521 */
+#undef FUTURE
+#undef SAFE_MIXED
+
+#define TEST_UPC 0
+#define SPEA_TEST 0
+#define TEST_STI 0
+#define OLD_BUSY 0
+#undef PATH_CHECK
+#ifndef SOUND_BASE
+#define SOUND_BASE 0
+#endif
+#if DISTRIBUTION
+#undef SBP_TEAC_SPEED
+#define SBP_TEAC_SPEED 0
+#endif
+/*==========================================================================*/
+/*
+ * DDI interface definitions
+ * "invented" by Fred N. van Kempen..
+ */
+#define DDIOCSDBG 0x9000
+
+/*==========================================================================*/
+/*
+ * "private" IOCTL functions
+ */
+#define CDROMAUDIOBUFSIZ 0x5382 /* set the audio buffer size */
+
+/*==========================================================================*/
+/*
+ * Debug output levels
+ */
+#define DBG_INF 1 /* necessary information */
+#define DBG_BSZ 2 /* BLOCK_SIZE trace */
+#define DBG_REA 3 /* READ status trace */
+#define DBG_CHK 4 /* MEDIA CHECK trace */
+#define DBG_TIM 5 /* datarate timer test */
+#define DBG_INI 6 /* initialization trace */
+#define DBG_TOC 7 /* tell TocEntry values */
+#define DBG_IOC 8 /* ioctl trace */
+#define DBG_STA 9 /* ResponseStatus() trace */
+#define DBG_ERR 10 /* cc_ReadError() trace */
+#define DBG_CMD 11 /* cmd_out() trace */
+#define DBG_WRN 12 /* give explanation before auto-probing */
+#define DBG_MUL 13 /* multi session code test */
+#define DBG_IDX 14 /* test code for drive_id !=0 */
+#define DBG_IOX 15 /* some special information */
+#define DBG_DID 16 /* drive ID test */
+#define DBG_RES 17 /* drive reset info */
+#define DBG_SPI 18 /* SpinUp test */
+#define DBG_IOS 19 /* ioctl trace: subchannel functions */
+#define DBG_IO2 20 /* ioctl trace: general */
+#define DBG_UPC 21 /* show UPC information */
+#define DBG_XA1 22 /* XA mode debugging */
+#define DBG_LCK 23 /* door (un)lock info */
+#define DBG_SQ1 24 /* dump SubQ frame */
+#define DBG_AUD 25 /* READ AUDIO debugging */
+#define DBG_SEQ 26 /* Sequoia interface configuration trace */
+#define DBG_LCS 27 /* Longshine LCS-7260 debugging trace */
+#define DBG_CD2 28 /* MKE/Funai CD200 debugging trace */
+#define DBG_TEA 29 /* TEAC CD-55A debugging trace */
+#define DBG_ECS 30 /* ECS-AT (Vertos 100) debugging trace */
+#define DBG_000 31 /* unnecessary information */
+
+/*==========================================================================*/
+/*==========================================================================*/
+
+/*
+ * bits of flags_cmd_out:
+ */
+#define f_respo3 0x100
+#define f_putcmd 0x80
+#define f_respo2 0x40
+#define f_lopsta 0x20
+#define f_getsta 0x10
+#define f_ResponseStatus 0x08
+#define f_obey_p_check 0x04
+#define f_bit1 0x02
+#define f_wait_if_busy 0x01
+
+/*
+ * diskstate_flags:
+ */
+#define x80_bit 0x80
+#define upc_bit 0x40
+#define volume_bit 0x20
+#define toc_bit 0x10
+#define multisession_bit 0x08
+#define cd_size_bit 0x04
+#define subq_bit 0x02
+#define frame_size_bit 0x01
+
+/*
+ * disk states (bits of diskstate_flags):
+ */
+#define upc_valid (D_S[d].diskstate_flags&upc_bit)
+#define volume_valid (D_S[d].diskstate_flags&volume_bit)
+#define toc_valid (D_S[d].diskstate_flags&toc_bit)
+#define cd_size_valid (D_S[d].diskstate_flags&cd_size_bit)
+#define subq_valid (D_S[d].diskstate_flags&subq_bit)
+#define frame_size_valid (D_S[d].diskstate_flags&frame_size_bit)
+
+/*
+ * the status_bits variable
+ */
+#define p_success 0x100
+#define p_door_closed 0x80
+#define p_caddy_in 0x40
+#define p_spinning 0x20
+#define p_check 0x10
+#define p_busy_new 0x08
+#define p_door_locked 0x04
+#define p_disk_ok 0x01
+
+/*
+ * LCS-7260 special status result bits:
+ */
+#define p_lcs_door_locked 0x02
+#define p_lcs_door_closed 0x01 /* probably disk_in */
+
+/*
+ * CR-52x special status result bits:
+ */
+#define p_caddin_old 0x40
+#define p_success_old 0x08
+#define p_busy_old 0x04
+#define p_bit_1 0x02 /* hopefully unused now */
+
+/*
+ * "generation specific" defs of the status result bits:
+ */
+#define p0_door_closed 0x80
+#define p0_caddy_in 0x40
+#define p0_spinning 0x20
+#define p0_check 0x10
+#define p0_success 0x08 /* unused */
+#define p0_busy 0x04
+#define p0_bit_1 0x02 /* unused */
+#define p0_disk_ok 0x01
+
+#define pL_disk_in 0x40
+#define pL_spinning 0x20
+#define pL_check 0x10
+#define pL_success 0x08 /* unused ?? */
+#define pL_busy 0x04
+#define pL_door_locked 0x02
+#define pL_door_closed 0x01
+
+#define pV_door_closed 0x40
+#define pV_spinning 0x20
+#define pV_check 0x10
+#define pV_success 0x08
+#define pV_busy 0x04
+#define pV_door_locked 0x02
+#define pV_disk_ok 0x01
+
+#define p1_door_closed 0x80
+#define p1_disk_in 0x40
+#define p1_spinning 0x20
+#define p1_check 0x10
+#define p1_busy 0x08
+#define p1_door_locked 0x04
+#define p1_bit_1 0x02 /* unused */
+#define p1_disk_ok 0x01
+
+#define p2_disk_ok 0x80
+#define p2_door_locked 0x40
+#define p2_spinning 0x20
+#define p2_busy2 0x10
+#define p2_busy1 0x08
+#define p2_door_closed 0x04
+#define p2_disk_in 0x02
+#define p2_check 0x01
+
+/*
+ * used drive states:
+ */
+#define st_door_closed (D_S[d].status_bits&p_door_closed)
+#define st_caddy_in (D_S[d].status_bits&p_caddy_in)
+#define st_spinning (D_S[d].status_bits&p_spinning)
+#define st_check (D_S[d].status_bits&p_check)
+#define st_busy (D_S[d].status_bits&p_busy_new)
+#define st_door_locked (D_S[d].status_bits&p_door_locked)
+#define st_diskok (D_S[d].status_bits&p_disk_ok)
+
+/*
+ * bits of the CDi_status register:
+ */
+#define s_not_result_ready 0x04 /* 0: "result ready" */
+#define s_not_data_ready 0x02 /* 0: "data ready" */
+#define s_attention 0x01 /* 1: "attention required" */
+/*
+ * usable as:
+ */
+#define DRV_ATTN ((inb(CDi_status)&s_attention)!=0)
+#define DATA_READY ((inb(CDi_status)&s_not_data_ready)==0)
+#define RESULT_READY ((inb(CDi_status)&s_not_result_ready)==0)
+
+/*
+ * drive families and types (firmware versions):
+ */
+#define drv_fam0 0x0100 /* CR-52x family */
+#define drv_199 (drv_fam0+0x01) /* <200 */
+#define drv_200 (drv_fam0+0x02) /* <201 */
+#define drv_201 (drv_fam0+0x03) /* <210 */
+#define drv_210 (drv_fam0+0x04) /* <211 */
+#define drv_211 (drv_fam0+0x05) /* <300 */
+#define drv_300 (drv_fam0+0x06) /* >=300 */
+
+#define drv_fam1 0x0200 /* CR-56x family */
+#define drv_099 (drv_fam1+0x01) /* <100 */
+#define drv_100 (drv_fam1+0x02) /* >=100, only 1.02 and 5.00 known */
+
+#define drv_fam2 0x0400 /* CD200 family */
+
+#define drv_famT 0x0800 /* TEAC CD-55A */
+
+#define drv_famL 0x1000 /* Longshine family */
+#define drv_260 (drv_famL+0x01) /* LCS-7260 */
+#define drv_e1 (drv_famL+0x01) /* LCS-7260, firmware "A E1" */
+#define drv_f4 (drv_famL+0x02) /* LCS-7260, firmware "A4F4" */
+
+#define drv_famV 0x2000 /* ECS-AT (vertos-100) family */
+#define drv_at (drv_famV+0x01) /* ECS-AT, firmware "1.00" */
+
+#define fam0_drive (D_S[d].drv_type&drv_fam0)
+#define famL_drive (D_S[d].drv_type&drv_famL)
+#define famV_drive (D_S[d].drv_type&drv_famV)
+#define fam1_drive (D_S[d].drv_type&drv_fam1)
+#define fam2_drive (D_S[d].drv_type&drv_fam2)
+#define famT_drive (D_S[d].drv_type&drv_famT)
+#define fam0L_drive (D_S[d].drv_type&(drv_fam0|drv_famL))
+#define fam0V_drive (D_S[d].drv_type&(drv_fam0|drv_famV))
+#define famLV_drive (D_S[d].drv_type&(drv_famL|drv_famV))
+#define fam0LV_drive (D_S[d].drv_type&(drv_fam0|drv_famL|drv_famV))
+#define fam1L_drive (D_S[d].drv_type&(drv_fam1|drv_famL))
+#define fam1V_drive (D_S[d].drv_type&(drv_fam1|drv_famV))
+#define fam1LV_drive (D_S[d].drv_type&(drv_fam1|drv_famL|drv_famV))
+#define fam01_drive (D_S[d].drv_type&(drv_fam0|drv_fam1))
+#define fam12_drive (D_S[d].drv_type&(drv_fam1|drv_fam2))
+#define fam2T_drive (D_S[d].drv_type&(drv_fam2|drv_famT))
+
+/*
+ * audio states:
+ */
+#define audio_playing 2
+#define audio_pausing 1
+
+/*
+ * drv_pattern, drv_options:
+ */
+#define speed_auto 0x80
+#define speed_300 0x40
+#define speed_150 0x20
+#define audio_mono 0x04
+
+/*
+ * values of cmd_type (0 else):
+ */
+#define READ_M1 0x01 /* "data mode 1": 2048 bytes per frame */
+#define READ_M2 0x02 /* "data mode 2": 12+2048+280 bytes per frame */
+#define READ_SC 0x04 /* "subchannel info": 96 bytes per frame */
+#define READ_AU 0x08 /* "audio frame": 2352 bytes per frame */
+
+/*
+ * sense_byte:
+ *
+ * values: 00
+ * 01
+ * 81
+ * 82 "raw audio" mode
+ * xx from infobuf[0] after 85 00 00 00 00 00 00
+ */
+
+/* audio status (bin) */
+#define aud_00 0x00 /* Audio status byte not supported or not valid */
+#define audx11 0x0b /* Audio play operation in progress */
+#define audx12 0x0c /* Audio play operation paused */
+#define audx13 0x0d /* Audio play operation successfully completed */
+#define audx14 0x0e /* Audio play operation stopped due to error */
+#define audx15 0x0f /* No current audio status to return */
+/* audio status (bcd) */
+#define aud_11 0x11 /* Audio play operation in progress */
+#define aud_12 0x12 /* Audio play operation paused */
+#define aud_13 0x13 /* Audio play operation successfully completed */
+#define aud_14 0x14 /* Audio play operation stopped due to error */
+#define aud_15 0x15 /* No current audio status to return */
+
+/*
+ * highest allowed drive number (MINOR+1)
+ */
+#define NR_SBPCD 4
+
+/*
+ * we try to never disable interrupts - seems to work
+ */
+#define SBPCD_DIS_IRQ 0
+
+/*
+ * "write byte to port"
+ */
+#define OUT(x,y) outb(y,x)
+
+/*==========================================================================*/
+
+#define MIXER_addr SOUND_BASE+4 /* sound card's address register */
+#define MIXER_data SOUND_BASE+5 /* sound card's data register */
+#define MIXER_CD_Volume 0x28 /* internal SB Pro register address */
+
+/*==========================================================================*/
+
+#define MAX_TRACKS 99
+
+#define ERR_DISKCHANGE 615
+
+/*==========================================================================*/
+/*
+ * To make conversions easier (machine dependent!)
+ */
+typedef union _msf
+{
+ u_int n;
+ u_char c[4];
+} MSF;
+
+typedef union _blk
+{
+ u_int n;
+ u_char c[4];
+} BLK;
+
+/*==========================================================================*/
+
+/*============================================================================
+==============================================================================
+
+COMMAND SET of "old" drives like CR-521, CR-522
+ (the CR-562 family is different):
+
+No. Command Code
+--------------------------------------------
+
+Drive Commands:
+ 1 Seek 01
+ 2 Read Data 02
+ 3 Read XA-Data 03
+ 4 Read Header 04
+ 5 Spin Up 05
+ 6 Spin Down 06
+ 7 Diagnostic 07
+ 8 Read UPC 08
+ 9 Read ISRC 09
+10 Play Audio 0A
+11 Play Audio MSF 0B
+12 Play Audio Track/Index 0C
+
+Status Commands:
+13 Read Status 81
+14 Read Error 82
+15 Read Drive Version 83
+16 Mode Select 84
+17 Mode Sense 85
+18 Set XA Parameter 86
+19 Read XA Parameter 87
+20 Read Capacity 88
+21 Read SUB_Q 89
+22 Read Disc Code 8A
+23 Read Disc Information 8B
+24 Read TOC 8C
+25 Pause/Resume 8D
+26 Read Packet 8E
+27 Read Path Check 00
+
+
+all numbers (lba, msf-bin, msf-bcd, counts) to transfer high byte first
+
+mnemo 7-byte command #bytes response (r0...rn)
+________ ____________________ ____
+
+Read Status:
+status: 81. (1) one-byte command, gives the main
+ status byte
+Read Error:
+check1: 82 00 00 00 00 00 00. (6) r1: audio status
+
+Read Packet:
+check2: 8e xx 00 00 00 00 00. (xx) gets xx bytes response, relating
+ to commands 01 04 05 07 08 09
+
+Play Audio:
+play: 0a ll-bb-aa nn-nn-nn. (0) play audio, ll-bb-aa: starting block (lba),
+ nn-nn-nn: #blocks
+Play Audio MSF:
+ 0b mm-ss-ff mm-ss-ff (0) play audio from/to
+
+Play Audio Track/Index:
+ 0c ...
+
+Pause/Resume:
+pause: 8d pr 00 00 00 00 00. (0) pause (pr=00)
+ resume (pr=80) audio playing
+
+Mode Select:
+ 84 00 nn-nn ??-?? 00 (0) nn-nn: 2048 or 2340
+ possibly defines transfer size
+
+set_vol: 84 83 00 00 sw le 00. (0) sw(itch): lrxxxxxx (off=1)
+ le(vel): min=0, max=FF, else half
+ (firmware 2.11)
+
+Mode Sense:
+get_vol: 85 03 00 00 00 00 00. (2) tell current audio volume setting
+
+Read Disc Information:
+tocdesc: 8b 00 00 00 00 00 00. (6) read the toc descriptor ("msf-bin"-format)
+
+Read TOC:
+tocent: 8c fl nn 00 00 00 00. (8) read toc entry #nn
+ (fl=0:"lba"-, =2:"msf-bin"-format)
+
+Read Capacity:
+capacit: 88 00 00 00 00 00 00. (5) "read CD-ROM capacity"
+
+
+Read Path Check:
+ping: 00 00 00 00 00 00 00. (2) r0=AA, r1=55
+ ("ping" if the drive is connected)
+
+Read Drive Version:
+ident: 83 00 00 00 00 00 00. (12) gives "MATSHITAn.nn"
+ (n.nn = 2.01, 2.11., 3.00, ...)
+
+Seek:
+seek: 01 00 ll-bb-aa 00 00. (0)
+seek: 01 02 mm-ss-ff 00 00. (0)
+
+Read Data:
+read: 02 xx-xx-xx nn-nn fl. (??) read nn-nn blocks of 2048 bytes,
+ starting at block xx-xx-xx
+ fl=0: "lba"-, =2:"msf-bcd"-coded xx-xx-xx
+
+Read XA-Data:
+read: 03 xx-xx-xx nn-nn fl. (??) read nn-nn blocks of 2340 bytes,
+ starting at block xx-xx-xx
+ fl=0: "lba"-, =2:"msf-bcd"-coded xx-xx-xx
+
+Read SUB_Q:
+ 89 fl 00 00 00 00 00. (13) r0: audio status, r4-r7: lba/msf,
+ fl=0: "lba", fl=2: "msf"
+
+Read Disc Code:
+ 8a 00 00 00 00 00 00. (14) possibly extended "check condition"-info
+
+Read Header:
+ 04 00 ll-bb-aa 00 00. (0) 4 bytes response with "check2"
+ 04 02 mm-ss-ff 00 00. (0) 4 bytes response with "check2"
+
+Spin Up:
+ 05 00 ll-bb-aa 00 00. (0) possibly implies a "seek"
+
+Spin Down:
+ 06 ...
+
+Diagnostic:
+ 07 00 ll-bb-aa 00 00. (2) 2 bytes response with "check2"
+ 07 02 mm-ss-ff 00 00. (2) 2 bytes response with "check2"
+
+Read UPC:
+ 08 00 ll-bb-aa 00 00. (16)
+ 08 02 mm-ss-ff 00 00. (16)
+
+Read ISRC:
+ 09 00 ll-bb-aa 00 00. (15) 15 bytes response with "check2"
+ 09 02 mm-ss-ff 00 00. (15) 15 bytes response with "check2"
+
+Set XA Parameter:
+ 86 ...
+
+Read XA Parameter:
+ 87 ...
+
+==============================================================================
+============================================================================*/
+
+/*
+ * commands
+ *
+ * CR-52x: CMD0_
+ * CR-56x: CMD1_
+ * CD200: CMD2_
+ * LCS-7260: CMDL_
+ * TEAC CD-55A: CMDT_
+ * ECS-AT: CMDV_
+ */
+#define CMD1_RESET 0x0a
+#define CMD2_RESET 0x01
+#define CMDT_RESET 0xc0
+
+#define CMD1_LOCK_CTL 0x0c
+#define CMD2_LOCK_CTL 0x1e
+#define CMDT_LOCK_CTL CMD2_LOCK_CTL
+#define CMDL_LOCK_CTL 0x0e
+#define CMDV_LOCK_CTL CMDL_LOCK_CTL
+
+#define CMD1_TRAY_CTL 0x07
+#define CMD2_TRAY_CTL 0x1b
+#define CMDT_TRAY_CTL CMD2_TRAY_CTL
+#define CMDL_TRAY_CTL 0x0d
+#define CMDV_TRAY_CTL CMDL_TRAY_CTL
+
+#define CMD1_MULTISESS 0x8d
+#define CMDL_MULTISESS 0x8c
+#define CMDV_MULTISESS CMDL_MULTISESS
+
+#define CMD1_SUBCHANINF 0x11
+#define CMD2_SUBCHANINF 0x??
+
+#define CMD1_ABORT 0x08
+#define CMD2_ABORT 0x08
+#define CMDT_ABORT 0x08
+
+#define CMD2_x02 0x02
+
+#define CMD2_SETSPEED 0xda
+
+#define CMD0_PATH_CHECK 0x00
+#define CMD1_PATH_CHECK 0x???
+#define CMD2_PATH_CHECK 0x???
+#define CMDT_PATH_CHECK 0x???
+#define CMDL_PATH_CHECK CMD0_PATH_CHECK
+#define CMDV_PATH_CHECK CMD0_PATH_CHECK
+
+#define CMD0_SEEK 0x01
+#define CMD1_SEEK CMD0_SEEK
+#define CMD2_SEEK 0x2b
+#define CMDT_SEEK CMD2_SEEK
+#define CMDL_SEEK CMD0_SEEK
+#define CMDV_SEEK CMD0_SEEK
+
+#define CMD0_READ 0x02
+#define CMD1_READ 0x10
+#define CMD2_READ 0x28
+#define CMDT_READ CMD2_READ
+#define CMDL_READ CMD0_READ
+#define CMDV_READ CMD0_READ
+
+#define CMD0_READ_XA 0x03
+#define CMD2_READ_XA 0xd4
+#define CMD2_READ_XA2 0xd5
+#define CMDL_READ_XA CMD0_READ_XA /* really ?? */
+#define CMDV_READ_XA CMD0_READ_XA
+
+#define CMD0_READ_HEAD 0x04
+
+#define CMD0_SPINUP 0x05
+#define CMD1_SPINUP 0x02
+#define CMD2_SPINUP CMD2_TRAY_CTL
+#define CMDL_SPINUP CMD0_SPINUP
+#define CMDV_SPINUP CMD0_SPINUP
+
+#define CMD0_SPINDOWN 0x06 /* really??? */
+#define CMD1_SPINDOWN 0x06
+#define CMD2_SPINDOWN CMD2_TRAY_CTL
+#define CMDL_SPINDOWN 0x0d
+#define CMDV_SPINDOWN CMD0_SPINDOWN
+
+#define CMD0_DIAG 0x07
+
+#define CMD0_READ_UPC 0x08
+#define CMD1_READ_UPC 0x88
+#define CMD2_READ_UPC 0x???
+#define CMDL_READ_UPC CMD0_READ_UPC
+#define CMDV_READ_UPC 0x8f
+
+#define CMD0_READ_ISRC 0x09
+
+#define CMD0_PLAY 0x0a
+#define CMD1_PLAY 0x???
+#define CMD2_PLAY 0x???
+#define CMDL_PLAY CMD0_PLAY
+#define CMDV_PLAY CMD0_PLAY
+
+#define CMD0_PLAY_MSF 0x0b
+#define CMD1_PLAY_MSF 0x0e
+#define CMD2_PLAY_MSF 0x47
+#define CMDT_PLAY_MSF CMD2_PLAY_MSF
+#define CMDL_PLAY_MSF 0x???
+
+#define CMD0_PLAY_TI 0x0c
+#define CMD1_PLAY_TI 0x0f
+
+#define CMD0_STATUS 0x81
+#define CMD1_STATUS 0x05
+#define CMD2_STATUS 0x00
+#define CMDT_STATUS CMD2_STATUS
+#define CMDL_STATUS CMD0_STATUS
+#define CMDV_STATUS CMD0_STATUS
+#define CMD2_SEEK_LEADIN 0x00
+
+#define CMD0_READ_ERR 0x82
+#define CMD1_READ_ERR CMD0_READ_ERR
+#define CMD2_READ_ERR 0x03
+#define CMDT_READ_ERR CMD2_READ_ERR /* get audio status */
+#define CMDL_READ_ERR CMD0_READ_ERR
+#define CMDV_READ_ERR CMD0_READ_ERR
+
+#define CMD0_READ_VER 0x83
+#define CMD1_READ_VER CMD0_READ_VER
+#define CMD2_READ_VER 0x12
+#define CMDT_READ_VER CMD2_READ_VER /* really ?? */
+#define CMDL_READ_VER CMD0_READ_VER
+#define CMDV_READ_VER CMD0_READ_VER
+
+#define CMD0_SETMODE 0x84
+#define CMD1_SETMODE 0x09
+#define CMD2_SETMODE 0x55
+#define CMDT_SETMODE CMD2_SETMODE
+#define CMDL_SETMODE CMD0_SETMODE
+
+#define CMD0_GETMODE 0x85
+#define CMD1_GETMODE 0x84
+#define CMD2_GETMODE 0x5a
+#define CMDT_GETMODE CMD2_GETMODE
+#define CMDL_GETMODE CMD0_GETMODE
+
+#define CMD0_SET_XA 0x86
+
+#define CMD0_GET_XA 0x87
+
+#define CMD0_CAPACITY 0x88
+#define CMD1_CAPACITY 0x85
+#define CMD2_CAPACITY 0x25
+#define CMDL_CAPACITY CMD0_CAPACITY /* missing in some firmware versions */
+
+#define CMD0_READSUBQ 0x89
+#define CMD1_READSUBQ 0x87
+#define CMD2_READSUBQ 0x42
+#define CMDT_READSUBQ CMD2_READSUBQ
+#define CMDL_READSUBQ CMD0_READSUBQ
+#define CMDV_READSUBQ CMD0_READSUBQ
+
+#define CMD0_DISKCODE 0x8a
+
+#define CMD0_DISKINFO 0x8b
+#define CMD1_DISKINFO CMD0_DISKINFO
+#define CMD2_DISKINFO 0x43
+#define CMDT_DISKINFO CMD2_DISKINFO
+#define CMDL_DISKINFO CMD0_DISKINFO
+#define CMDV_DISKINFO CMD0_DISKINFO
+
+#define CMD0_READTOC 0x8c
+#define CMD1_READTOC CMD0_READTOC
+#define CMD2_READTOC 0x???
+#define CMDL_READTOC CMD0_READTOC
+#define CMDV_READTOC CMD0_READTOC
+
+#define CMD0_PAU_RES 0x8d
+#define CMD1_PAU_RES 0x0d
+#define CMD2_PAU_RES 0x4b
+#define CMDT_PAUSE CMD2_PAU_RES
+#define CMDL_PAU_RES CMD0_PAU_RES
+#define CMDV_PAUSE CMD0_PAU_RES
+
+#define CMD0_PACKET 0x8e
+#define CMD1_PACKET CMD0_PACKET
+#define CMD2_PACKET 0x???
+#define CMDL_PACKET CMD0_PACKET
+#define CMDV_PACKET 0x???
+
+/*==========================================================================*/
+/*==========================================================================*/
+#endif _LINUX_SBPCD_H
+/*==========================================================================*/
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/drivers/cdrom/sjcd.h b/drivers/cdrom/sjcd.h
new file mode 100644
index 000000000..0aa5e7146
--- /dev/null
+++ b/drivers/cdrom/sjcd.h
@@ -0,0 +1,181 @@
+/*
+ * Definitions for a Sanyo CD-ROM interface.
+ *
+ * Copyright (C) 1995 Vadim V. Model
+ * model@cecmow.enet.dec.com
+ * vadim@rbrf.msk.su
+ * vadim@ipsun.ras.ru
+ * Eric van der Maarel
+ * H.T.M.v.d.Maarel@marin.nl
+ *
+ * This information is based on mcd.c from M. Harriss and sjcd102.lst from
+ * E. Moenkeberg.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __SJCD_H__
+#define __SJCD_H__
+
+/*
+ * Change this to set the I/O port address as default. More flexibility
+ * come with setup implementation.
+ */
+#define SJCD_BASE_ADDR 0x340
+
+/*
+ * Change this to set the irq as default. Really SANYO do not use interrupts
+ * at all.
+ */
+#define SJCD_INTR_NR 0
+
+/*
+ * Change this to set the dma as default value. really SANYO does not use
+ * direct memory access at all.
+ */
+#define SJCD_DMA_NR 0
+
+/*
+ * Macros which allow us to find out the status of the drive.
+ */
+#define SJCD_STATUS_AVAILABLE( x ) (((x)&0x02)==0)
+#define SJCD_DATA_AVAILABLE( x ) (((x)&0x01)==0)
+
+/*
+ * Port access macro. Three ports are available: S-data port (command port),
+ * status port (read only) and D-data port (read only).
+ */
+#define SJCDPORT( x ) ( sjcd_base + ( x ) )
+#define SJCD_STATUS_PORT SJCDPORT( 1 )
+#define SJCD_S_DATA_PORT SJCDPORT( 0 )
+#define SJCD_COMMAND_PORT SJCDPORT( 0 )
+#define SJCD_D_DATA_PORT SJCDPORT( 2 )
+
+/*
+ * Drive info bits. Drive info available as first (mandatory) byte of
+ * command completion status.
+ */
+#define SST_NOT_READY 0x10 /* no disk in the drive (???) */
+#define SST_MEDIA_CHANGED 0x20 /* disk is changed */
+#define SST_DOOR_OPENED 0x40 /* door is open */
+
+/* commands */
+
+#define SCMD_EJECT_TRAY 0xD0 /* eject tray if not locked */
+#define SCMD_LOCK_TRAY 0xD2 /* lock tray when in */
+#define SCMD_UNLOCK_TRAY 0xD4 /* unlock tray when in */
+#define SCMD_CLOSE_TRAY 0xD6 /* load tray in */
+
+#define SCMD_RESET 0xFA /* soft reset */
+#define SCMD_GET_STATUS 0x80
+#define SCMD_GET_VERSION 0xCC
+
+#define SCMD_DATA_READ 0xA0 /* are the same, depend on mode&args */
+#define SCMD_SEEK 0xA0
+#define SCMD_PLAY 0xA0
+
+#define SCMD_GET_QINFO 0xA8
+
+#define SCMD_SET_MODE 0xC4
+#define SCMD_MODE_PLAY 0xE0
+#define SCMD_MODE_COOKED (0xF8 & ~0x20)
+#define SCMD_MODE_RAW 0xF9
+#define SCMD_MODE_x20_BIT 0x20 /* What is it for ? */
+
+#define SCMD_SET_VOLUME 0xAE
+#define SCMD_PAUSE 0xE0
+#define SCMD_STOP 0xE0
+
+#define SCMD_GET_DISK_INFO 0xAA
+
+/*
+ * Some standard arguments for SCMD_GET_DISK_INFO.
+ */
+#define SCMD_GET_1_TRACK 0xA0 /* get the first track information */
+#define SCMD_GET_L_TRACK 0xA1 /* get the last track information */
+#define SCMD_GET_D_SIZE 0xA2 /* get the whole disk information */
+
+/*
+ * Borrowed from hd.c. Allows to optimize multiple port read commands.
+ */
+#define S_READ_DATA( port, buf, nr ) insb( port, buf, nr )
+
+/*
+ * We assume that there are no audio disks with TOC length more than this
+ * number (I personally have never seen disks with more than 20 fragments).
+ */
+#define SJCD_MAX_TRACKS 100
+
+struct msf {
+ unsigned char min;
+ unsigned char sec;
+ unsigned char frame;
+};
+
+struct sjcd_hw_disk_info {
+ unsigned char track_control;
+ unsigned char track_no;
+ unsigned char x, y, z;
+ union {
+ unsigned char track_no;
+ struct msf track_msf;
+ } un;
+};
+
+struct sjcd_hw_qinfo {
+ unsigned char track_control;
+ unsigned char track_no;
+ unsigned char x;
+ struct msf rel;
+ struct msf abs;
+};
+
+struct sjcd_play_msf {
+ struct msf start;
+ struct msf end;
+};
+
+struct sjcd_disk_info {
+ unsigned char first;
+ unsigned char last;
+ struct msf disk_length;
+ struct msf first_track;
+};
+
+struct sjcd_toc {
+ unsigned char ctrl_addr;
+ unsigned char track;
+ unsigned char point_index;
+ struct msf track_time;
+ struct msf disk_time;
+};
+
+#if defined( SJCD_GATHER_STAT )
+
+struct sjcd_stat {
+ int ticks;
+ int tticks[ 8 ];
+ int idle_ticks;
+ int start_ticks;
+ int mode_ticks;
+ int read_ticks;
+ int data_ticks;
+ int stop_ticks;
+ int stopping_ticks;
+};
+
+#endif
+
+#endif
diff --git a/drivers/cdrom/sonycd535.h b/drivers/cdrom/sonycd535.h
new file mode 100644
index 000000000..5dea1ef16
--- /dev/null
+++ b/drivers/cdrom/sonycd535.h
@@ -0,0 +1,183 @@
+#ifndef SONYCD535_H
+#define SONYCD535_H
+
+/*
+ * define all the commands recognized by the CDU-531/5
+ */
+#define SONY535_REQUEST_DRIVE_STATUS_1 (0x80)
+#define SONY535_REQUEST_SENSE (0x82)
+#define SONY535_REQUEST_DRIVE_STATUS_2 (0x84)
+#define SONY535_REQUEST_ERROR_STATUS (0x86)
+#define SONY535_REQUEST_AUDIO_STATUS (0x88)
+#define SONY535_INQUIRY (0x8a)
+
+#define SONY535_SET_INACTIVITY_TIME (0x90)
+
+#define SONY535_SEEK_AND_READ_N_BLOCKS_1 (0xa0)
+#define SONY535_SEEK_AND_READ_N_BLOCKS_2 (0xa4)
+#define SONY535_PLAY_AUDIO (0xa6)
+
+#define SONY535_REQUEST_DISC_CAPACITY (0xb0)
+#define SONY535_REQUEST_TOC_DATA (0xb2)
+#define SONY535_REQUEST_SUB_Q_DATA (0xb4)
+#define SONY535_REQUEST_ISRC (0xb6)
+#define SONY535_REQUEST_UPC_EAN (0xb8)
+
+#define SONY535_SET_DRIVE_MODE (0xc0)
+#define SONY535_REQUEST_DRIVE_MODE (0xc2)
+#define SONY535_SET_RETRY_COUNT (0xc4)
+
+#define SONY535_DIAGNOSTIC_1 (0xc6)
+#define SONY535_DIAGNOSTIC_4 (0xcc)
+#define SONY535_DIAGNOSTIC_5 (0xce)
+
+#define SONY535_EJECT_CADDY (0xd0)
+#define SONY535_DISABLE_EJECT_BUTTON (0xd2)
+#define SONY535_ENABLE_EJECT_BUTTON (0xd4)
+
+#define SONY535_HOLD (0xe0)
+#define SONY535_AUDIO_PAUSE_ON_OFF (0xe2)
+#define SONY535_SET_VOLUME (0xe8)
+
+#define SONY535_STOP (0xf0)
+#define SONY535_SPIN_UP (0xf2)
+#define SONY535_SPIN_DOWN (0xf4)
+
+#define SONY535_CLEAR_PARAMETERS (0xf6)
+#define SONY535_CLEAR_ENDING_ADDRESS (0xf8)
+
+/*
+ * define some masks
+ */
+#define SONY535_DATA_NOT_READY_BIT (0x1)
+#define SONY535_RESULT_NOT_READY_BIT (0x2)
+
+/*
+ * drive status 1
+ */
+#define SONY535_STATUS1_COMMAND_ERROR (0x1)
+#define SONY535_STATUS1_DATA_ERROR (0x2)
+#define SONY535_STATUS1_SEEK_ERROR (0x4)
+#define SONY535_STATUS1_DISC_TYPE_ERROR (0x8)
+#define SONY535_STATUS1_NOT_SPINNING (0x10)
+#define SONY535_STATUS1_EJECT_BUTTON_PRESSED (0x20)
+#define SONY535_STATUS1_CADDY_NOT_INSERTED (0x40)
+#define SONY535_STATUS1_BYTE_TWO_FOLLOWS (0x80)
+
+/*
+ * drive status 2
+ */
+#define SONY535_CDD_LOADING_ERROR (0x7)
+#define SONY535_CDD_NO_DISC (0x8)
+#define SONY535_CDD_UNLOADING_ERROR (0x9)
+#define SONY535_CDD_CADDY_NOT_INSERTED (0xd)
+#define SONY535_ATN_RESET_OCCURRED (0x2)
+#define SONY535_ATN_DISC_CHANGED (0x4)
+#define SONY535_ATN_RESET_AND_DISC_CHANGED (0x6)
+#define SONY535_ATN_EJECT_IN_PROGRESS (0xe)
+#define SONY535_ATN_BUSY (0xf)
+
+/*
+ * define some parameters
+ */
+#define SONY535_AUDIO_DRIVE_MODE (0)
+#define SONY535_CDROM_DRIVE_MODE (0xe0)
+
+#define SONY535_PLAY_OP_PLAYBACK (0)
+#define SONY535_PLAY_OP_ENTER_HOLD (1)
+#define SONY535_PLAY_OP_SET_AUDIO_ENDING_ADDR (2)
+#define SONY535_PLAY_OP_SCAN_FORWARD (3)
+#define SONY535_PLAY_OP_SCAN_BACKWARD (4)
+
+/*
+ * convert from msf format to block number
+ */
+#define SONY_BLOCK_NUMBER(m,s,f) (((m)*60L+(s))*75L+(f))
+#define SONY_BLOCK_NUMBER_MSF(x) (((x)[0]*60L+(x)[1])*75L+(x)[2])
+
+/*
+ * error return values from the doSonyCmd() routines
+ */
+#define TIME_OUT (-1)
+#define NO_CDROM (-2)
+#define BAD_STATUS (-3)
+#define CD_BUSY (-4)
+#define NOT_DATA_CD (-5)
+#define NO_ROOM (-6)
+
+#define LOG_START_OFFSET 150 /* Offset of first logical sector */
+
+#define SONY_JIFFIES_TIMEOUT (5*HZ) /* Maximum time
+ the drive will wait/try for an
+ operation */
+#define SONY_READY_RETRIES (50000) /* How many times to retry a
+ spin waiting for a register
+ to come ready */
+#define SONY535_FAST_POLLS (10000) /* how many times recheck
+ status waiting for a data
+ to become ready */
+
+typedef unsigned char Byte;
+
+/*
+ * This is the complete status returned from the drive configuration request
+ * command.
+ */
+struct s535_sony_drive_config
+{
+ char vendor_id[8];
+ char product_id[16];
+ char product_rev_level[4];
+};
+
+/* The following is returned from the request sub-q data command */
+struct s535_sony_subcode
+{
+ unsigned char address :4;
+ unsigned char control :4;
+ unsigned char track_num;
+ unsigned char index_num;
+ unsigned char rel_msf[3];
+ unsigned char abs_msf[3];
+};
+
+struct s535_sony_disc_capacity
+{
+ Byte mFirstTrack, sFirstTrack, fFirstTrack;
+ Byte mLeadOut, sLeadOut, fLeadOut;
+};
+
+/*
+ * The following is returned from the request TOC (Table Of Contents) command.
+ * (last_track_num-first_track_num+1) values are valid in tracks.
+ */
+struct s535_sony_toc
+{
+ unsigned char reserved0 :4;
+ unsigned char control0 :4;
+ unsigned char point0;
+ unsigned char first_track_num;
+ unsigned char reserved0a;
+ unsigned char reserved0b;
+ unsigned char reserved1 :4;
+ unsigned char control1 :4;
+ unsigned char point1;
+ unsigned char last_track_num;
+ unsigned char dummy1;
+ unsigned char dummy2;
+ unsigned char reserved2 :4;
+ unsigned char control2 :4;
+ unsigned char point2;
+ unsigned char lead_out_start_msf[3];
+ struct
+ {
+ unsigned char reserved :4;
+ unsigned char control :4;
+ unsigned char track;
+ unsigned char track_start_msf[3];
+ } tracks[100];
+
+ unsigned int lead_out_start_lba;
+};
+
+#endif /* SONYCD535_H */
diff --git a/drivers/char/cd1865.h b/drivers/char/cd1865.h
new file mode 100644
index 000000000..e7407f4b8
--- /dev/null
+++ b/drivers/char/cd1865.h
@@ -0,0 +1,263 @@
+/*
+ * linux/drivers/char/cd1865.h -- Definitions relating to the CD1865
+ * for the Specialix IO8+ multiport serial driver.
+ *
+ * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
+ * Copyright (C) 1994-1996 Dmitry Gorodchanin (begemot@bgm.rosprint.net)
+ *
+ * Specialix pays for the development and support of this driver.
+ * Please DO contact io8-linux@specialix.co.uk if you require
+ * support.
+ *
+ * This driver was developped in the BitWizard linux device
+ * driver service. If you require a linux device driver for your
+ * product, please contact devices@BitWizard.nl for a quote.
+ *
+ */
+
+/*
+ * Definitions for Driving CD180/CD1864/CD1865 based eightport serial cards.
+ */
+
+
+/* Values of choice for Interrupt ACKs */
+/* These values are "obligatory" if you use the register based
+ * interrupt acknowledgements. See page 99-101 of V2.0 of the CD1865
+ * databook */
+#define SX_ACK_MINT 0x75 /* goes to PILR1 */
+#define SX_ACK_TINT 0x76 /* goes to PILR2 */
+#define SX_ACK_RINT 0x77 /* goes to PILR3 */
+
+/* Chip ID (is used when chips ar daisy chained.) */
+#define SX_ID 0x10
+
+/* Definitions for Cirrus Logic CL-CD186x 8-port async mux chip */
+
+#define CD186x_NCH 8 /* Total number of channels */
+#define CD186x_TPC 16 /* Ticks per character */
+#define CD186x_NFIFO 8 /* TX FIFO size */
+
+
+/* Global registers */
+
+#define CD186x_GIVR 0x40 /* Global Interrupt Vector Register */
+#define CD186x_GICR 0x41 /* Global Interrupting Channel Register */
+#define CD186x_PILR1 0x61 /* Priority Interrupt Level Register 1 */
+#define CD186x_PILR2 0x62 /* Priority Interrupt Level Register 2 */
+#define CD186x_PILR3 0x63 /* Priority Interrupt Level Register 3 */
+#define CD186x_CAR 0x64 /* Channel Access Register */
+#define CD186x_SRSR 0x65 /* Channel Access Register */
+#define CD186x_GFRCR 0x6b /* Global Firmware Revision Code Register */
+#define CD186x_PPRH 0x70 /* Prescaler Period Register High */
+#define CD186x_PPRL 0x71 /* Prescaler Period Register Low */
+#define CD186x_RDR 0x78 /* Receiver Data Register */
+#define CD186x_RCSR 0x7a /* Receiver Character Status Register */
+#define CD186x_TDR 0x7b /* Transmit Data Register */
+#define CD186x_EOIR 0x7f /* End of Interrupt Register */
+#define CD186x_MRAR 0x75 /* Modem Request Acknowlege register */
+#define CD186x_TRAR 0x76 /* Transmit Request Acknowlege register */
+#define CD186x_RRAR 0x77 /* Recieve Request Acknowlege register */
+#define CD186x_SRCR 0x66 /* Service Request Configuration register */
+
+/* Channel Registers */
+
+#define CD186x_CCR 0x01 /* Channel Command Register */
+#define CD186x_IER 0x02 /* Interrupt Enable Register */
+#define CD186x_COR1 0x03 /* Channel Option Register 1 */
+#define CD186x_COR2 0x04 /* Channel Option Register 2 */
+#define CD186x_COR3 0x05 /* Channel Option Register 3 */
+#define CD186x_CCSR 0x06 /* Channel Control Status Register */
+#define CD186x_RDCR 0x07 /* Receive Data Count Register */
+#define CD186x_SCHR1 0x09 /* Special Character Register 1 */
+#define CD186x_SCHR2 0x0a /* Special Character Register 2 */
+#define CD186x_SCHR3 0x0b /* Special Character Register 3 */
+#define CD186x_SCHR4 0x0c /* Special Character Register 4 */
+#define CD186x_MCOR1 0x10 /* Modem Change Option 1 Register */
+#define CD186x_MCOR2 0x11 /* Modem Change Option 2 Register */
+#define CD186x_MCR 0x12 /* Modem Change Register */
+#define CD186x_RTPR 0x18 /* Receive Timeout Period Register */
+#define CD186x_MSVR 0x28 /* Modem Signal Value Register */
+#define CD186x_MSVRTS 0x29 /* Modem Signal Value Register */
+#define CD186x_MSVDTR 0x2a /* Modem Signal Value Register */
+#define CD186x_RBPRH 0x31 /* Receive Baud Rate Period Register High */
+#define CD186x_RBPRL 0x32 /* Receive Baud Rate Period Register Low */
+#define CD186x_TBPRH 0x39 /* Transmit Baud Rate Period Register High */
+#define CD186x_TBPRL 0x3a /* Transmit Baud Rate Period Register Low */
+
+
+/* Global Interrupt Vector Register (R/W) */
+
+#define GIVR_ITMASK 0x07 /* Interrupt type mask */
+#define GIVR_IT_MODEM 0x01 /* Modem Signal Change Interrupt */
+#define GIVR_IT_TX 0x02 /* Transmit Data Interrupt */
+#define GIVR_IT_RCV 0x03 /* Receive Good Data Interrupt */
+#define GIVR_IT_REXC 0x07 /* Receive Exception Interrupt */
+
+
+/* Global Interrupt Channel Register (R/W) */
+
+#define GICR_CHAN 0x1c /* Channel Number Mask */
+#define GICR_CHAN_OFF 2 /* Channel Number shift */
+
+
+/* Channel Address Register (R/W) */
+
+#define CAR_CHAN 0x07 /* Channel Number Mask */
+#define CAR_A7 0x08 /* A7 Address Extension (unused) */
+
+
+/* Receive Character Status Register (R/O) */
+
+#define RCSR_TOUT 0x80 /* Rx Timeout */
+#define RCSR_SCDET 0x70 /* Special Character Detected Mask */
+#define RCSR_NO_SC 0x00 /* No Special Characters Detected */
+#define RCSR_SC_1 0x10 /* Special Char 1 (or 1 & 3) Detected */
+#define RCSR_SC_2 0x20 /* Special Char 2 (or 2 & 4) Detected */
+#define RCSR_SC_3 0x30 /* Special Char 3 Detected */
+#define RCSR_SC_4 0x40 /* Special Char 4 Detected */
+#define RCSR_BREAK 0x08 /* Break has been detected */
+#define RCSR_PE 0x04 /* Parity Error */
+#define RCSR_FE 0x02 /* Frame Error */
+#define RCSR_OE 0x01 /* Overrun Error */
+
+
+/* Channel Command Register (R/W) (commands in groups can be OR-ed) */
+
+#define CCR_HARDRESET 0x81 /* Reset the chip */
+
+#define CCR_SOFTRESET 0x80 /* Soft Channel Reset */
+
+#define CCR_CORCHG1 0x42 /* Channel Option Register 1 Changed */
+#define CCR_CORCHG2 0x44 /* Channel Option Register 2 Changed */
+#define CCR_CORCHG3 0x48 /* Channel Option Register 3 Changed */
+
+#define CCR_SSCH1 0x21 /* Send Special Character 1 */
+
+#define CCR_SSCH2 0x22 /* Send Special Character 2 */
+
+#define CCR_SSCH3 0x23 /* Send Special Character 3 */
+
+#define CCR_SSCH4 0x24 /* Send Special Character 4 */
+
+#define CCR_TXEN 0x18 /* Enable Transmitter */
+#define CCR_RXEN 0x12 /* Enable Receiver */
+
+#define CCR_TXDIS 0x14 /* Disable Transmitter */
+#define CCR_RXDIS 0x11 /* Disable Receiver */
+
+
+/* Interrupt Enable Register (R/W) */
+
+#define IER_DSR 0x80 /* Enable interrupt on DSR change */
+#define IER_CD 0x40 /* Enable interrupt on CD change */
+#define IER_CTS 0x20 /* Enable interrupt on CTS change */
+#define IER_RXD 0x10 /* Enable interrupt on Receive Data */
+#define IER_RXSC 0x08 /* Enable interrupt on Receive Spec. Char */
+#define IER_TXRDY 0x04 /* Enable interrupt on TX FIFO empty */
+#define IER_TXEMPTY 0x02 /* Enable interrupt on TX completely empty */
+#define IER_RET 0x01 /* Enable interrupt on RX Exc. Timeout */
+
+
+/* Channel Option Register 1 (R/W) */
+
+#define COR1_ODDP 0x80 /* Odd Parity */
+#define COR1_PARMODE 0x60 /* Parity Mode mask */
+#define COR1_NOPAR 0x00 /* No Parity */
+#define COR1_FORCEPAR 0x20 /* Force Parity */
+#define COR1_NORMPAR 0x40 /* Normal Parity */
+#define COR1_IGNORE 0x10 /* Ignore Parity on RX */
+#define COR1_STOPBITS 0x0c /* Number of Stop Bits */
+#define COR1_1SB 0x00 /* 1 Stop Bit */
+#define COR1_15SB 0x04 /* 1.5 Stop Bits */
+#define COR1_2SB 0x08 /* 2 Stop Bits */
+#define COR1_CHARLEN 0x03 /* Character Length */
+#define COR1_5BITS 0x00 /* 5 bits */
+#define COR1_6BITS 0x01 /* 6 bits */
+#define COR1_7BITS 0x02 /* 7 bits */
+#define COR1_8BITS 0x03 /* 8 bits */
+
+
+/* Channel Option Register 2 (R/W) */
+
+#define COR2_IXM 0x80 /* Implied XON mode */
+#define COR2_TXIBE 0x40 /* Enable In-Band (XON/XOFF) Flow Control */
+#define COR2_ETC 0x20 /* Embedded Tx Commands Enable */
+#define COR2_LLM 0x10 /* Local Loopback Mode */
+#define COR2_RLM 0x08 /* Remote Loopback Mode */
+#define COR2_RTSAO 0x04 /* RTS Automatic Output Enable */
+#define COR2_CTSAE 0x02 /* CTS Automatic Enable */
+#define COR2_DSRAE 0x01 /* DSR Automatic Enable */
+
+
+/* Channel Option Register 3 (R/W) */
+
+#define COR3_XONCH 0x80 /* XON is a pair of characters (1 & 3) */
+#define COR3_XOFFCH 0x40 /* XOFF is a pair of characters (2 & 4) */
+#define COR3_FCT 0x20 /* Flow-Control Transparency Mode */
+#define COR3_SCDE 0x10 /* Special Character Detection Enable */
+#define COR3_RXTH 0x0f /* RX FIFO Threshold value (1-8) */
+
+
+/* Channel Control Status Register (R/O) */
+
+#define CCSR_RXEN 0x80 /* Receiver Enabled */
+#define CCSR_RXFLOFF 0x40 /* Receive Flow Off (XOFF was sent) */
+#define CCSR_RXFLON 0x20 /* Receive Flow On (XON was sent) */
+#define CCSR_TXEN 0x08 /* Transmitter Enabled */
+#define CCSR_TXFLOFF 0x04 /* Transmit Flow Off (got XOFF) */
+#define CCSR_TXFLON 0x02 /* Transmit Flow On (got XON) */
+
+
+/* Modem Change Option Register 1 (R/W) */
+
+#define MCOR1_DSRZD 0x80 /* Detect 0->1 transition of DSR */
+#define MCOR1_CDZD 0x40 /* Detect 0->1 transition of CD */
+#define MCOR1_CTSZD 0x20 /* Detect 0->1 transition of CTS */
+#define MCOR1_DTRTH 0x0f /* Auto DTR flow control Threshold (1-8) */
+#define MCOR1_NODTRFC 0x0 /* Automatic DTR flow control disabled */
+
+
+/* Modem Change Option Register 2 (R/W) */
+
+#define MCOR2_DSROD 0x80 /* Detect 1->0 transition of DSR */
+#define MCOR2_CDOD 0x40 /* Detect 1->0 transition of CD */
+#define MCOR2_CTSOD 0x20 /* Detect 1->0 transition of CTS */
+
+/* Modem Change Register (R/W) */
+
+#define MCR_DSRCHG 0x80 /* DSR Changed */
+#define MCR_CDCHG 0x40 /* CD Changed */
+#define MCR_CTSCHG 0x20 /* CTS Changed */
+
+
+/* Modem Signal Value Register (R/W) */
+
+#define MSVR_DSR 0x80 /* Current state of DSR input */
+#define MSVR_CD 0x40 /* Current state of CD input */
+#define MSVR_CTS 0x20 /* Current state of CTS input */
+#define MSVR_DTR 0x02 /* Current state of DTR output */
+#define MSVR_RTS 0x01 /* Current state of RTS output */
+
+
+/* Escape characters */
+
+#define CD186x_C_ESC 0x00 /* Escape character */
+#define CD186x_C_SBRK 0x81 /* Start sending BREAK */
+#define CD186x_C_DELAY 0x82 /* Delay output */
+#define CD186x_C_EBRK 0x83 /* Stop sending BREAK */
+
+#define SRSR_RREQint 0x10 /* This chip wants "rec" serviced */
+#define SRSR_TREQint 0x04 /* This chip wants "transmit" serviced */
+#define SRSR_MREQint 0x01 /* This chip wants "mdm change" serviced */
+
+
+
+#define SRCR_PKGTYPE 0x80
+#define SRCR_REGACKEN 0x40
+#define SRCR_DAISYEN 0x20
+#define SRCR_GLOBPRI 0x10
+#define SRCR_UNFAIR 0x08
+#define SRCR_AUTOPRI 0x02
+#define SRCR_PRISEL 0x01
+
+
diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c
new file mode 100644
index 000000000..edd80e3d2
--- /dev/null
+++ b/drivers/char/specialix.c
@@ -0,0 +1,2333 @@
+/*
+ * specialix.c -- specialix IO8+ multiport serial driver.
+ *
+ * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
+ * Copyright (C) 1994-1996 Dmitry Gorodchanin (begemot@bgm.rosprint.net)
+ *
+ * Specialix pays for the development and support of this driver.
+ * Please DO contact io8-linux@specialix.co.uk if you require
+ * support. But please read the documentation (specialix.txt)
+ * first.
+ *
+ * This driver was developped in the BitWizard linux device
+ * driver service. If you require a linux device driver for your
+ * product, please contact devices@BitWizard.nl for a quote.
+ *
+ * This code is firmly based on the riscom/8 serial driver,
+ * written by Dmitry Gorodchanin. The specialix IO8+ card
+ * programming information was obtained from the CL-CD1865 Data
+ * Book, and Specialix document number 6200059: IO8+ Hardware
+ * Functional Specification.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+ * USA.
+ *
+ * Revision history:
+ *
+ * Revision 1.0: April 1st 1997.
+ * Initial release for alpha testing.
+ * Revision 1.1: April 14th 1997.
+ * Incorporated Richard Hudsons suggestions,
+ * removed some debugging printk's.
+ * Revision 1.2: April 15th 1997.
+ * Ported to 2.1.x kernels.
+ * Revision 1.3: April 17th 1997
+ * Backported to 2.0. (Compatibility macros).
+ * Revision 1.4: April 18th 1997
+ * Fixed DTR/RTS bug that caused the card to indicate
+ * "don't send data" to a modem after the password prompt.
+ * Fixed bug for premature (fake) interrupts.
+ * Revision 1.5: April 19th 1997
+ * fixed a minor typo in the header file, cleanup a little.
+ * performance warnings are now MAXed at once per minute.
+ * Revision 1.6: May 23 1997
+ * Changed the specialix=... format to include interrupt.
+ * Revision 1.7: May 27 1997
+ * Made many more debug printk's a compile time option.
+ * Revision 1.8: Jul 1 1997
+ * port to linux-2.1.43 kernel.
+ *
+ */
+
+#define VERSION "1.8"
+
+
+/*
+ * There is a bunch of documentation about the card, jumpers, config
+ * settings, restrictions, cables, device names and numbers in
+ * ../../Documentation/specialix.txt
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/mm.h>
+#include <linux/serial.h>
+#include <linux/fcntl.h>
+#include <linux/major.h>
+#include <linux/delay.h>
+#include <linux/tqueue.h>
+#include <linux/version.h>
+
+
+/* ************************************************************** */
+/* * This section can be removed when 2.0 becomes outdated.... * */
+/* ************************************************************** */
+
+#if LINUX_VERSION_CODE < 131328 /* Less than 2.1.0 */
+#define TWO_ZERO
+#else
+#if LINUX_VERSION_CODE < 131371 /* less than 2.1.43 */
+/* This has not been extensively tested yet. Sorry. */
+#warning "You're on your own between 2.1.0 and 2.1.43.... "
+#warning "Please use a recent kernel."
+#endif
+#endif
+
+
+#ifdef TWO_ZERO
+#define Get_user(a,b) a = get_user(b)
+#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c)
+#define copy_to_user(a,b,c) memcpy_tofs(a,b,c)
+#define queue_task queue_task_irq_off
+#else
+#define Get_user(a,b) get_user(a,b)
+#endif
+
+/* ************************************************************** */
+/* * End of compatibility section.. * */
+/* ************************************************************** */
+
+
+#ifndef TWO_ZERO
+#include <asm/uaccess.h>
+#endif
+
+#include "specialix_io8.h"
+#include "cd1865.h"
+
+
+
+/* Configurable options: */
+
+/* Am I paranoid or not ? ;-) */
+#define SPECIALIX_PARANOIA_CHECK
+
+/* Do I trust the IRQ from the card? (enabeling it doesn't seem to help)
+ When the IRQ routine leaves the chip in a state that is keeps on
+ requiring attention, the timer doesn't help either. */
+#undef SPECIALIX_TIMER
+
+/*
+ * The following defines are mostly for testing purposes. But if you need
+ * some nice reporting in your syslog, you can define them also.
+ */
+#undef SX_REPORT_FIFO
+#undef SX_REPORT_OVERRUN
+
+
+
+#ifdef CONFIG_SPECIALIX_RTSCTS
+#define SX_CRTSCTS(bla) 1
+#else
+#define SX_CRTSCTS(tty) C_CRTSCTS(tty)
+#endif
+
+
+/* Used to be outb (0xff, 0x80); */
+#define short_pause() udelay (1)
+
+
+#define SPECIALIX_LEGAL_FLAGS \
+ (ASYNC_HUP_NOTIFY | ASYNC_SAK | ASYNC_SPLIT_TERMIOS | \
+ ASYNC_SPD_HI | ASYNC_SPEED_VHI | ASYNC_SESSION_LOCKOUT | \
+ ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+DECLARE_TASK_QUEUE(tq_specialix);
+
+
+
+#define SPECIALIX_TYPE_NORMAL 1
+#define SPECIALIX_TYPE_CALLOUT 2
+
+static struct specialix_board * IRQ_to_board[16] = { NULL, } ;
+static struct tty_driver specialix_driver, specialix_callout_driver;
+static int specialix_refcount = 0;
+static struct tty_struct * specialix_table[SX_NBOARD * SX_NPORT] = { NULL, };
+static struct termios * specialix_termios[SX_NBOARD * SX_NPORT] = { NULL, };
+static struct termios * specialix_termios_locked[SX_NBOARD * SX_NPORT] = { NULL, };
+static unsigned char * tmp_buf = NULL;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static unsigned long baud_table[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+ 9600, 19200, 38400, 57600, 115200, 0,
+};
+
+static struct specialix_board sx_board[SX_NBOARD] = {
+ { 0, SX_IOBASE1, 9, },
+ { 0, SX_IOBASE2, 11, },
+ { 0, SX_IOBASE3, 12, },
+ { 0, SX_IOBASE4, 15, },
+};
+
+static struct specialix_port sx_port[SX_NBOARD * SX_NPORT] = {
+ { 0, },
+};
+
+
+#ifdef SPECIALIX_TIMER
+static struct timer_list missed_irq_timer;
+static void sx_interrupt(int irq, void * dev_id, struct pt_regs * regs);
+#endif
+
+
+
+static inline int sx_paranoia_check(struct specialix_port const * port,
+ kdev_t device, const char *routine)
+{
+#ifdef SPECIALIX_PARANOIA_CHECK
+ static const char *badmagic =
+ KERN_ERR "sx: Warning: bad specialix port magic number for device %s in %s\n";
+ static const char *badinfo =
+ KERN_ERR "sx: Warning: null specialix port for device %s in %s\n";
+
+ if (!port) {
+ printk(badinfo, kdevname(device), routine);
+ return 1;
+ }
+ if (port->magic != SPECIALIX_MAGIC) {
+ printk(badmagic, kdevname(device), routine);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+
+/*
+ *
+ * Service functions for specialix IO8+ driver.
+ *
+ */
+
+/* Get board number from pointer */
+extern inline int board_No (struct specialix_board * bp)
+{
+ return bp - sx_board;
+}
+
+
+/* Get port number from pointer */
+extern inline int port_No (struct specialix_port const * port)
+{
+ return SX_PORT(port - sx_port);
+}
+
+
+/* Get pointer to board from pointer to port */
+extern inline struct specialix_board * port_Board(struct specialix_port const * port)
+{
+ return &sx_board[SX_BOARD(port - sx_port)];
+}
+
+
+/* Input Byte from CL CD186x register */
+extern inline unsigned char sx_in(struct specialix_board * bp, unsigned short reg)
+{
+ bp->reg = reg | 0x80;
+ outb (reg | 0x80, bp->base + SX_ADDR_REG);
+ return inb (bp->base + SX_DATA_REG);
+}
+
+
+/* Output Byte to CL CD186x register */
+extern inline void sx_out(struct specialix_board * bp, unsigned short reg,
+ unsigned char val)
+{
+ bp->reg = reg | 0x80;
+ outb (reg | 0x80, bp->base + SX_ADDR_REG);
+ outb (val, bp->base + SX_DATA_REG);
+}
+
+
+/* Input Byte from CL CD186x register */
+extern inline unsigned char sx_in_off(struct specialix_board * bp, unsigned short reg)
+{
+ bp->reg = reg;
+ outb (reg, bp->base + SX_ADDR_REG);
+ return inb (bp->base + SX_DATA_REG);
+}
+
+
+/* Output Byte to CL CD186x register */
+extern inline void sx_out_off(struct specialix_board * bp, unsigned short reg,
+ unsigned char val)
+{
+ bp->reg = reg;
+ outb (reg, bp->base + SX_ADDR_REG);
+ outb (val, bp->base + SX_DATA_REG);
+}
+
+
+/* Wait for Channel Command Register ready */
+extern inline void sx_wait_CCR(struct specialix_board * bp)
+{
+ unsigned long delay;
+
+ for (delay = SX_CCR_TIMEOUT; delay; delay--)
+ if (!sx_in(bp, CD186x_CCR))
+ return;
+
+ printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
+}
+
+
+/* Wait for Channel Command Register ready */
+extern inline void sx_wait_CCR_off(struct specialix_board * bp)
+{
+ unsigned long delay;
+
+ for (delay = SX_CCR_TIMEOUT; delay; delay--)
+ if (!sx_in_off(bp, CD186x_CCR))
+ return;
+
+ printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
+}
+
+
+/*
+ * specialix IO8+ IO range functions.
+ */
+
+extern inline int sx_check_io_range(struct specialix_board * bp)
+{
+ return check_region (bp->base, SX_IO_SPACE);
+}
+
+
+extern inline void sx_request_io_range(struct specialix_board * bp)
+{
+ request_region(bp->base, SX_IO_SPACE, "specialix IO8+" );
+}
+
+
+extern inline void sx_release_io_range(struct specialix_board * bp)
+{
+ release_region(bp->base, SX_IO_SPACE);
+}
+
+
+/* Must be called with enabled interrupts */
+extern inline void sx_long_delay(unsigned long delay)
+{
+ unsigned long i;
+
+ for (i = jiffies + delay; i > jiffies; ) ;
+}
+
+
+
+/* Set the IRQ using the RTS lines that run to the PAL on the board.... */
+int sx_set_irq ( struct specialix_board *bp)
+{
+ int virq;
+ int i;
+
+ switch (bp->irq) {
+ /* In the same order as in the docs... */
+ case 15: virq = 0;break;
+ case 12: virq = 1;break;
+ case 11: virq = 2;break;
+ case 9: virq = 3;break;
+ default: printk (KERN_ERR "Speclialix: cannot set irq to %d.\n", bp->irq);
+ return 0;
+ }
+
+ for (i=0;i<2;i++) {
+ sx_out(bp, CD186x_CAR, i);
+ sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0);
+ }
+ return 1;
+}
+
+
+/* Reset and setup CD186x chip */
+static int sx_init_CD186x(struct specialix_board * bp)
+{
+ unsigned long flags;
+ int scaler;
+ int rv = 1;
+
+ save_flags(flags); cli();
+
+ sx_wait_CCR_off(bp); /* Wait for CCR ready */
+ sx_out_off(bp, CD186x_CCR, CCR_HARDRESET); /* Reset CD186x chip */
+ sti();
+ sx_long_delay(HZ/20); /* Delay 0.05 sec */
+ cli();
+ sx_out_off(bp, CD186x_GIVR, SX_ID); /* Set ID for this chip */
+ sx_out_off(bp, CD186x_GICR, 0); /* Clear all bits */
+ sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT); /* Prio for modem intr */
+ sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT); /* Prio for transmitter intr */
+ sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT); /* Prio for receiver intr */
+ /* Set RegAckEn */
+ sx_out_off(bp, CD186x_SRCR, sx_in (bp, CD186x_SRCR) | SRCR_REGACKEN);
+
+ /* Setting up prescaler. We need 4 ticks per 1 ms */
+ scaler = SX_OSCFREQ/SPECIALIX_TPS;
+
+ sx_out_off(bp, CD186x_PPRH, scaler >> 8);
+ sx_out_off(bp, CD186x_PPRL, scaler & 0xff);
+
+ if (!sx_set_irq (bp)) {
+ /* Figure out how to pass this along... */
+ printk (KERN_ERR "Cannot set irq to %d.\n", bp->irq);
+ rv = 0;
+ }
+
+ restore_flags(flags);
+ return rv;
+}
+
+
+int read_cross_byte (struct specialix_board *bp, int reg, int bit)
+{
+ int i;
+ int t;
+
+ for (i=0, t=0;i<8;i++) {
+ sx_out_off (bp, CD186x_CAR, i);
+ if (sx_in_off (bp, reg) & bit)
+ t |= 1 << i;
+ }
+ return t;
+}
+
+
+#ifdef SPECIALIX_TIMER
+void missed_irq (unsigned long data)
+{
+ if (sx_in ((struct specialix_board *)data, CD186x_SRSR) &
+ (SRSR_RREQint |
+ SRSR_TREQint |
+ SRSR_MREQint)) {
+ printk (KERN_INFO "Missed interrupt... Calling int from timer. \n");
+ sx_interrupt (((struct specialix_board *)data)->irq,
+ NULL, NULL);
+ }
+ missed_irq_timer.expires = jiffies + HZ;
+ add_timer (&missed_irq_timer);
+}
+#endif
+
+
+
+/* Main probing routine, also sets irq. */
+static int sx_probe(struct specialix_board *bp)
+{
+ unsigned char val1, val2;
+#if 0
+ int irqs = 0;
+ int retries;
+#endif
+ int rev;
+ int chip;
+
+ if (sx_check_io_range(bp))
+ return 1;
+
+ /* Are the I/O ports here ? */
+ sx_out_off(bp, CD186x_PPRL, 0x5a);
+ short_pause ();
+ val1 = sx_in_off(bp, CD186x_PPRL);
+
+ sx_out_off(bp, CD186x_PPRL, 0xa5);
+ short_pause ();
+ val2 = sx_in_off(bp, CD186x_PPRL);
+
+
+ if ((val1 != 0x5a) || (val2 != 0xa5)) {
+ printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n",
+ board_No(bp), bp->base);
+ return 1;
+ }
+
+ /* Check the DSR lines that Specialix uses as board
+ identification */
+ val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR);
+ val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS);
+#ifdef SPECIALIX_DEBUG
+ printk (KERN_DEBUG "sx%d: DSR lines are: %02x, rts lines are: %02x\n",
+ board_No(bp), val1, val2);
+#endif
+ if (val1 != 0xb2) {
+ printk(KERN_INFO "sx%d: specialix IO8+ ID at 0x%03x not found.\n",
+ board_No(bp), bp->base);
+ return 1;
+ }
+
+
+#if 0
+ /* It's time to find IRQ for this board */
+ for (retries = 0; retries < 5 && irqs <= 0; retries++) {
+ irqs = probe_irq_on();
+ sx_init_CD186x(bp); /* Reset CD186x chip */
+ sx_out(bp, CD186x_CAR, 2); /* Select port 2 */
+ sx_wait_CCR(bp);
+ sx_out(bp, CD186x_CCR, CCR_TXEN); /* Enable transmitter */
+ sx_out(bp, CD186x_IER, IER_TXRDY); /* Enable tx empty intr */
+ sx_long_delay(HZ/20);
+ irqs = probe_irq_off(irqs);
+
+#if SPECIALIX_DEBUG > 2
+ printk (KERN_DEBUG "SRSR = %02x, ", sx_in(bp, CD186x_SRSR));
+ printk ( "TRAR = %02x, ", sx_in(bp, CD186x_TRAR));
+ printk ( "GIVR = %02x, ", sx_in(bp, CD186x_GIVR));
+ printk ( "GICR = %02x, ", sx_in(bp, CD186x_GICR));
+ printk ( "\n");
+#endif
+ /* Reset CD186x again */
+ if (!sx_init_CD186x(bp)) {
+ /* Hmmm. This is dead code anyway. */
+ }
+#if SPECIALIX_DEBUG > 2
+ printk (KERN_DEBUG "val1 = %02x, val2 = %02x, val3 = %02x.\n",
+ val1, val2, val3);
+#endif
+
+ }
+
+#if 0
+ if (irqs <= 0) {
+ printk(KERN_ERR "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n",
+ board_No(bp), bp->base);
+ return 1;
+ }
+#endif
+ printk (KERN_INFO "Started with irq=%d, but now have irq=%d.\n", bp->irq, irqs);
+ if (irqs > 0)
+ bp->irq = irqs;
+#endif
+ /* Reset CD186x again */
+ if (!sx_init_CD186x(bp)) {
+ return -EIO;
+ }
+
+ sx_request_io_range(bp);
+ bp->flags |= SX_BOARD_PRESENT;
+
+ /* Chip revcode pkgtype
+ GFRCR SRCR bit 7
+ CD180 rev B 0x81 0
+ CD180 rev C 0x82 0
+ CD1864 rev A 0x82 1
+ CD1865 rev A 0x83 1 -- Do not use!!! Does not work.
+ CD1865 rev B 0x84 1
+ -- Thanks to Gwen Wang, Cirrus Logic.
+ */
+
+ switch (sx_in_off(bp, CD186x_GFRCR)) {
+ case 0x82:chip = 1864;rev='A';break;
+ case 0x83:chip = 1865;rev='A';break;
+ case 0x84:chip = 1865;rev='B';break;
+ case 0x85:chip = 1865;rev='C';break; /* Does not exist at this time */
+ default:chip=-1;rev='x';
+ }
+
+#if SPECIALIX_DEBUG > 2
+ printk (KERN_DEBUG " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) );
+#endif
+
+#ifdef SPECIALIX_TIMER
+ init_timer (&missed_irq_timer);
+ missed_irq_timer.function = missed_irq;
+ missed_irq_timer.data = (unsigned long) bp;
+ missed_irq_timer.expires = jiffies + HZ;
+ add_timer (&missed_irq_timer);
+#endif
+
+ printk(KERN_INFO"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
+ board_No(bp),
+ bp->base, bp->irq,
+ chip, rev);
+
+ return 0;
+}
+
+/*
+ *
+ * Interrupt processing routines.
+ * */
+
+extern inline void sx_mark_event(struct specialix_port * port, int event)
+{
+ /*
+ * I'm not quite happy with current scheme all serial
+ * drivers use their own BH routine.
+ * It seems this easily can be done with one BH routine
+ * serving for all serial drivers.
+ * For now I must introduce another one - SPECIALIX_BH.
+ * Still hope this will be changed in near future.
+ * -- Dmitry.
+ */
+ set_bit(event, &port->event);
+ queue_task(&port->tqueue, &tq_specialix);
+ mark_bh(SPECIALIX_BH);
+}
+
+
+extern inline struct specialix_port * sx_get_port(struct specialix_board * bp,
+ unsigned char const * what)
+{
+ unsigned char channel;
+ struct specialix_port * port;
+
+ channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
+ if (channel < CD186x_NCH) {
+ port = &sx_port[board_No(bp) * SX_NPORT + channel];
+ if (port->flags & ASYNC_INITIALIZED) {
+ return port;
+ }
+ }
+ printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n",
+ board_No(bp), what, channel);
+ return NULL;
+}
+
+
+extern inline void sx_receive_exc(struct specialix_board * bp)
+{
+ struct specialix_port *port;
+ struct tty_struct *tty;
+ unsigned char status;
+ unsigned char ch;
+
+ if (!(port = sx_get_port(bp, "Receive")))
+ return;
+
+ tty = port->tty;
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n",
+ board_No(bp), port_No(port));
+ return;
+ }
+
+#ifdef SX_REPORT_OVERRUN
+ status = sx_in(bp, CD186x_RCSR);
+ if (status & RCSR_OE) {
+ port->overrun++;
+#if SPECIALIX_DEBUG
+ printk(KERN_DEBUG "sx%d: port %d: Overrun. Total %ld overruns.\n",
+ board_No(bp), port_No(port), port->overrun);
+#endif
+ }
+ status &= port->mark_mask;
+#else
+ status = sx_in(bp, CD186x_RCSR) & port->mark_mask;
+#endif
+ ch = sx_in(bp, CD186x_RDR);
+ if (!status) {
+ return;
+ }
+ if (status & RCSR_TOUT) {
+ printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n",
+ board_No(bp), port_No(port));
+ return;
+
+ } else if (status & RCSR_BREAK) {
+#ifdef SPECIALIX_DEBUG
+ printk(KERN_DEBUG "sx%d: port %d: Handling break...\n",
+ board_No(bp), port_No(port));
+#endif
+ *tty->flip.flag_buf_ptr++ = TTY_BREAK;
+ if (port->flags & ASYNC_SAK)
+ do_SAK(tty);
+
+ } else if (status & RCSR_PE)
+ *tty->flip.flag_buf_ptr++ = TTY_PARITY;
+
+ else if (status & RCSR_FE)
+ *tty->flip.flag_buf_ptr++ = TTY_FRAME;
+
+ else if (status & RCSR_OE)
+ *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
+
+ else
+ *tty->flip.flag_buf_ptr++ = 0;
+
+ *tty->flip.char_buf_ptr++ = ch;
+ tty->flip.count++;
+ queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+
+extern inline void sx_receive(struct specialix_board * bp)
+{
+ struct specialix_port *port;
+ struct tty_struct *tty;
+ unsigned char count;
+
+ if (!(port = sx_get_port(bp, "Receive")))
+ return;
+
+ tty = port->tty;
+
+ count = sx_in(bp, CD186x_RDCR);
+
+#ifdef SX_REPORT_FIFO
+ port->hits[count > 8 ? 9 : count]++;
+#endif
+
+ while (count--) {
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n",
+ board_No(bp), port_No(port));
+ break;
+ }
+ *tty->flip.char_buf_ptr++ = sx_in(bp, CD186x_RDR);
+ *tty->flip.flag_buf_ptr++ = 0;
+ tty->flip.count++;
+ }
+ queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+
+extern inline void sx_transmit(struct specialix_board * bp)
+{
+ struct specialix_port *port;
+ struct tty_struct *tty;
+ unsigned char count;
+
+
+ if (!(port = sx_get_port(bp, "Transmit")))
+ return;
+
+ tty = port->tty;
+
+ if (port->IER & IER_TXEMPTY) {
+ /* FIFO drained */
+ sx_out(bp, CD186x_CAR, port_No(port));
+ port->IER &= ~IER_TXEMPTY;
+ sx_out(bp, CD186x_IER, port->IER);
+ return;
+ }
+
+ if ((port->xmit_cnt <= 0 && !port->break_length)
+ || tty->stopped || tty->hw_stopped) {
+ sx_out(bp, CD186x_CAR, port_No(port));
+ port->IER &= ~IER_TXRDY;
+ sx_out(bp, CD186x_IER, port->IER);
+ return;
+ }
+
+ if (port->break_length) {
+ if (port->break_length > 0) {
+ if (port->COR2 & COR2_ETC) {
+ sx_out(bp, CD186x_TDR, CD186x_C_ESC);
+ sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
+ port->COR2 &= ~COR2_ETC;
+ }
+ count = MIN(port->break_length, 0xff);
+ sx_out(bp, CD186x_TDR, CD186x_C_ESC);
+ sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
+ sx_out(bp, CD186x_TDR, count);
+ if (!(port->break_length -= count))
+ port->break_length--;
+ } else {
+ sx_out(bp, CD186x_TDR, CD186x_C_ESC);
+ sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
+ sx_out(bp, CD186x_COR2, port->COR2);
+ sx_wait_CCR(bp);
+ sx_out(bp, CD186x_CCR, CCR_CORCHG2);
+ port->break_length = 0;
+ }
+ return;
+ }
+
+ count = CD186x_NFIFO;
+ do {
+ sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
+ port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
+ if (--port->xmit_cnt <= 0)
+ break;
+ } while (--count > 0);
+
+ if (port->xmit_cnt <= 0) {
+ sx_out(bp, CD186x_CAR, port_No(port));
+ port->IER &= ~IER_TXRDY;
+ sx_out(bp, CD186x_IER, port->IER);
+ }
+ if (port->xmit_cnt <= port->wakeup_chars)
+ sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
+}
+
+
+extern inline void sx_check_modem(struct specialix_board * bp)
+{
+ struct specialix_port *port;
+ struct tty_struct *tty;
+ unsigned char mcr;
+
+#ifdef SPECIALIX_DEBUG
+ printk (KERN_DEBUG "Modem intr. ");
+#endif
+ if (!(port = sx_get_port(bp, "Modem")))
+ return;
+
+ tty = port->tty;
+
+ mcr = sx_in(bp, CD186x_MCR);
+ printk ("mcr = %02x.\n", mcr);
+
+ if ((mcr & MCR_CDCHG)) {
+#ifdef SPECIALIX_DEBUG
+ printk (KERN_DEBUG "CD just changed... ");
+#endif
+ if (sx_in(bp, CD186x_MSVR) & MSVR_CD) {
+#ifdef SPECIALIX_DEBUG
+ printk ( "Waking up guys in open.\n");
+#endif
+ wake_up_interruptible(&port->open_wait);
+ }
+ else if (!((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (port->flags & ASYNC_CALLOUT_NOHUP))) {
+#ifdef SPECIALIX_DEBUG
+ printk ( "Sending HUP.\n");
+#endif
+ queue_task(&port->tqueue_hangup,
+ &tq_scheduler);
+ } else {
+#ifdef SPECIALIX_DEBUG
+ printk ( "Don't need to send HUP.\n");
+#endif
+ }
+ }
+
+#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
+ if (mcr & MCR_CTSCHG) {
+ if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
+ tty->hw_stopped = 0;
+ port->IER |= IER_TXRDY;
+ if (port->xmit_cnt <= port->wakeup_chars)
+ sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
+ } else {
+ tty->hw_stopped = 1;
+ port->IER &= ~IER_TXRDY;
+ }
+ sx_out(bp, CD186x_IER, port->IER);
+ }
+ if (mcr & MCR_DSSXHG) {
+ if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
+ tty->hw_stopped = 0;
+ port->IER |= IER_TXRDY;
+ if (port->xmit_cnt <= port->wakeup_chars)
+ sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
+ } else {
+ tty->hw_stopped = 1;
+ port->IER &= ~IER_TXRDY;
+ }
+ sx_out(bp, CD186x_IER, port->IER);
+ }
+#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
+
+ /* Clear change bits */
+ sx_out(bp, CD186x_MCR, 0);
+}
+
+
+/* The main interrupt processing routine */
+static void sx_interrupt(int irq, void * dev_id, struct pt_regs * regs)
+{
+ unsigned char status;
+ unsigned char ack;
+ struct specialix_board *bp;
+ unsigned long loop = 0;
+ int saved_reg;
+
+ bp = IRQ_to_board[irq];
+
+ if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) {
+#ifdef SPECIALIX_DEBUG
+ printk (KERN_DEBUG "sx: False interrupt. irq %d.\n", irq);
+#endif
+ return;
+ }
+
+ saved_reg = bp->reg;
+
+ while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) &
+ (SRSR_RREQint |
+ SRSR_TREQint |
+ SRSR_MREQint)))) {
+ if (status & SRSR_RREQint) {
+ ack = sx_in(bp, CD186x_RRAR);
+
+ if (ack == (SX_ID | GIVR_IT_RCV))
+ sx_receive(bp);
+ else if (ack == (SX_ID | GIVR_IT_REXC))
+ sx_receive_exc(bp);
+ else
+ printk(KERN_ERR "sx%d: Bad receive ack 0x%02x.\n",
+ board_No(bp), ack);
+
+ } else if (status & SRSR_TREQint) {
+ ack = sx_in(bp, CD186x_TRAR);
+
+ if (ack == (SX_ID | GIVR_IT_TX))
+ sx_transmit(bp);
+ else
+ printk(KERN_ERR "sx%d: Bad transmit ack 0x%02x.\n",
+ board_No(bp), ack);
+ } else if (status & SRSR_MREQint) {
+ ack = sx_in(bp, CD186x_MRAR);
+
+ if (ack == (SX_ID | GIVR_IT_MODEM))
+ sx_check_modem(bp);
+ else
+ printk(KERN_ERR "sx%d: Bad modem ack 0x%02x.\n",
+ board_No(bp), ack);
+
+ }
+
+ sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */
+ }
+ bp->reg = saved_reg;
+ outb (bp->reg, bp->base + SX_ADDR_REG);
+}
+
+
+/*
+ * Routines for open & close processing.
+ */
+
+
+/* Called with disabled interrupts */
+extern inline int sx_setup_board(struct specialix_board * bp)
+{
+ int error;
+
+ if (bp->flags & SX_BOARD_ACTIVE)
+ return 0;
+
+ error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", NULL);
+
+ if (error)
+ return error;
+
+ IRQ_to_board[bp->irq] = bp;
+ (void) sx_in (bp, 0); /* Turn ON interrupts. */
+
+ bp->flags |= SX_BOARD_ACTIVE;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+
+/* Called with disabled interrupts */
+extern inline void sx_shutdown_board(struct specialix_board *bp)
+{
+ if (!(bp->flags & SX_BOARD_ACTIVE))
+ return;
+
+ bp->flags &= ~SX_BOARD_ACTIVE;
+
+ free_irq(bp->irq, NULL);
+ (void) sx_in_off (bp, 0); /* Turn off interrupts. */
+
+ IRQ_to_board[bp->irq] = NULL;
+
+ MOD_DEC_USE_COUNT;
+}
+
+
+/*
+ * Setting up port characteristics.
+ * Must be called with disabled interrupts
+ */
+static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port)
+{
+ struct tty_struct *tty;
+ unsigned long baud;
+ long tmp;
+ unsigned char cor1 = 0, cor3 = 0;
+ unsigned char mcor1 = 0, mcor2 = 0;
+ static int again=0;
+
+ if (!(tty = port->tty) || !tty->termios)
+ return;
+
+ port->IER = 0;
+ port->COR2 = 0;
+ /* Select port on the board */
+ sx_out(bp, CD186x_CAR, port_No(port));
+
+ /* The Specialix board doens't implement the RTS lines.
+ They are used to set the IRQ level. Don't touch them. */
+ if (SX_CRTSCTS(tty))
+ port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
+ else
+ port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
+#ifdef DEBUG_SPECIALIX
+ printk (KERN_DEBUG "sx: got MSVR=%02x.\n", port->MSVR);
+#endif
+ baud = C_BAUD(tty);
+
+ if (baud & CBAUDEX) {
+ baud &= ~CBAUDEX;
+ if (baud < 1 || baud > 2)
+ port->tty->termios->c_cflag &= ~CBAUDEX;
+ else
+ baud += 15;
+ }
+ if (baud == 15) {
+ if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+ baud ++;
+ if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+ baud += 2;
+ }
+
+
+ if (!baud_table[baud]) {
+ /* Drop DTR & exit */
+#ifdef SPECIALIX_DEBUG
+ printk (KERN_DEBUG "Dropping DTR... Hmm....\n");
+#endif
+ if (!SX_CRTSCTS (tty)) {
+ port -> MSVR &= ~ MSVR_DTR;
+ sx_out(bp, CD186x_MSVR, port->MSVR );
+ }
+#ifdef DEBUG_SPECIALIX
+ else
+ printk (KERN_DEBUG "Can't drop DTR: no DTR.\n");
+#endif
+ return;
+ } else {
+ /* Set DTR on */
+ if (!SX_CRTSCTS (tty)) {
+ port ->MSVR |= MSVR_DTR;
+ }
+ }
+
+ /*
+ * Now we must calculate some speed depended things
+ */
+
+ /* Set baud rate for port */
+ tmp = (((SX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] +
+ CD186x_TPC/2) / CD186x_TPC);
+ if ((tmp < 0x10) && (again < jiffies)) {
+ again = jiffies + HZ * 60;
+ /* Page 48 of version 2.0 of the CL-CD1865 databook */
+ if (tmp >= 12) {
+ printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
+ "Performance degradation is possible.\n",
+ port_No (port), tmp);
+ } else {
+ printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
+ "Warning: overstressing Cirrus chip. "
+ "This might not work.\n",
+ port_No (port), tmp);
+ }
+ }
+
+ sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff);
+ sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff);
+ sx_out(bp, CD186x_RBPRL, tmp & 0xff);
+ sx_out(bp, CD186x_TBPRL, tmp & 0xff);
+
+ baud = (baud_table[baud] + 5) / 10; /* Estimated CPS */
+
+ /* Two timer ticks seems enough to wakeup something like SLIP driver */
+ tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;
+ port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
+ SERIAL_XMIT_SIZE - 1 : tmp);
+
+ /* Receiver timeout will be transmission time for 1.5 chars */
+ tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
+ tmp = (tmp > 0xff) ? 0xff : tmp;
+ sx_out(bp, CD186x_RTPR, tmp);
+
+ switch (C_CSIZE(tty)) {
+ case CS5:
+ cor1 |= COR1_5BITS;
+ break;
+ case CS6:
+ cor1 |= COR1_6BITS;
+ break;
+ case CS7:
+ cor1 |= COR1_7BITS;
+ break;
+ case CS8:
+ cor1 |= COR1_8BITS;
+ break;
+ }
+
+ if (C_CSTOPB(tty))
+ cor1 |= COR1_2SB;
+
+ cor1 |= COR1_IGNORE;
+ if (C_PARENB(tty)) {
+ cor1 |= COR1_NORMPAR;
+ if (C_PARODD(tty))
+ cor1 |= COR1_ODDP;
+ if (I_INPCK(tty))
+ cor1 &= ~COR1_IGNORE;
+ }
+ /* Set marking of some errors */
+ port->mark_mask = RCSR_OE | RCSR_TOUT;
+ if (I_INPCK(tty))
+ port->mark_mask |= RCSR_FE | RCSR_PE;
+ if (I_BRKINT(tty) || I_PARMRK(tty))
+ port->mark_mask |= RCSR_BREAK;
+ if (I_IGNPAR(tty))
+ port->mark_mask &= ~(RCSR_FE | RCSR_PE);
+ if (I_IGNBRK(tty)) {
+ port->mark_mask &= ~RCSR_BREAK;
+ if (I_IGNPAR(tty))
+ /* Real raw mode. Ignore all */
+ port->mark_mask &= ~RCSR_OE;
+ }
+ /* Enable Hardware Flow Control */
+ if (C_CRTSCTS(tty)) {
+#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
+ port->IER |= IER_DSR | IER_CTS;
+ mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
+ mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
+ tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR));
+#else
+ port->COR2 |= COR2_CTSAE;
+#endif
+ }
+ /* Enable Software Flow Control. FIXME: I'm not sure about this */
+ /* Some people reported that it works, but I still doubt it */
+ if (I_IXON(tty)) {
+ port->COR2 |= COR2_TXIBE;
+ cor3 |= (COR3_FCT | COR3_SCDE);
+ if (I_IXANY(tty))
+ port->COR2 |= COR2_IXM;
+ sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
+ sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
+ sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
+ sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
+ }
+ if (!C_CLOCAL(tty)) {
+ /* Enable CD check */
+ port->IER |= IER_CD;
+ mcor1 |= MCOR1_CDZD;
+ mcor2 |= MCOR2_CDOD;
+ }
+
+ if (C_CREAD(tty))
+ /* Enable receiver */
+ port->IER |= IER_RXD;
+
+ /* Set input FIFO size (1-8 bytes) */
+ cor3 |= SPECIALIX_RXFIFO;
+ /* Setting up CD186x channel registers */
+ sx_out(bp, CD186x_COR1, cor1);
+ sx_out(bp, CD186x_COR2, port->COR2);
+ sx_out(bp, CD186x_COR3, cor3);
+ /* Make CD186x know about registers change */
+ sx_wait_CCR(bp);
+ sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
+ /* Setting up modem option registers */
+#ifdef DEBUG_SPECIALIX
+ printk ("Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2);
+#endif
+ sx_out(bp, CD186x_MCOR1, mcor1);
+ sx_out(bp, CD186x_MCOR2, mcor2);
+ /* Enable CD186x transmitter & receiver */
+ sx_wait_CCR(bp);
+ sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
+ /* Enable interrupts */
+ sx_out(bp, CD186x_IER, port->IER);
+ /* And finally set the modem lines... */
+ sx_out(bp, CD186x_MSVR, port->MSVR);
+}
+
+
+/* Must be called with interrupts enabled */
+static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port)
+{
+ unsigned long flags;
+
+ if (port->flags & ASYNC_INITIALIZED)
+ return 0;
+
+ if (!port->xmit_buf) {
+ /* We may sleep in get_free_page() */
+ unsigned long tmp;
+
+ if (!(tmp = get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+
+ if (port->xmit_buf) {
+ free_page(tmp);
+ return -ERESTARTSYS;
+ }
+ port->xmit_buf = (unsigned char *) tmp;
+ }
+
+ save_flags(flags); cli();
+
+ if (port->tty)
+ clear_bit(TTY_IO_ERROR, &port->tty->flags);
+
+ if (port->count == 1)
+ bp->count++;
+
+ port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+ sx_change_speed(bp, port);
+ port->flags |= ASYNC_INITIALIZED;
+
+ restore_flags(flags);
+ return 0;
+}
+
+
+/* Must be called with interrupts disabled */
+static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port)
+{
+ struct tty_struct *tty;
+
+ if (!(port->flags & ASYNC_INITIALIZED))
+ return;
+
+#ifdef SX_REPORT_OVERRUN
+ printk(KERN_INFO "sx%d: port %d: Total %ld overruns were detected.\n",
+ board_No(bp), port_No(port), port->overrun);
+#endif
+#ifdef SX_REPORT_FIFO
+ {
+ int i;
+
+ printk(KERN_INFO "sx%d: port %d: FIFO hits [ ",
+ board_No(bp), port_No(port));
+ for (i = 0; i < 10; i++) {
+ printk("%ld ", port->hits[i]);
+ }
+ printk("].\n");
+ }
+#endif
+ if (port->xmit_buf) {
+ free_page((unsigned long) port->xmit_buf);
+ port->xmit_buf = NULL;
+ }
+
+ /* Select port */
+ sx_out(bp, CD186x_CAR, port_No(port));
+
+ if (!(tty = port->tty) || C_HUPCL(tty)) {
+ /* Drop DTR */
+ sx_out(bp, CD186x_MSVDTR, 0);
+ }
+
+ /* Reset port */
+ sx_wait_CCR(bp);
+ sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
+ /* Disable all interrupts from this port */
+ port->IER = 0;
+ sx_out(bp, CD186x_IER, port->IER);
+
+ if (tty)
+ set_bit(TTY_IO_ERROR, &tty->flags);
+ port->flags &= ~ASYNC_INITIALIZED;
+
+ if (--bp->count < 0) {
+ printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d\n",
+ board_No(bp), bp->count);
+ bp->count = 0;
+ }
+
+ /*
+ * If this is the last opened port on the board
+ * shutdown whole board
+ */
+ if (!bp->count)
+ sx_shutdown_board(bp);
+}
+
+
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+ struct specialix_port *port)
+{
+ struct wait_queue wait = { current, NULL };
+ struct specialix_board *bp = port_Board(port);
+ int retval;
+ int do_clocal = 0;
+ int CD;
+
+ /*
+ * If the device is in the middle of being closed, then block
+ * until it's done, and then try again.
+ */
+ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
+ interruptible_sleep_on(&port->close_wait);
+ if (port->flags & ASYNC_HUP_NOTIFY)
+ return -EAGAIN;
+ else
+ return -ERESTARTSYS;
+ }
+
+ /*
+ * If this is a callout device, then just make sure the normal
+ * device isn't being used.
+ */
+ if (tty->driver.subtype == SPECIALIX_TYPE_CALLOUT) {
+ if (port->flags & ASYNC_NORMAL_ACTIVE)
+ return -EBUSY;
+ if ((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (port->flags & ASYNC_SESSION_LOCKOUT) &&
+ (port->session != current->session))
+ return -EBUSY;
+ if ((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (port->flags & ASYNC_PGRP_LOCKOUT) &&
+ (port->pgrp != current->pgrp))
+ return -EBUSY;
+ port->flags |= ASYNC_CALLOUT_ACTIVE;
+ return 0;
+ }
+
+ /*
+ * If non-blocking mode is set, or the port is not enabled,
+ * then make the check up front and then exit.
+ */
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (tty->flags & (1 << TTY_IO_ERROR))) {
+ if (port->flags & ASYNC_CALLOUT_ACTIVE)
+ return -EBUSY;
+ port->flags |= ASYNC_NORMAL_ACTIVE;
+ return 0;
+ }
+
+ if (port->flags & ASYNC_CALLOUT_ACTIVE) {
+ if (port->normal_termios.c_cflag & CLOCAL)
+ do_clocal = 1;
+ } else {
+ if (C_CLOCAL(tty))
+ do_clocal = 1;
+ }
+
+ /*
+ * Block waiting for the carrier detect and the line to become
+ * free (i.e., not in use by the callout). While we are in
+ * this loop, info->count is dropped by one, so that
+ * rs_close() knows when to free things. We restore it upon
+ * exit, either normal or abnormal.
+ */
+ retval = 0;
+ add_wait_queue(&port->open_wait, &wait);
+ cli();
+ if (!tty_hung_up_p(filp))
+ port->count--;
+ sti();
+ port->blocked_open++;
+ while (1) {
+ cli();
+ sx_out(bp, CD186x_CAR, port_No(port));
+ CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
+ if (!(port->flags & ASYNC_CALLOUT_ACTIVE)) {
+ if (SX_CRTSCTS (tty)) {
+ /* Activate RTS */
+ port->MSVR |= MSVR_DTR;
+ sx_out (bp, CD186x_MSVR, port->MSVR);
+ } else {
+ /* Activate DTR */
+ port->MSVR |= MSVR_DTR;
+ sx_out (bp, CD186x_MSVR, port->MSVR);
+ }
+ }
+ sti();
+ current->state = TASK_INTERRUPTIBLE;
+ if (tty_hung_up_p(filp) ||
+ !(port->flags & ASYNC_INITIALIZED)) {
+ if (port->flags & ASYNC_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTSYS;
+ break;
+ }
+ if (!(port->flags & ASYNC_CALLOUT_ACTIVE) &&
+ !(port->flags & ASYNC_CLOSING) &&
+ (do_clocal || CD))
+ break;
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&port->open_wait, &wait);
+ if (!tty_hung_up_p(filp))
+ port->count++;
+ port->blocked_open--;
+ if (retval)
+ return retval;
+
+ port->flags |= ASYNC_NORMAL_ACTIVE;
+ return 0;
+}
+
+
+static int sx_open(struct tty_struct * tty, struct file * filp)
+{
+ int board;
+ int error;
+ struct specialix_port * port;
+ struct specialix_board * bp;
+ unsigned long flags;
+
+ board = SX_BOARD(MINOR(tty->device));
+
+ if (board > SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT))
+ return -ENODEV;
+
+ bp = &sx_board[board];
+ port = sx_port + board * SX_NPORT + SX_PORT(MINOR(tty->device));
+
+#ifdef DEBUG_SPECIALIX
+ printk (KERN_DEBUG "Board = %d, bp = %p, port = %p, portno = %d.\n",
+ board, bp, port, SX_PORT(MINOR(tty->device)));
+#endif
+
+ if (sx_paranoia_check(port, tty->device, "sx_open"))
+ return -ENODEV;
+
+ if ((error = sx_setup_board(bp)))
+ return error;
+
+ port->count++;
+ tty->driver_data = port;
+ port->tty = tty;
+
+ if ((error = sx_setup_port(bp, port)))
+ return error;
+
+ if ((error = block_til_ready(tty, filp, port)))
+ return error;
+
+ if ((port->count == 1) && (port->flags & ASYNC_SPLIT_TERMIOS)) {
+ if (tty->driver.subtype == SPECIALIX_TYPE_NORMAL)
+ *tty->termios = port->normal_termios;
+ else
+ *tty->termios = port->callout_termios;
+ save_flags(flags); cli();
+ sx_change_speed(bp, port);
+ restore_flags(flags);
+ }
+
+ port->session = current->session;
+ port->pgrp = current->pgrp;
+ return 0;
+}
+
+
+static void sx_close(struct tty_struct * tty, struct file * filp)
+{
+ struct specialix_port *port = (struct specialix_port *) tty->driver_data;
+ struct specialix_board *bp;
+ unsigned long flags;
+ unsigned long timeout;
+
+ if (!port || sx_paranoia_check(port, tty->device, "close"))
+ return;
+
+ save_flags(flags); cli();
+ if (tty_hung_up_p(filp)) {
+ restore_flags(flags);
+ return;
+ }
+
+ bp = port_Board(port);
+ if ((tty->count == 1) && (port->count != 1)) {
+ printk(KERN_ERR "sx%d: sx_close: bad port count;"
+ " tty->count is 1, port count is %d\n",
+ board_No(bp), port->count);
+ port->count = 1;
+ }
+ if (--port->count < 0) {
+ printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n",
+ board_No(bp), port_No(port), port->count);
+ port->count = 0;
+ }
+ if (port->count) {
+ restore_flags(flags);
+ return;
+ }
+ port->flags |= ASYNC_CLOSING;
+ /*
+ * Save the termios structure, since this port may have
+ * separate termios for callout and dialin.
+ */
+ if (port->flags & ASYNC_NORMAL_ACTIVE)
+ port->normal_termios = *tty->termios;
+ if (port->flags & ASYNC_CALLOUT_ACTIVE)
+ port->callout_termios = *tty->termios;
+ /*
+ * Now we wait for the transmit buffer to clear; and we notify
+ * the line discipline to only process XON/XOFF characters.
+ */
+ tty->closing = 1;
+ if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+ tty_wait_until_sent(tty, port->closing_wait);
+ /*
+ * At this point we stop accepting input. To do this, we
+ * disable the receive line status interrupts, and tell the
+ * interrupt driver to stop checking the data ready bit in the
+ * line status register.
+ */
+ port->IER &= ~IER_RXD;
+ if (port->flags & ASYNC_INITIALIZED) {
+ port->IER &= ~IER_TXRDY;
+ port->IER |= IER_TXEMPTY;
+ sx_out(bp, CD186x_CAR, port_No(port));
+ sx_out(bp, CD186x_IER, port->IER);
+ /*
+ * Before we drop DTR, make sure the UART transmitter
+ * has completely drained; this is especially
+ * important if there is a transmit FIFO!
+ */
+ timeout = jiffies+HZ;
+ while(port->IER & IER_TXEMPTY) {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + port->timeout;
+ schedule();
+ if (jiffies > timeout) {
+ printk (KERN_INFO "Timeout waiting for close\n");
+ break;
+ }
+ }
+
+ }
+ sx_shutdown_port(bp, port);
+ if (tty->driver.flush_buffer)
+ tty->driver.flush_buffer(tty);
+ if (tty->ldisc.flush_buffer)
+ tty->ldisc.flush_buffer(tty);
+ tty->closing = 0;
+ port->event = 0;
+ port->tty = 0;
+ if (port->blocked_open) {
+ if (port->close_delay) {
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + port->close_delay;
+ schedule();
+ }
+ wake_up_interruptible(&port->open_wait);
+ }
+ port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+ ASYNC_CLOSING);
+ wake_up_interruptible(&port->close_wait);
+ restore_flags(flags);
+}
+
+
+static int sx_write(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+ struct specialix_board *bp;
+ int c, total = 0;
+ unsigned long flags;
+
+ if (sx_paranoia_check(port, tty->device, "sx_write"))
+ return 0;
+
+ bp = port_Board(port);
+
+ if (!tty || !port->xmit_buf || !tmp_buf)
+ return 0;
+
+ if (from_user)
+ down(&tmp_buf_sem);
+
+ save_flags(flags);
+ while (1) {
+ cli();
+ c = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - port->xmit_head));
+ if (c <= 0)
+ break;
+
+ if (from_user) {
+ copy_from_user(tmp_buf, buf, c);
+ c = MIN(c, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
+ SERIAL_XMIT_SIZE - port->xmit_head));
+ memcpy(port->xmit_buf + port->xmit_head, tmp_buf, c);
+ } else
+ memcpy(port->xmit_buf + port->xmit_head, buf, c);
+ port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+ port->xmit_cnt += c;
+ restore_flags(flags);
+ buf += c;
+ count -= c;
+ total += c;
+ }
+ if (from_user)
+ up(&tmp_buf_sem);
+ if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
+ !(port->IER & IER_TXRDY)) {
+ port->IER |= IER_TXRDY;
+ sx_out(bp, CD186x_CAR, port_No(port));
+ sx_out(bp, CD186x_IER, port->IER);
+ }
+ restore_flags(flags);
+ return total;
+}
+
+
+static void sx_put_char(struct tty_struct * tty, unsigned char ch)
+{
+ struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+ unsigned long flags;
+
+ if (sx_paranoia_check(port, tty->device, "sx_put_char"))
+ return;
+
+ if (!tty || !port->xmit_buf)
+ return;
+
+ save_flags(flags); cli();
+
+ if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+ restore_flags(flags);
+ return;
+ }
+
+ port->xmit_buf[port->xmit_head++] = ch;
+ port->xmit_head &= SERIAL_XMIT_SIZE - 1;
+ port->xmit_cnt++;
+ restore_flags(flags);
+}
+
+
+static void sx_flush_chars(struct tty_struct * tty)
+{
+ struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+ unsigned long flags;
+
+ if (sx_paranoia_check(port, tty->device, "sx_flush_chars"))
+ return;
+
+ if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+ !port->xmit_buf)
+ return;
+
+ save_flags(flags); cli();
+ port->IER |= IER_TXRDY;
+ sx_out(port_Board(port), CD186x_CAR, port_No(port));
+ sx_out(port_Board(port), CD186x_IER, port->IER);
+ restore_flags(flags);
+}
+
+
+static int sx_write_room(struct tty_struct * tty)
+{
+ struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+ int ret;
+
+ if (sx_paranoia_check(port, tty->device, "sx_write_room"))
+ return 0;
+
+ ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
+ if (ret < 0)
+ ret = 0;
+ return ret;
+}
+
+
+static int sx_chars_in_buffer(struct tty_struct *tty)
+{
+ struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+
+ if (sx_paranoia_check(port, tty->device, "sx_chars_in_buffer"))
+ return 0;
+
+ return port->xmit_cnt;
+}
+
+
+static void sx_flush_buffer(struct tty_struct *tty)
+{
+ struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+ unsigned long flags;
+
+ if (sx_paranoia_check(port, tty->device, "sx_flush_buffer"))
+ return;
+
+ save_flags(flags); cli();
+ port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+ restore_flags(flags);
+
+ wake_up_interruptible(&tty->write_wait);
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+}
+
+
+static int sx_get_modem_info(struct specialix_port * port, unsigned int *value)
+{
+ struct specialix_board * bp;
+ unsigned char status;
+ unsigned int result;
+ unsigned long flags;
+
+ bp = port_Board(port);
+ save_flags(flags); cli();
+ sx_out(bp, CD186x_CAR, port_No(port));
+ status = sx_in(bp, CD186x_MSVR);
+ restore_flags(flags);
+#ifdef DEBUG_SPECIALIX
+ printk (KERN_DEBUG "Got msvr[%d] = %02x, car = %d.\n",
+ port_No(port), status, sx_in (bp, CD186x_CAR));
+ printk (KERN_DEBUG "sx_port = %p, port = %p\n", sx_port, port);
+#endif
+ if (SX_CRTSCTS(port->tty)) {
+ result = /* (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */
+ | ((status & MSVR_DTR) ? TIOCM_RTS : 0)
+ | ((status & MSVR_CD) ? TIOCM_CAR : 0)
+ |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
+ | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
+ } else {
+ result = /* (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */
+ | ((status & MSVR_DTR) ? TIOCM_DTR : 0)
+ | ((status & MSVR_CD) ? TIOCM_CAR : 0)
+ |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
+ | ((status & MSVR_CTS) ? TIOCM_CTS : 0);
+ }
+ put_user(result,(unsigned long *) value);
+ return 0;
+}
+
+
+static int sx_set_modem_info(struct specialix_port * port, unsigned int cmd,
+ unsigned int *value)
+{
+ int error;
+ unsigned int arg;
+ unsigned long flags;
+ struct specialix_board *bp = port_Board(port);
+
+ error = verify_area(VERIFY_READ, value, sizeof(int));
+ if (error)
+ return error;
+
+ Get_user(arg, (unsigned long *) value);
+ switch (cmd) {
+ case TIOCMBIS:
+ /* if (arg & TIOCM_RTS)
+ port->MSVR |= MSVR_RTS; */
+ /* if (arg & TIOCM_DTR)
+ port->MSVR |= MSVR_DTR; */
+
+ if (SX_CRTSCTS(port->tty)) {
+ if (arg & TIOCM_RTS)
+ port->MSVR |= MSVR_DTR;
+ } else {
+ if (arg & TIOCM_DTR)
+ port->MSVR |= MSVR_DTR;
+ }
+ break;
+ case TIOCMBIC:
+ /* if (arg & TIOCM_RTS)
+ port->MSVR &= ~MSVR_RTS; */
+ /* if (arg & TIOCM_DTR)
+ port->MSVR &= ~MSVR_DTR; */
+ if (SX_CRTSCTS(port->tty)) {
+ if (arg & TIOCM_RTS)
+ port->MSVR &= ~MSVR_DTR;
+ } else {
+ if (arg & TIOCM_DTR)
+ port->MSVR &= ~MSVR_DTR;
+ }
+ break;
+ case TIOCMSET:
+ /* port->MSVR = (arg & TIOCM_RTS) ? (port->MSVR | MSVR_RTS) :
+ (port->MSVR & ~MSVR_RTS); */
+ /* port->MSVR = (arg & TIOCM_DTR) ? (port->MSVR | MSVR_DTR) :
+ (port->MSVR & ~MSVR_DTR); */
+ if (SX_CRTSCTS(port->tty)) {
+ port->MSVR = (arg & TIOCM_RTS) ?
+ (port->MSVR | MSVR_DTR) :
+ (port->MSVR & ~MSVR_DTR);
+ } else {
+ port->MSVR = (arg & TIOCM_DTR) ?
+ (port->MSVR | MSVR_DTR):
+ (port->MSVR & ~MSVR_DTR);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ save_flags(flags); cli();
+ sx_out(bp, CD186x_CAR, port_No(port));
+ sx_out(bp, CD186x_MSVR, port->MSVR);
+ restore_flags(flags);
+ return 0;
+}
+
+
+extern inline void sx_send_break(struct specialix_port * port, unsigned long length)
+{
+ struct specialix_board *bp = port_Board(port);
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ port->break_length = SPECIALIX_TPS / HZ * length;
+ port->COR2 |= COR2_ETC;
+ port->IER |= IER_TXRDY;
+ sx_out(bp, CD186x_CAR, port_No(port));
+ sx_out(bp, CD186x_COR2, port->COR2);
+ sx_out(bp, CD186x_IER, port->IER);
+ sx_wait_CCR(bp);
+ sx_out(bp, CD186x_CCR, CCR_CORCHG2);
+ sx_wait_CCR(bp);
+ restore_flags(flags);
+}
+
+
+extern inline int sx_set_serial_info(struct specialix_port * port,
+ struct serial_struct * newinfo)
+{
+ struct serial_struct tmp;
+ struct specialix_board *bp = port_Board(port);
+ int change_speed;
+ unsigned long flags;
+ int error;
+
+ error = verify_area(VERIFY_READ, (void *) newinfo, sizeof(tmp));
+ if (error)
+ return error;
+
+ copy_from_user(&tmp, newinfo, sizeof(tmp));
+
+#if 0
+ if ((tmp.irq != bp->irq) ||
+ (tmp.port != bp->base) ||
+ (tmp.type != PORT_CIRRUS) ||
+ (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) ||
+ (tmp.custom_divisor != 0) ||
+ (tmp.xmit_fifo_size != CD186x_NFIFO) ||
+ (tmp.flags & ~SPECIALIX_LEGAL_FLAGS))
+ return -EINVAL;
+#endif
+
+ change_speed = ((port->flags & ASYNC_SPD_MASK) !=
+ (tmp.flags & ASYNC_SPD_MASK));
+
+ if (!suser()) {
+ if ((tmp.close_delay != port->close_delay) ||
+ (tmp.closing_wait != port->closing_wait) ||
+ ((tmp.flags & ~ASYNC_USR_MASK) !=
+ (port->flags & ~ASYNC_USR_MASK)))
+ return -EPERM;
+ port->flags = ((port->flags & ~ASYNC_USR_MASK) |
+ (tmp.flags & ASYNC_USR_MASK));
+ } else {
+ port->flags = ((port->flags & ~ASYNC_FLAGS) |
+ (tmp.flags & ASYNC_FLAGS));
+ port->close_delay = tmp.close_delay;
+ port->closing_wait = tmp.closing_wait;
+ }
+ if (change_speed) {
+ save_flags(flags); cli();
+ sx_change_speed(bp, port);
+ restore_flags(flags);
+ }
+ return 0;
+}
+
+
+extern inline int sx_get_serial_info(struct specialix_port * port,
+ struct serial_struct * retinfo)
+{
+ struct serial_struct tmp;
+ struct specialix_board *bp = port_Board(port);
+ int error;
+
+ error = verify_area(VERIFY_WRITE, (void *) retinfo, sizeof(tmp));
+ if (error)
+ return error;
+
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.type = PORT_CIRRUS;
+ tmp.line = port - sx_port;
+ tmp.port = bp->base;
+ tmp.irq = bp->irq;
+ tmp.flags = port->flags;
+ tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
+ tmp.close_delay = port->close_delay * HZ/100;
+ tmp.closing_wait = port->closing_wait * HZ/100;
+ tmp.xmit_fifo_size = CD186x_NFIFO;
+ copy_to_user(retinfo, &tmp, sizeof(tmp));
+ return 0;
+}
+
+
+static int sx_ioctl(struct tty_struct * tty, struct file * filp,
+ unsigned int cmd, unsigned long arg)
+{
+ struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+ int error;
+ int retval;
+
+ if (sx_paranoia_check(port, tty->device, "sx_ioctl"))
+ return -ENODEV;
+
+ switch (cmd) {
+ case TCSBRK: /* SVID version: non-zero arg --> no break */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ if (!arg)
+ sx_send_break(port, HZ/4); /* 1/4 second */
+ return 0;
+ case TCSBRKP: /* support for POSIX tcsendbreak() */
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ sx_send_break(port, arg ? arg*(HZ/10) : HZ/4);
+ return 0;
+ case TIOCGSOFTCAR:
+ error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
+ if (error)
+ return error;
+ put_user(C_CLOCAL(tty) ? 1 : 0,
+ (unsigned long *) arg);
+ return 0;
+ case TIOCSSOFTCAR:
+ Get_user(arg, (unsigned long *) arg);
+ tty->termios->c_cflag =
+ ((tty->termios->c_cflag & ~CLOCAL) |
+ (arg ? CLOCAL : 0));
+ return 0;
+ case TIOCMGET:
+ error = verify_area(VERIFY_WRITE, (void *) arg,
+ sizeof(unsigned int));
+ if (error)
+ return error;
+ return sx_get_modem_info(port, (unsigned int *) arg);
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ return sx_set_modem_info(port, cmd, (unsigned int *) arg);
+ case TIOCGSERIAL:
+ return sx_get_serial_info(port, (struct serial_struct *) arg);
+ case TIOCSSERIAL:
+ return sx_set_serial_info(port, (struct serial_struct *) arg);
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+
+static void sx_throttle(struct tty_struct * tty)
+{
+ struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+ struct specialix_board *bp;
+ unsigned long flags;
+
+ if (sx_paranoia_check(port, tty->device, "sx_throttle"))
+ return;
+
+ bp = port_Board(port);
+
+ save_flags(flags); cli();
+
+ /* Use DTR instead of RTS ! */
+ if (SX_CRTSCTS (tty))
+ port->MSVR &= ~MSVR_DTR;
+ else {
+ /* Auch!!! I think the system shouldn't call this then. */
+ /* Or maybe we're supposed (allowed?) to do our side of hw
+ handshake anyway, even when hardware handshake is off.
+ When you see this in your logs, please report.... */
+ printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n",
+ port_No (port));
+ }
+ sx_out(bp, CD186x_CAR, port_No(port));
+ if (I_IXOFF(tty)) {
+ sx_wait_CCR(bp);
+ sx_out(bp, CD186x_CCR, CCR_SSCH2);
+ sx_wait_CCR(bp);
+ }
+ sx_out(bp, CD186x_MSVR, port->MSVR);
+ restore_flags(flags);
+}
+
+
+static void sx_unthrottle(struct tty_struct * tty)
+{
+ struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+ struct specialix_board *bp;
+ unsigned long flags;
+
+ if (sx_paranoia_check(port, tty->device, "sx_unthrottle"))
+ return;
+
+ bp = port_Board(port);
+
+ save_flags(flags); cli();
+ /* XXXX Use DTR INSTEAD???? */
+ if (SX_CRTSCTS(tty)) {
+ port->MSVR |= MSVR_DTR;
+ } /* Else clause: see remark in "sx_throttle"... */
+
+ sx_out(bp, CD186x_CAR, port_No(port));
+ if (I_IXOFF(tty)) {
+ sx_wait_CCR(bp);
+ sx_out(bp, CD186x_CCR, CCR_SSCH1);
+ sx_wait_CCR(bp);
+ }
+ sx_out(bp, CD186x_MSVR, port->MSVR);
+ restore_flags(flags);
+}
+
+
+static void sx_stop(struct tty_struct * tty)
+{
+ struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+ struct specialix_board *bp;
+ unsigned long flags;
+
+ if (sx_paranoia_check(port, tty->device, "sx_stop"))
+ return;
+
+ bp = port_Board(port);
+
+ save_flags(flags); cli();
+ port->IER &= ~IER_TXRDY;
+ sx_out(bp, CD186x_CAR, port_No(port));
+ sx_out(bp, CD186x_IER, port->IER);
+ restore_flags(flags);
+}
+
+
+static void sx_start(struct tty_struct * tty)
+{
+ struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+ struct specialix_board *bp;
+ unsigned long flags;
+
+ if (sx_paranoia_check(port, tty->device, "sx_start"))
+ return;
+
+ bp = port_Board(port);
+
+ save_flags(flags); cli();
+ if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
+ port->IER |= IER_TXRDY;
+ sx_out(bp, CD186x_CAR, port_No(port));
+ sx_out(bp, CD186x_IER, port->IER);
+ }
+ restore_flags(flags);
+}
+
+
+/*
+ * This routine is called from the scheduler tqueue when the interrupt
+ * routine has signalled that a hangup has occurred. The path of
+ * hangup processing is:
+ *
+ * serial interrupt routine -> (scheduler tqueue) ->
+ * do_sx_hangup() -> tty->hangup() -> sx_hangup()
+ *
+ */
+static void do_sx_hangup(void *private_)
+{
+ struct specialix_port *port = (struct specialix_port *) private_;
+ struct tty_struct *tty;
+
+ tty = port->tty;
+ if (!tty)
+ return;
+
+ tty_hangup(tty);
+}
+
+
+static void sx_hangup(struct tty_struct * tty)
+{
+ struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+ struct specialix_board *bp;
+
+ if (sx_paranoia_check(port, tty->device, "sx_hangup"))
+ return;
+
+ bp = port_Board(port);
+
+ sx_shutdown_port(bp, port);
+ port->event = 0;
+ port->count = 0;
+ port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+ port->tty = 0;
+ wake_up_interruptible(&port->open_wait);
+}
+
+
+static void sx_set_termios(struct tty_struct * tty, struct termios * old_termios)
+{
+ struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+ unsigned long flags;
+
+ if (sx_paranoia_check(port, tty->device, "sx_set_termios"))
+ return;
+
+ if (tty->termios->c_cflag == old_termios->c_cflag &&
+ tty->termios->c_iflag == old_termios->c_iflag)
+ return;
+
+ save_flags(flags); cli();
+ sx_change_speed(port_Board(port), port);
+ restore_flags(flags);
+
+ if ((old_termios->c_cflag & CRTSCTS) &&
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ tty->hw_stopped = 0;
+ sx_start(tty);
+ }
+}
+
+
+static void do_specialix_bh(void)
+{
+ run_task_queue(&tq_specialix);
+}
+
+
+static void do_softint(void *private_)
+{
+ struct specialix_port *port = (struct specialix_port *) private_;
+ struct tty_struct *tty;
+
+ if(!(tty = port->tty))
+ return;
+
+ if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+ tty->ldisc.write_wakeup)
+ (tty->ldisc.write_wakeup)(tty);
+ wake_up_interruptible(&tty->write_wait);
+ }
+}
+
+
+static int sx_init_drivers(void)
+{
+ int error;
+ int i;
+
+
+ if (!(tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL))) {
+ printk(KERN_ERR "sx: Couldn't get free page.\n");
+ return 1;
+ }
+ init_bh(SPECIALIX_BH, do_specialix_bh);
+ memset(IRQ_to_board, 0, sizeof(IRQ_to_board));
+ memset(&specialix_driver, 0, sizeof(specialix_driver));
+ specialix_driver.magic = TTY_DRIVER_MAGIC;
+ specialix_driver.name = "ttyW";
+ specialix_driver.major = SPECIALIX_NORMAL_MAJOR;
+ specialix_driver.num = SX_NBOARD * SX_NPORT;
+ specialix_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ specialix_driver.subtype = SPECIALIX_TYPE_NORMAL;
+ specialix_driver.init_termios = tty_std_termios;
+ specialix_driver.init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ specialix_driver.flags = TTY_DRIVER_REAL_RAW;
+ specialix_driver.refcount = &specialix_refcount;
+ specialix_driver.table = specialix_table;
+ specialix_driver.termios = specialix_termios;
+ specialix_driver.termios_locked = specialix_termios_locked;
+
+ specialix_driver.open = sx_open;
+ specialix_driver.close = sx_close;
+ specialix_driver.write = sx_write;
+ specialix_driver.put_char = sx_put_char;
+ specialix_driver.flush_chars = sx_flush_chars;
+ specialix_driver.write_room = sx_write_room;
+ specialix_driver.chars_in_buffer = sx_chars_in_buffer;
+ specialix_driver.flush_buffer = sx_flush_buffer;
+ specialix_driver.ioctl = sx_ioctl;
+ specialix_driver.throttle = sx_throttle;
+ specialix_driver.unthrottle = sx_unthrottle;
+ specialix_driver.set_termios = sx_set_termios;
+ specialix_driver.stop = sx_stop;
+ specialix_driver.start = sx_start;
+ specialix_driver.hangup = sx_hangup;
+
+ specialix_callout_driver = specialix_driver;
+ specialix_callout_driver.name = "cuw";
+ specialix_callout_driver.major = SPECIALIX_CALLOUT_MAJOR;
+ specialix_callout_driver.subtype = SPECIALIX_TYPE_CALLOUT;
+
+ if ((error = tty_register_driver(&specialix_driver))) {
+ free_page((unsigned long)tmp_buf);
+ printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n",
+ error);
+ return 1;
+ }
+ if ((error = tty_register_driver(&specialix_callout_driver))) {
+ free_page((unsigned long)tmp_buf);
+ tty_unregister_driver(&specialix_driver);
+ printk(KERN_ERR "sx: Couldn't register specialix IO8+ callout driver, error = %d\n",
+ error);
+ return 1;
+ }
+
+ memset(sx_port, 0, sizeof(sx_port));
+ for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
+ sx_port[i].callout_termios = specialix_callout_driver.init_termios;
+ sx_port[i].normal_termios = specialix_driver.init_termios;
+ sx_port[i].magic = SPECIALIX_MAGIC;
+ sx_port[i].tqueue.routine = do_softint;
+ sx_port[i].tqueue.data = &sx_port[i];
+ sx_port[i].tqueue_hangup.routine = do_sx_hangup;
+ sx_port[i].tqueue_hangup.data = &sx_port[i];
+ sx_port[i].close_delay = 50 * HZ/100;
+ sx_port[i].closing_wait = 3000 * HZ/100;
+ }
+
+ return 0;
+}
+
+
+static void sx_release_drivers(void)
+{
+ free_page((unsigned long)tmp_buf);
+ tty_unregister_driver(&specialix_driver);
+ tty_unregister_driver(&specialix_callout_driver);
+}
+
+
+#ifndef MODULE
+/*
+ * Called at boot time.
+ *
+ * You can specify IO base for up to SX_NBOARD cards,
+ * using line "specialix=0xiobase1,0xiobase2,.." at LILO prompt.
+ * Note that there will be no probing at default
+ * addresses in this case.
+ *
+ */
+void specialix_setup(char *str, int * ints)
+{
+ int i;
+
+ for (i=0;i<SX_NBOARD;i++) {
+ sx_board[i].base = 0;
+ }
+
+ for (i = 1; i <= ints[0]; i++) {
+ if (i&1)
+ sx_board[i/2].base = ints[i];
+ else
+ sx_board[i/2 -1].irq = ints[i];
+ }
+}
+#endif
+
+/*
+ * This routine must be called by kernel at boot time
+ */
+int specialix_init(void)
+{
+ int i;
+ int found = 0;
+
+ printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997.\n");
+ printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
+#ifdef CONFIG_SPECIALIX_RTSCTS
+ printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
+#else
+ printk (KERN_INFO "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
+#endif
+
+ if (sx_init_drivers())
+ return -EIO;
+
+ for (i = 0; i < SX_NBOARD; i++)
+ if (sx_board[i].base && !sx_probe(&sx_board[i]))
+ found++;
+
+ if (!found) {
+ sx_release_drivers();
+ printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+#ifdef MODULE
+int iobase[SX_NBOARD] = {0,};
+
+int irq [SX_NBOARD] = {0,};
+
+/*
+ * You can setup up to 4 boards.
+ * by specifying "iobase=0xXXX iobase1=0xXXX ..." as insmod parameter.
+ *
+ * More than 4 boards in one computer is not possible, as the card can
+ * only use 4 different interrupts.
+ *
+ */
+int init_module(void)
+{
+ int i;
+
+ if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
+ for(i = 0; i < SX_NBOARD; i++) {
+ sx_board[i].base = iobase[i];
+ sx_board[i].irq = irq[i];
+ }
+ }
+
+ return specialix_init();
+}
+
+
+void cleanup_module(void)
+{
+ int i;
+
+ sx_release_drivers();
+ for (i = 0; i < SX_NBOARD; i++)
+ if (sx_board[i].flags & SX_BOARD_PRESENT)
+ sx_release_io_range(&sx_board[i]);
+#ifdef SPECIALIX_TIMER
+ del_timer (&missed_irq_timer);
+#endif
+
+}
+#endif /* MODULE */
diff --git a/drivers/char/specialix_io8.h b/drivers/char/specialix_io8.h
new file mode 100644
index 000000000..f35c959e4
--- /dev/null
+++ b/drivers/char/specialix_io8.h
@@ -0,0 +1,146 @@
+/*
+ * linux/drivers/char/specialix_io8.h --
+ * Specialix IO8+ multiport serial driver.
+ *
+ * Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
+ * Copyright (C) 1994-1996 Dmitry Gorodchanin (begemot@bgm.rosprint.net)
+ *
+ *
+ * Specialix pays for the development and support of this driver.
+ * Please DO contact io8-linux@specialix.co.uk if you require
+ * support.
+ *
+ * This driver was developped in the BitWizard linux device
+ * driver service. If you require a linux device driver for your
+ * product, please contact devices@BitWizard.nl for a quote.
+ *
+ * This code is firmly based on the riscom/8 serial driver,
+ * written by Dmitry Gorodchanin. The specialix IO8+ card
+ * programming information was obtained from the CL-CD1865 Data
+ * Book, and Specialix document number 6200059: IO8+ Hardware
+ * Functional Specification.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be
+ * useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+ * USA.
+ * */
+
+#ifndef __LINUX_SPECIALIX_H
+#define __LINUX_SPECIALIX_H
+
+#include <linux/serial.h>
+
+#ifdef __KERNEL__
+
+#define SX_NBOARD 4
+/* NOTE: Specialix decoder recognizes 4 addresses, but only two are used.... */
+#define SX_IO_SPACE 4
+/* eight ports per board. */
+#define SX_NPORT 8
+#define SX_BOARD(line) ((line) / SX_NPORT)
+#define SX_PORT(line) ((line) & (SX_NPORT - 1))
+
+
+#define SX_DATA_REG 0 /* Base+0 : Data register */
+#define SX_ADDR_REG 1 /* base+1 : Address register. */
+
+#define MHz *1000000 /* I'm ashamed of myself. */
+
+/* On-board oscillator frequency */
+#define SX_OSCFREQ (25 MHz/2)
+/* There is a 25MHz crystal on the board, but the chip is in /2 mode */
+
+
+/* Ticks per sec. Used for setting receiver timeout and break length */
+#define SPECIALIX_TPS 4000
+
+/* Yeah, after heavy testing I decided it must be 6.
+ * Sure, You can change it if needed.
+ */
+#define SPECIALIX_RXFIFO 6 /* Max. receiver FIFO size (1-8) */
+
+#define SPECIALIX_MAGIC 0x0907
+
+#define SX_CCR_TIMEOUT 10000 /* CCR timeout. You may need to wait upto
+ 10 milliseconds before the internal
+ processor is available again after
+ you give it a command */
+
+#define SX_IOBASE1 0x100
+#define SX_IOBASE2 0x180
+#define SX_IOBASE3 0x250
+#define SX_IOBASE4 0x260
+
+struct specialix_board {
+ unsigned long flags;
+ unsigned short base;
+ unsigned char irq;
+ signed char count;
+ unsigned char DTR;
+ int reg;
+};
+
+#define SX_BOARD_PRESENT 0x00000001
+#define SX_BOARD_ACTIVE 0x00000002
+
+
+struct specialix_port {
+ int magic;
+ int baud_base;
+ int flags;
+ struct tty_struct * tty;
+ int count;
+ int blocked_open;
+ int event;
+ int timeout;
+ int close_delay;
+ long session;
+ long pgrp;
+ unsigned char * xmit_buf;
+ int custom_divisor;
+ int xmit_head;
+ int xmit_tail;
+ int xmit_cnt;
+ struct termios normal_termios;
+ struct termios callout_termios;
+ struct wait_queue *open_wait;
+ struct wait_queue *close_wait;
+ struct tq_struct tqueue;
+ struct tq_struct tqueue_hangup;
+ short wakeup_chars;
+ short break_length;
+ unsigned short closing_wait;
+ unsigned char mark_mask;
+ unsigned char IER;
+ unsigned char MSVR;
+ unsigned char COR2;
+#ifdef SX_REPORT_OVERRUN
+ unsigned long overrun;
+#endif
+#ifdef SX_REPORT_FIFO
+ unsigned long hits[10];
+#endif
+};
+
+#endif /* __KERNEL__ */
+#endif /* __LINUX_SPECIALIX_H */
+
+
+
+
+
+
+
+
+
diff --git a/drivers/misc/parport_ax.c b/drivers/misc/parport_ax.c
new file mode 100644
index 000000000..caaccd6e7
--- /dev/null
+++ b/drivers/misc/parport_ax.c
@@ -0,0 +1,554 @@
+/* $Id: parport_ax.c,v 1.2 1997/10/25 17:27:03 philip Exp $
+ * Parallel-port routines for Sun Ultra/AX architecture
+ *
+ * Author: Eddie C. Dost <ecd@skynet.be>
+ *
+ * based on work by:
+ * Phil Blundell <Philip.Blundell@pobox.com>
+ * Tim Waugh <tim@cyberelk.demon.co.uk>
+ * Jose Renau <renau@acm.org>
+ * David Campbell <campbell@tirian.che.curtin.edu.au>
+ * Grant Guenther <grant@torque.net>
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+
+#include <linux/parport.h>
+
+#include <asm/ptrace.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <asm/ebus.h>
+#include <asm/ns87303.h>
+
+
+/*
+ * Define this if you have Devices which don't support short
+ * host read/write cycles.
+ */
+#undef HAVE_SLOW_DEVICES
+
+
+#define DATA 0x00
+#define STATUS 0x01
+#define CONTROL 0x02
+
+#define CFIFO 0x400
+#define DFIFO 0x400
+#define TFIFO 0x400
+#define CNFA 0x400
+#define CNFB 0x401
+#define ECR 0x402
+
+static void
+ax_null_intr_func(int irq, void *dev_id, struct pt_regs *regs)
+{
+ /* NULL function - Does nothing */
+ return;
+}
+
+#if 0
+static unsigned int
+ax_read_configb(struct parport *p)
+{
+ return (unsigned int)inb(p->base + CNFB);
+}
+#endif
+
+static void
+ax_write_data(struct parport *p, unsigned int d)
+{
+ outb(d, p->base + DATA);
+}
+
+static unsigned int
+ax_read_data(struct parport *p)
+{
+ return (unsigned int)inb(p->base + DATA);
+}
+
+static void
+ax_write_control(struct parport *p, unsigned int d)
+{
+ outb(d, p->base + CONTROL);
+}
+
+static unsigned int
+ax_read_control(struct parport *p)
+{
+ return (unsigned int)inb(p->base + CONTROL);
+}
+
+static unsigned int
+ax_frob_control(struct parport *p, unsigned int mask, unsigned int val)
+{
+ unsigned int old = (unsigned int)inb(p->base + CONTROL);
+ outb(((old & ~mask) ^ val), p->base + CONTROL);
+ return old;
+}
+
+static void
+ax_write_status(struct parport *p, unsigned int d)
+{
+ outb(d, p->base + STATUS);
+}
+
+static unsigned int
+ax_read_status(struct parport *p)
+{
+ return (unsigned int)inb(p->base + STATUS);
+}
+
+static void
+ax_write_econtrol(struct parport *p, unsigned int d)
+{
+ outb(d, p->base + ECR);
+}
+
+static unsigned int
+ax_read_econtrol(struct parport *p)
+{
+ return (unsigned int)inb(p->base + ECR);
+}
+
+static unsigned int
+ax_frob_econtrol(struct parport *p, unsigned int mask, unsigned int val)
+{
+ unsigned int old = (unsigned int)inb(p->base + ECR);
+ outb(((old & ~mask) ^ val), p->base + ECR);
+ return old;
+}
+
+static void
+ax_change_mode(struct parport *p, int m)
+{
+ ax_frob_econtrol(p, 0xe0, m << 5);
+}
+
+static void
+ax_write_fifo(struct parport *p, unsigned int v)
+{
+ outb(v, p->base + DFIFO);
+}
+
+static unsigned int
+ax_read_fifo(struct parport *p)
+{
+ return inb(p->base + DFIFO);
+}
+
+static void
+ax_disable_irq(struct parport *p)
+{
+ struct linux_ebus_dma *dma = p->private_data;
+ unsigned int dcsr;
+
+ dcsr = readl((unsigned long)&dma->dcsr);
+ dcsr &= ~(EBUS_DCSR_INT_EN);
+ writel(dcsr, (unsigned long)&dma->dcsr);
+}
+
+static void
+ax_enable_irq(struct parport *p)
+{
+ struct linux_ebus_dma *dma = p->private_data;
+ unsigned int dcsr;
+
+ dcsr = readl((unsigned long)&dma->dcsr);
+ dcsr |= EBUS_DCSR_INT_EN;
+ writel(dcsr, (unsigned long)&dma->dcsr);
+}
+
+static void
+ax_release_resources(struct parport *p)
+{
+ if (p->irq != PARPORT_IRQ_NONE) {
+ ax_disable_irq(p);
+ free_irq(p->irq, NULL);
+ }
+ release_region(p->base, p->size);
+ if (p->modes & PARPORT_MODE_PCECR)
+ release_region(p->base+0x400, 3);
+ release_region((unsigned long)p->private_data,
+ sizeof(struct linux_ebus_dma));
+}
+
+static int
+ax_claim_resources(struct parport *p)
+{
+ /* FIXME check that resources are free */
+ if (p->irq != PARPORT_IRQ_NONE) {
+ request_irq(p->irq, ax_null_intr_func, 0, p->name, NULL);
+ ax_enable_irq(p);
+ }
+ request_region(p->base, p->size, p->name);
+ if (p->modes & PARPORT_MODE_PCECR)
+ request_region(p->base+0x400, 3, p->name);
+ request_region((unsigned long)p->private_data,
+ sizeof(struct linux_ebus_dma), p->name);
+ return 0;
+}
+
+static void
+ax_save_state(struct parport *p, struct parport_state *s)
+{
+ s->u.pc.ctr = ax_read_control(p);
+ s->u.pc.ecr = ax_read_econtrol(p);
+}
+
+static void
+ax_restore_state(struct parport *p, struct parport_state *s)
+{
+ ax_write_control(p, s->u.pc.ctr);
+ ax_write_econtrol(p, s->u.pc.ecr);
+}
+
+static unsigned int
+ax_epp_read_block(struct parport *p, void *buf, unsigned int length)
+{
+ return 0; /* FIXME */
+}
+
+static unsigned int
+ax_epp_write_block(struct parport *p, void *buf, unsigned int length)
+{
+ return 0; /* FIXME */
+}
+
+static unsigned int
+ax_ecp_read_block(struct parport *p, void *buf, unsigned int length,
+ void (*fn)(struct parport *, void *, unsigned int),
+ void *handle)
+{
+ return 0; /* FIXME */
+}
+
+static unsigned int
+ax_ecp_write_block(struct parport *p, void *buf, unsigned int length,
+ void (*fn)(struct parport *, void *, unsigned int),
+ void *handle)
+{
+ return 0; /* FIXME */
+}
+
+static int
+ax_examine_irq(struct parport *p)
+{
+ return 0; /* FIXME */
+}
+
+static void
+ax_inc_use_count(void)
+{
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+}
+
+static void
+ax_dec_use_count(void)
+{
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+}
+
+static struct parport_operations ax_ops =
+{
+ ax_write_data,
+ ax_read_data,
+
+ ax_write_control,
+ ax_read_control,
+ ax_frob_control,
+
+ ax_write_econtrol,
+ ax_read_econtrol,
+ ax_frob_econtrol,
+
+ ax_write_status,
+ ax_read_status,
+
+ ax_write_fifo,
+ ax_read_fifo,
+
+ ax_change_mode,
+
+ ax_release_resources,
+ ax_claim_resources,
+
+ ax_epp_write_block,
+ ax_epp_read_block,
+
+ ax_ecp_write_block,
+ ax_ecp_read_block,
+
+ ax_save_state,
+ ax_restore_state,
+
+ ax_enable_irq,
+ ax_disable_irq,
+ ax_examine_irq,
+
+ ax_inc_use_count,
+ ax_dec_use_count
+};
+
+
+/******************************************************
+ * MODE detection section:
+ */
+
+/* Check for ECP
+ *
+ * Old style XT ports alias io ports every 0x400, hence accessing ECR
+ * on these cards actually accesses the CTR.
+ *
+ * Modern cards don't do this but reading from ECR will return 0xff
+ * regardless of what is written here if the card does NOT support
+ * ECP.
+ *
+ * We will write 0x2c to ECR and 0xcc to CTR since both of these
+ * values are "safe" on the CTR since bits 6-7 of CTR are unused.
+ */
+static int parport_ECR_present(struct parport *pb)
+{
+ unsigned int r, octr = pb->ops->read_control(pb),
+ oecr = pb->ops->read_econtrol(pb);
+
+ r = pb->ops->read_control(pb);
+ if ((pb->ops->read_econtrol(pb) & 0x3) == (r & 0x3)) {
+ pb->ops->write_control(pb, r ^ 0x2 ); /* Toggle bit 1 */
+
+ r = pb->ops->read_control(pb);
+ if ((pb->ops->read_econtrol(pb) & 0x2) == (r & 0x2)) {
+ pb->ops->write_control(pb, octr);
+ return 0; /* Sure that no ECR register exists */
+ }
+ }
+
+ if ((pb->ops->read_econtrol(pb) & 0x3 ) != 0x1)
+ return 0;
+
+ pb->ops->write_econtrol(pb, 0x34);
+ if (pb->ops->read_econtrol(pb) != 0x35)
+ return 0;
+
+ pb->ops->write_econtrol(pb, oecr);
+ pb->ops->write_control(pb, octr);
+
+ return PARPORT_MODE_PCECR;
+}
+
+static int parport_ECP_supported(struct parport *pb)
+{
+ int i, oecr = pb->ops->read_econtrol(pb);
+
+ /* If there is no ECR, we have no hope of supporting ECP. */
+ if (!(pb->modes & PARPORT_MODE_PCECR))
+ return 0;
+
+ /*
+ * Using LGS chipset it uses ECR register, but
+ * it doesn't support ECP or FIFO MODE
+ */
+
+ pb->ops->write_econtrol(pb, 0xc0); /* TEST FIFO */
+ for (i=0; i < 1024 && (pb->ops->read_econtrol(pb) & 0x01); i++)
+ pb->ops->write_fifo(pb, 0xaa);
+
+ pb->ops->write_econtrol(pb, oecr);
+ return (i == 1024) ? 0 : PARPORT_MODE_PCECP;
+}
+
+/* Detect PS/2 support.
+ *
+ * Bit 5 (0x20) sets the PS/2 data direction; setting this high
+ * allows us to read data from the data lines. In theory we would get back
+ * 0xff but any peripheral attached to the port may drag some or all of the
+ * lines down to zero. So if we get back anything that isn't the contents
+ * of the data register we deem PS/2 support to be present.
+ *
+ * Some SPP ports have "half PS/2" ability - you can't turn off the line
+ * drivers, but an external peripheral with sufficiently beefy drivers of
+ * its own can overpower them and assert its own levels onto the bus, from
+ * where they can then be read back as normal. Ports with this property
+ * and the right type of device attached are likely to fail the SPP test,
+ * (as they will appear to have stuck bits) and so the fact that they might
+ * be misdetected here is rather academic.
+ */
+
+static int parport_PS2_supported(struct parport *pb)
+{
+ int ok = 0, octr = pb->ops->read_control(pb);
+
+ pb->ops->write_control(pb, octr | 0x20); /* try to tri-state buffer */
+
+ pb->ops->write_data(pb, 0x55);
+ if (pb->ops->read_data(pb) != 0x55) ok++;
+
+ pb->ops->write_data(pb, 0xaa);
+ if (pb->ops->read_data(pb) != 0xaa) ok++;
+
+ pb->ops->write_control(pb, octr); /* cancel input mode */
+
+ return ok ? PARPORT_MODE_PCPS2 : 0;
+}
+
+static int parport_ECPPS2_supported(struct parport *pb)
+{
+ int mode, oecr = pb->ops->read_econtrol(pb);
+
+ if (!(pb->modes & PARPORT_MODE_PCECR))
+ return 0;
+
+ pb->ops->write_econtrol(pb, 0x20);
+
+ mode = parport_PS2_supported(pb);
+
+ pb->ops->write_econtrol(pb, oecr);
+ return mode ? PARPORT_MODE_PCECPPS2 : 0;
+}
+
+#define printmode(x) \
+{ \
+ if (p->modes & PARPORT_MODE_PC##x) { \
+ printk("%s%s", f ? "," : "", #x); \
+ f++; \
+ } \
+}
+
+int
+init_one_port(struct linux_ebus_device *dev)
+{
+ struct parport tmpport, *p;
+ unsigned long base;
+ unsigned long config;
+ unsigned char tmp;
+ int irq, dma;
+
+ /* Pointer to NS87303 Configuration Registers */
+ config = dev->base_address[1];
+
+ /* Setup temporary access to Device operations */
+ tmpport.base = dev->base_address[0];
+ tmpport.ops = &ax_ops;
+
+ /* Enable ECP mode, set bit 2 of the CTR first */
+ tmpport.ops->write_control(&tmpport, 0x04);
+ tmp = ns87303_readb(config, PCR);
+ tmp |= (PCR_EPP_IEEE | PCR_ECP_ENABLE | PCR_ECP_CLK_ENA);
+ ns87303_writeb(config, PCR, tmp);
+
+ /* LPT CTR bit 5 controls direction of parallel port */
+ tmp = ns87303_readb(config, PTR);
+ tmp |= PTR_LPT_REG_DIR;
+ ns87303_writeb(config, PTR, tmp);
+
+ /* Configure IRQ to Push Pull, Level Low */
+ tmp = ns87303_readb(config, PCR);
+ tmp &= ~(PCR_IRQ_ODRAIN);
+ tmp |= PCR_IRQ_POLAR;
+ ns87303_writeb(config, PCR, tmp);
+
+#ifndef HAVE_SLOW_DEVICES
+ /* Enable Zero Wait State for ECP */
+ tmp = ns87303_readb(config, FCR);
+ tmp |= FCR_ZWS_ENA;
+ ns87303_writeb(config, FCR, tmp);
+#endif
+
+ /*
+ * Now continue initializing the port
+ */
+ base = dev->base_address[0];
+ irq = dev->irqs[0];
+ dma = PARPORT_DMA_AUTO;
+
+ if (!(p = parport_register_port(base, irq, dma, &ax_ops)))
+ return 0;
+
+ /* Safe away pointer to our EBus DMA */
+ p->private_data = (void *)dev->base_address[2];
+
+ p->modes = PARPORT_MODE_PCSPP | parport_PS2_supported(p);
+ if (!check_region(p->base + 0x400, 3)) {
+ p->modes |= parport_ECR_present(p);
+ p->modes |= parport_ECP_supported(p);
+ p->modes |= parport_ECPPS2_supported(p);
+ }
+ p->size = 3;
+
+ if (p->dma == PARPORT_DMA_AUTO)
+ p->dma = (p->modes & PARPORT_MODE_PCECP) ? 0 : PARPORT_DMA_NONE;
+
+ printk(KERN_INFO "%s: PC-style at 0x%lx", p->name, p->base);
+ if (p->irq != PARPORT_IRQ_NONE)
+ printk(", irq %x", (unsigned int)p->irq);
+ if (p->dma != PARPORT_DMA_NONE)
+ printk(", dma %d", p->dma);
+ printk(" [");
+ {
+ int f = 0;
+ printmode(SPP);
+ printmode(PS2);
+ printmode(ECP);
+ printmode(ECPPS2);
+ }
+ printk("]\n");
+ parport_proc_register(p);
+ p->flags |= PARPORT_FLAG_COMA;
+
+ ax_write_control(p, 0x0c);
+ ax_write_data(p, 0);
+
+ if (parport_probe_hook)
+ (*parport_probe_hook)(p);
+
+ return 1;
+}
+
+int
+parport_ax_init(void)
+{
+ struct linux_ebus *ebus;
+ struct linux_ebus_device *edev;
+ int count = 0;
+
+ for_all_ebusdev(edev, ebus)
+ if (!strcmp(edev->prom_name, "ecpp"))
+ count += init_one_port(edev);
+ return count;
+}
+
+#ifdef MODULE
+
+int
+init_module(void)
+{
+ return (parport_ax_init() ? 0 : 1);
+}
+
+void
+cleanup_module(void)
+{
+ struct parport *p = parport_enumerate(), *tmp;
+ while (p) {
+ tmp = p->next;
+ if (p->modes & PARPORT_MODE_PCSPP) {
+ if (!(p->flags & PARPORT_FLAG_COMA))
+ parport_quiesce(p);
+ parport_proc_unregister(p);
+ parport_unregister_port(p);
+ }
+ p = tmp;
+ }
+}
+#endif
diff --git a/drivers/net/baycom.c b/drivers/net/baycom.c
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/baycom.c
+++ /dev/null
diff --git a/drivers/net/bpqether.c b/drivers/net/bpqether.c
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/bpqether.c
+++ /dev/null
diff --git a/drivers/net/dmascc.c b/drivers/net/dmascc.c
new file mode 100644
index 000000000..33b473d32
--- /dev/null
+++ b/drivers/net/dmascc.c
@@ -0,0 +1,1260 @@
+/*
+ * $Id: dmascc.c,v 1.2 1997/12/02 16:49:49 oe1kib Exp $
+ *
+ * Driver for high-speed SCC boards (those with DMA support)
+ * Copyright (C) 1997 Klaus Kudielka
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/module.h>
+#include <linux/dmascc.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/in.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/netdevice.h>
+#include <linux/sockios.h>
+#include <linux/tqueue.h>
+#include <linux/version.h>
+#include <asm/atomic.h>
+#include <asm/bitops.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <net/ax25.h>
+#include <stdio.h>
+#include "z8530.h"
+
+
+/* Number of buffers per channel */
+
+#define NUM_TX_BUF 2 /* NUM_TX_BUF >= 1 (2 recommended) */
+#define NUM_RX_BUF 2 /* NUM_RX_BUF >= 1 (2 recommended) */
+#define BUF_SIZE 2016
+
+
+/* Cards supported */
+
+#define HW_PI { "Ottawa PI", 0x300, 0x20, 0x10, 8, \
+ 0, 8, 1843200, 3686400 }
+#define HW_PI2 { "Ottawa PI2", 0x300, 0x20, 0x10, 8, \
+ 0, 8, 3686400, 7372800 }
+#define HW_TWIN { "Gracilis PackeTwin", 0x200, 0x10, 0x10, 32, \
+ 0, 4, 6144000, 6144000 }
+
+#define HARDWARE { HW_PI, HW_PI2, HW_TWIN }
+
+#define TYPE_PI 0
+#define TYPE_PI2 1
+#define TYPE_TWIN 2
+#define NUM_TYPES 3
+
+#define MAX_NUM_DEVS 32
+
+
+/* SCC chips supported */
+
+#define Z8530 0
+#define Z85C30 1
+#define Z85230 2
+
+#define CHIPNAMES { "Z8530", "Z85C30", "Z85230" }
+
+
+/* I/O registers */
+
+/* 8530 registers relative to card base */
+#define SCCB_CMD 0x00
+#define SCCB_DATA 0x01
+#define SCCA_CMD 0x02
+#define SCCA_DATA 0x03
+
+/* 8254 registers relative to card base */
+#define TMR_CNT0 0x00
+#define TMR_CNT1 0x01
+#define TMR_CNT2 0x02
+#define TMR_CTRL 0x03
+
+/* Additional PI/PI2 registers relative to card base */
+#define PI_DREQ_MASK 0x04
+
+/* Additional PackeTwin registers relative to card base */
+#define TWIN_INT_REG 0x08
+#define TWIN_CLR_TMR1 0x09
+#define TWIN_CLR_TMR2 0x0a
+#define TWIN_SPARE_1 0x0b
+#define TWIN_DMA_CFG 0x08
+#define TWIN_SERIAL_CFG 0x09
+#define TWIN_DMA_CLR_FF 0x0a
+#define TWIN_SPARE_2 0x0b
+
+
+/* PackeTwin I/O register values */
+
+/* INT_REG */
+#define TWIN_SCC_MSK 0x01
+#define TWIN_TMR1_MSK 0x02
+#define TWIN_TMR2_MSK 0x04
+#define TWIN_INT_MSK 0x07
+
+/* SERIAL_CFG */
+#define TWIN_DTRA_ON 0x01
+#define TWIN_DTRB_ON 0x02
+#define TWIN_EXTCLKA 0x04
+#define TWIN_EXTCLKB 0x08
+#define TWIN_LOOPA_ON 0x10
+#define TWIN_LOOPB_ON 0x20
+#define TWIN_EI 0x80
+
+/* DMA_CFG */
+#define TWIN_DMA_HDX_T1 0x08
+#define TWIN_DMA_HDX_R1 0x0a
+#define TWIN_DMA_HDX_T3 0x14
+#define TWIN_DMA_HDX_R3 0x16
+#define TWIN_DMA_FDX_T3R1 0x1b
+#define TWIN_DMA_FDX_T1R3 0x1d
+
+
+/* Status values */
+
+/* tx_state */
+#define TX_IDLE 0
+#define TX_OFF 1
+#define TX_TXDELAY 2
+#define TX_ACTIVE 3
+#define TX_SQDELAY 4
+
+
+/* Data types */
+
+struct scc_hardware {
+ char *name;
+ int io_region;
+ int io_delta;
+ int io_size;
+ int num_devs;
+ int scc_offset;
+ int tmr_offset;
+ int tmr_hz;
+ int pclk_hz;
+};
+
+struct scc_priv {
+ char name[10];
+ struct enet_statistics stats;
+ struct scc_info *info;
+ int channel;
+ int cmd, data, tmr;
+ struct scc_param param;
+ char rx_buf[NUM_RX_BUF][BUF_SIZE];
+ int rx_len[NUM_RX_BUF];
+ int rx_ptr;
+ struct tq_struct rx_task;
+ int rx_head, rx_tail, rx_count;
+ int rx_over;
+ char tx_buf[NUM_TX_BUF][BUF_SIZE];
+ int tx_len[NUM_TX_BUF];
+ int tx_ptr;
+ int tx_head, tx_tail, tx_count;
+ int tx_sem, tx_state;
+ unsigned long tx_start;
+ int status;
+};
+
+struct scc_info {
+ int type;
+ int chip;
+ int open;
+ int scc_base;
+ int tmr_base;
+ int twin_serial_cfg;
+ struct device dev[2];
+ struct scc_priv priv[2];
+ struct scc_info *next;
+};
+
+
+/* Function declarations */
+
+int dmascc_init(void) __init;
+static int setup_adapter(int io, int h, int n) __init;
+
+static inline void write_scc(int ctl, int reg, int val);
+static inline int read_scc(int ctl, int reg);
+static int scc_open(struct device *dev);
+static int scc_close(struct device *dev);
+static int scc_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
+static int scc_send_packet(struct sk_buff *skb, struct device *dev);
+static struct enet_statistics *scc_get_stats(struct device *dev);
+static int scc_set_mac_address(struct device *dev, void *sa);
+static void scc_isr(int irq, void *dev_id, struct pt_regs * regs);
+static inline void z8530_isr(struct scc_info *info);
+static void rx_isr(struct device *dev);
+static void special_condition(struct device *dev, int rc);
+static void rx_bh(void *arg);
+static void tx_isr(struct device *dev);
+static void es_isr(struct device *dev);
+static void tm_isr(struct device *dev);
+static inline void delay(struct device *dev, int t);
+static inline unsigned char random(void);
+
+
+/* Initialization variables */
+
+static int io[MAX_NUM_DEVS] __initdata = { 0, };
+/* Beware! hw[] is also used in cleanup_module(). If __initdata also applies
+ to modules, we may not declare hw[] as __initdata */
+static struct scc_hardware hw[NUM_TYPES] __initdata = HARDWARE;
+static char ax25_broadcast[7] __initdata =
+ { 'Q'<<1, 'S'<<1, 'T'<<1, ' '<<1, ' '<<1, ' '<<1, '0'<<1 };
+static char ax25_test[7] __initdata =
+ { 'L'<<1, 'I'<<1, 'N'<<1, 'U'<<1, 'X'<<1, ' '<<1, '1'<<1 };
+
+
+/* Global variables */
+
+static struct scc_info *first = NULL;
+static unsigned long rand;
+
+
+
+/* Module functions */
+
+#ifdef MODULE
+
+
+MODULE_AUTHOR("Klaus Kudielka <oe1kib@oe1xtu.ampr.org>");
+MODULE_DESCRIPTION("Driver for high-speed SCC boards");
+MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NUM_DEVS) "i");
+
+
+int init_module(void)
+{
+ return dmascc_init();
+}
+
+
+void cleanup_module(void)
+{
+ int i;
+ struct scc_info *info;
+
+ while (first) {
+ info = first;
+
+ /* Unregister devices */
+ for (i = 0; i < 2; i++) {
+ if (info->dev[i].name)
+ unregister_netdev(&info->dev[i]);
+ }
+
+ /* Reset board */
+ if (info->type == TYPE_TWIN)
+ outb_p(0, info->dev[0].base_addr + TWIN_SERIAL_CFG);
+ write_scc(info->priv[0].cmd, R9, FHWRES);
+ release_region(info->dev[0].base_addr,
+ hw[info->type].io_size);
+
+ /* Free memory */
+ first = info->next;
+ kfree_s(info, sizeof(struct scc_info));
+ }
+}
+
+
+#else
+
+
+__initfunc(void dmascc_setup(char *str, int *ints))
+{
+ int i;
+
+ for (i = 0; i < MAX_NUM_DEVS && i < ints[0]; i++)
+ io[i] = ints[i+1];
+}
+
+
+#endif
+
+
+/* Initialization functions */
+
+__initfunc(int dmascc_init(void))
+{
+ int h, i, j, n, base[MAX_NUM_DEVS], tcmd, t0, t1, status;
+ unsigned long time, start[MAX_NUM_DEVS], stop[MAX_NUM_DEVS];
+
+ /* Initialize random number generator */
+ rand = jiffies;
+
+ /* Cards found = 0 */
+ n = 0;
+
+ /* Run autodetection for each card type */
+ for (h = 0; h < NUM_TYPES; h++) {
+
+ if (io[0]) {
+ /* User-specified I/O address regions */
+ for (i = 0; i < hw[h].num_devs; i++) base[i] = 0;
+ for (i = 0; i < MAX_NUM_DEVS && io[i]; i++) {
+ j = (io[i] - hw[h].io_region) / hw[h].io_delta;
+ if (j >= 0 &&
+ j < hw[h].num_devs &&
+ hw[h].io_region + j * hw[h].io_delta == io[i])
+ base[j] = io[i];
+ }
+ } else {
+ /* Default I/O address regions */
+ for (i = 0; i < hw[h].num_devs; i++)
+ base[i] = hw[h].io_region + i * hw[h].io_delta;
+ }
+
+ /* Check valid I/O address regions */
+ for (i = 0; i < hw[h].num_devs; i++)
+ if (base[i] && check_region(base[i], hw[h].io_size))
+ base[i] = 0;
+
+ /* Start timers */
+ for (i = 0; i < hw[h].num_devs; i++)
+ if (base[i]) {
+ tcmd = base[i] + hw[h].tmr_offset + TMR_CTRL;
+ t0 = base[i] + hw[h].tmr_offset + TMR_CNT0;
+ t1 = base[i] + hw[h].tmr_offset + TMR_CNT1;
+ /* Timer 0: LSB+MSB, Mode 3, TMR_0_HZ */
+ outb_p(0x36, tcmd);
+ outb_p((hw[h].tmr_hz/TMR_0_HZ) & 0xFF, t0);
+ outb_p((hw[h].tmr_hz/TMR_0_HZ) >> 8, t0);
+ /* Timer 1: LSB+MSB, Mode 0, HZ/10 */
+ outb_p(0x70, tcmd);
+ outb_p((TMR_0_HZ/HZ*10) & 0xFF, t1);
+ outb_p((TMR_0_HZ/HZ*10) >> 8, t1);
+ /* Timer 2: LSB+MSB, Mode 0 */
+ outb_p(0xb0, tcmd);
+ }
+
+ /* Initialize start values in case we miss the null count bit */
+ time = jiffies;
+ for (i = 0; i < hw[h].num_devs; i++) start[i] = time;
+
+ /* Timing loop */
+ while (jiffies - time < 12) {
+ for (i = 0; i < hw[h].num_devs; i++)
+ if (base[i]) {
+ /* Read back Timer 1: Status */
+ outb_p(0xE4, base[i] + hw[h].tmr_offset + TMR_CTRL);
+ status = inb_p(base[i] + hw[h].tmr_offset + TMR_CNT1);
+ if ((status & 0x3F) != 0x30) base[i] = 0;
+ if (status & 0x40) start[i] = jiffies;
+ if (~status & 0x80) stop[i] = jiffies;
+ }
+ }
+
+ /* Evaluate measurements */
+ for (i = 0; i < hw[h].num_devs; i++)
+ if (base[i]) {
+ time = stop[i] - start[i];
+ if (time < 9 || time > 11)
+ /* The time expired doesn't match */
+ base[i] = 0;
+ else {
+ /* Ok, we have found an adapter */
+ if (setup_adapter(base[i], h, n) == 0)
+ n++;
+ }
+ }
+
+ } /* NUM_TYPES */
+
+ /* If any adapter was successfully initialized, return ok */
+ if (n) return 0;
+
+ /* If no adapter found, return error */
+ printk("dmascc: no adapters found\n");
+ return -EIO;
+}
+
+
+__initfunc(int setup_adapter(int io, int h, int n))
+{
+ int i, irq, chip;
+ struct scc_info *info;
+ struct device *dev;
+ struct scc_priv *priv;
+ unsigned long time;
+ unsigned int irqs;
+ int tmr = io + hw[h].tmr_offset;
+ int scc = io + hw[h].scc_offset;
+ int cmd = scc + SCCA_CMD;
+ char *chipnames[] = CHIPNAMES;
+
+ /* Reset 8530 */
+ write_scc(cmd, R9, FHWRES | MIE | NV);
+
+ /* Determine type of chip */
+ write_scc(cmd, R15, 1);
+ if (!read_scc(cmd, R15)) {
+ /* WR7' not present. This is an ordinary Z8530 SCC. */
+ chip = Z8530;
+ } else {
+ /* Put one character in TX FIFO */
+ write_scc(cmd, R8, 0);
+ if (read_scc(cmd, R0) & Tx_BUF_EMP) {
+ /* TX FIFO not full. This is a Z85230 ESCC with a 4-byte FIFO. */
+ chip = Z85230;
+ } else {
+ /* TX FIFO full. This is a Z85C30 SCC with a 1-byte FIFO. */
+ chip = Z85C30;
+ }
+ }
+ write_scc(cmd, R15, 0);
+
+ /* Start IRQ auto-detection */
+ sti();
+ irqs = probe_irq_on();
+
+ /* Enable interrupts */
+ switch (h) {
+ case TYPE_PI:
+ case TYPE_PI2:
+ outb_p(0, io + PI_DREQ_MASK);
+ write_scc(cmd, R15, CTSIE);
+ write_scc(cmd, R0, RES_EXT_INT);
+ write_scc(cmd, R1, EXT_INT_ENAB);
+ break;
+ case TYPE_TWIN:
+ outb_p(0, io + TWIN_DMA_CFG);
+ inb_p(io + TWIN_CLR_TMR1);
+ inb_p(io + TWIN_CLR_TMR2);
+ outb_p(TWIN_EI, io + TWIN_SERIAL_CFG);
+ break;
+ }
+
+ /* Start timer */
+ outb_p(1, tmr + TMR_CNT1);
+ outb_p(0, tmr + TMR_CNT1);
+ /* Wait and detect IRQ */
+ time = jiffies; while (jiffies - time < 2 + HZ / TMR_0_HZ);
+ irq = probe_irq_off(irqs);
+
+ /* Clear pending interrupt, disable interrupts */
+ switch (h) {
+ case TYPE_PI:
+ case TYPE_PI2:
+ write_scc(cmd, R1, 0);
+ write_scc(cmd, R15, 0);
+ write_scc(cmd, R0, RES_EXT_INT);
+ break;
+ case TYPE_TWIN:
+ inb_p(io + TWIN_CLR_TMR1);
+ outb_p(0, io + TWIN_SERIAL_CFG);
+ break;
+ }
+
+ if (irq <= 0) {
+ printk("dmascc: could not find irq of %s at %#3x (irq=%d)\n",
+ hw[h].name, io, irq);
+ return -1;
+ }
+
+ /* Allocate memory */
+ info = kmalloc(sizeof(struct scc_info), GFP_KERNEL | GFP_DMA);
+ if (!info) {
+ printk("dmascc: could not allocate memory for %s at %#3x\n",
+ hw[h].name, io);
+ return -1;
+ }
+
+ /* Set up data structures */
+ memset(info, 0, sizeof(struct scc_info));
+ info->type = h;
+ info->chip = chip;
+ info->scc_base = io + hw[h].scc_offset;
+ info->tmr_base = io + hw[h].tmr_offset;
+ info->twin_serial_cfg = 0;
+ for (i = 0; i < 2; i++) {
+ dev = &info->dev[i];
+ priv = &info->priv[i];
+ sprintf(priv->name, "dmascc%i", 2*n+i);
+ priv->info = info;
+ priv->channel = i;
+ priv->cmd = info->scc_base + (i ? SCCB_CMD : SCCA_CMD);
+ priv->data = info->scc_base + (i ? SCCB_DATA : SCCA_DATA);
+ priv->tmr = info->tmr_base + (i ? TMR_CNT2 : TMR_CNT1);
+ priv->param.pclk_hz = hw[h].pclk_hz;
+ priv->param.brg_tc = -1;
+ priv->param.clocks = TCTRxCP | RCRTxCP;
+ priv->param.txdelay = TMR_0_HZ * 10 / 1000;
+ priv->param.txtime = HZ * 3;
+ priv->param.sqdelay = TMR_0_HZ * 1 / 1000;
+ priv->param.slottime = TMR_0_HZ * 10 / 1000;
+ priv->param.waittime = TMR_0_HZ * 100 / 1000;
+ priv->param.persist = 32;
+ priv->rx_task.routine = rx_bh;
+ priv->rx_task.data = dev;
+ dev->priv = priv;
+ dev->name = priv->name;
+ dev->base_addr = io;
+ dev->irq = irq;
+ dev->open = scc_open;
+ dev->stop = scc_close;
+ dev->do_ioctl = scc_ioctl;
+ dev->hard_start_xmit = scc_send_packet;
+ dev->get_stats = scc_get_stats;
+ dev->hard_header = ax25_encapsulate;
+ dev->rebuild_header = ax25_rebuild_header;
+ dev->set_mac_address = scc_set_mac_address;
+ dev->type = ARPHRD_AX25;
+ dev->hard_header_len = 73;
+ dev->mtu = 1500;
+ dev->addr_len = 7;
+ dev->tx_queue_len = 64;
+ memcpy(dev->broadcast, ax25_broadcast, 7);
+ memcpy(dev->dev_addr, ax25_test, 7);
+ dev->flags = 0;
+ dev_init_buffers(dev);
+ if (register_netdev(dev)) {
+ printk("dmascc: could not register %s\n", dev->name);
+ dev->name = NULL;
+ }
+ }
+
+ request_region(io, hw[h].io_size, "dmascc");
+
+ info->next = first;
+ first = info;
+ printk("dmascc: found %s (%s) at %#3x, irq %d\n", hw[h].name,
+ chipnames[chip], io, irq);
+ return 0;
+}
+
+
+/* Driver functions */
+
+static inline void write_scc(int ctl, int reg, int val)
+{
+ outb_p(reg, ctl);
+ outb_p(val, ctl);
+}
+
+
+static inline int read_scc(int ctl, int reg)
+{
+ outb_p(reg, ctl);
+ return inb_p(ctl);
+}
+
+
+static int scc_open(struct device *dev)
+{
+ struct scc_priv *priv = dev->priv;
+ struct scc_info *info = priv->info;
+ int io = dev->base_addr;
+ int cmd = priv->cmd;
+
+ /* Request IRQ if not already used by other channel */
+ if (!info->open) {
+ if (request_irq(dev->irq, scc_isr, SA_INTERRUPT, "dmascc", info))
+ return -EAGAIN;
+ }
+
+ /* Request DMA if required */
+ if (dev->dma && request_dma(dev->dma, "dmascc")) {
+ if (!info->open) free_irq(dev->irq, info);
+ return -EAGAIN;
+ }
+
+ /* Initialize local variables */
+ dev->tbusy = 0;
+ priv->rx_ptr = 0;
+ priv->rx_over = 0;
+ priv->rx_head = priv->rx_tail = priv->rx_count = 0;
+ priv->tx_state = TX_IDLE;
+ priv->tx_head = priv->tx_tail = priv->tx_count = 0;
+ priv->tx_ptr = 0;
+ priv->tx_sem = 0;
+
+ /* Reset channel */
+ write_scc(cmd, R9, (priv->channel ? CHRB : CHRA) | MIE | NV);
+ /* X1 clock, SDLC mode */
+ write_scc(cmd, R4, SDLC | X1CLK);
+ /* DMA */
+ write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN);
+ /* 8 bit RX char, RX disable */
+ write_scc(cmd, R3, Rx8);
+ /* 8 bit TX char, TX disable */
+ write_scc(cmd, R5, Tx8);
+ /* SDLC address field */
+ write_scc(cmd, R6, 0);
+ /* SDLC flag */
+ write_scc(cmd, R7, FLAG);
+ switch (info->chip) {
+ case Z85C30:
+ /* Select WR7' */
+ write_scc(cmd, R15, 1);
+ /* Auto EOM reset */
+ write_scc(cmd, R7, 0x02);
+ write_scc(cmd, R15, 0);
+ break;
+ case Z85230:
+ /* Select WR7' */
+ write_scc(cmd, R15, 1);
+ /* RX FIFO half full (interrupt only), Auto EOM reset,
+ TX FIFO empty (DMA only) */
+ write_scc(cmd, R7, dev->dma ? 0x22 : 0x0a);
+ write_scc(cmd, R15, 0);
+ break;
+ }
+ /* Preset CRC, NRZ(I) encoding */
+ write_scc(cmd, R10, CRCPS | (priv->param.nrzi ? NRZI : NRZ));
+
+ /* Configure baud rate generator */
+ if (priv->param.brg_tc >= 0) {
+ /* Program BR generator */
+ write_scc(cmd, R12, priv->param.brg_tc & 0xFF);
+ write_scc(cmd, R13, (priv->param.brg_tc>>8) & 0xFF);
+ /* BRG source = SYS CLK; enable BRG; DTR REQ function (required by
+ PackeTwin, not connected on the PI2); set DPLL source to BRG */
+ write_scc(cmd, R14, SSBR | DTRREQ | BRSRC | BRENABL);
+ /* Enable DPLL */
+ write_scc(cmd, R14, SEARCH | DTRREQ | BRSRC | BRENABL);
+ } else {
+ /* Disable BR generator */
+ write_scc(cmd, R14, DTRREQ | BRSRC);
+ }
+
+ /* Configure clocks */
+ if (info->type == TYPE_TWIN) {
+ /* Disable external TX clock receiver */
+ outb_p((info->twin_serial_cfg &=
+ ~(priv->channel ? TWIN_EXTCLKB : TWIN_EXTCLKA)),
+ io + TWIN_SERIAL_CFG);
+ }
+ write_scc(cmd, R11, priv->param.clocks);
+ if ((info->type == TYPE_TWIN) && !(priv->param.clocks & TRxCOI)) {
+ /* Enable external TX clock receiver */
+ outb_p((info->twin_serial_cfg |=
+ (priv->channel ? TWIN_EXTCLKB : TWIN_EXTCLKA)),
+ io + TWIN_SERIAL_CFG);
+ }
+
+ /* Configure PackeTwin */
+ if (info->type == TYPE_TWIN) {
+ /* Assert DTR, enable interrupts */
+ outb_p((info->twin_serial_cfg |= TWIN_EI |
+ (priv->channel ? TWIN_DTRB_ON : TWIN_DTRA_ON)),
+ io + TWIN_SERIAL_CFG);
+ }
+
+ /* Read current status */
+ priv->status = read_scc(cmd, R0);
+ /* Enable SYNC, DCD, and CTS interrupts */
+ write_scc(cmd, R15, DCDIE | CTSIE | SYNCIE);
+
+ /* Configure PI2 DMA */
+ if (info->type <= TYPE_PI2) outb_p(1, io + PI_DREQ_MASK);
+
+ dev->start = 1;
+ info->open++;
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+
+static int scc_close(struct device *dev)
+{
+ struct scc_priv *priv = dev->priv;
+ struct scc_info *info = priv->info;
+ int io = dev->base_addr;
+ int cmd = priv->cmd;
+
+ dev->start = 0;
+ info->open--;
+ MOD_DEC_USE_COUNT;
+
+ if (info->type == TYPE_TWIN)
+ /* Drop DTR */
+ outb_p((info->twin_serial_cfg &=
+ (priv->channel ? ~TWIN_DTRB_ON : ~TWIN_DTRA_ON)),
+ io + TWIN_SERIAL_CFG);
+
+ /* Reset channel, free DMA */
+ write_scc(cmd, R9, (priv->channel ? CHRB : CHRA) | MIE | NV);
+ if (dev->dma) {
+ if (info->type == TYPE_TWIN) outb_p(0, io + TWIN_DMA_CFG);
+ free_dma(dev->dma);
+ }
+
+ if (!info->open) {
+ if (info->type <= TYPE_PI2) outb_p(0, io + PI_DREQ_MASK);
+ free_irq(dev->irq, info);
+ }
+ return 0;
+}
+
+
+static int scc_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+ int rc;
+ struct scc_priv *priv = dev->priv;
+
+ switch (cmd) {
+ case SIOCGSCCPARAM:
+ rc = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct scc_param));
+ if (rc) return rc;
+ copy_to_user(ifr->ifr_data, &priv->param, sizeof(struct scc_param));
+ return 0;
+ case SIOCSSCCPARAM:
+ if (!suser()) return -EPERM;
+ rc = verify_area(VERIFY_READ, ifr->ifr_data, sizeof(struct scc_param));
+ if (rc) return rc;
+ if (dev->start) return -EAGAIN;
+ copy_from_user(&priv->param, ifr->ifr_data, sizeof(struct scc_param));
+ dev->dma = priv->param.dma;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+
+static int scc_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct scc_priv *priv = dev->priv;
+ struct scc_info *info = priv->info;
+ int cmd = priv->cmd;
+ unsigned long flags;
+ int i;
+
+ /* Block a timer-based transmit from overlapping */
+ if (test_and_set_bit(0, (void *) &priv->tx_sem) != 0) {
+ atomic_inc((void *) &priv->stats.tx_dropped);
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
+ }
+
+ /* Return with an error if we cannot accept more data */
+ if (dev->tbusy) {
+ priv->tx_sem = 0;
+ return -1;
+ }
+
+ /* Transfer data to DMA buffer */
+ i = priv->tx_head;
+ memcpy(priv->tx_buf[i], skb->data+1, skb->len-1);
+ priv->tx_len[i] = skb->len-1;
+
+ save_flags(flags);
+ cli();
+
+ /* Set the busy flag if we just filled up the last buffer */
+ priv->tx_head = (i + 1) % NUM_TX_BUF;
+ priv->tx_count++;
+ if (priv->tx_count == NUM_TX_BUF) dev->tbusy = 1;
+
+ /* Set new TX state */
+ if (priv->tx_state == TX_IDLE) {
+ /* Assert RTS, start timer */
+ priv->tx_state = TX_TXDELAY;
+ if (info->type <= TYPE_PI2) outb_p(0, dev->base_addr + PI_DREQ_MASK);
+ write_scc(cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8);
+ if (info->type <= TYPE_PI2) outb_p(1, dev->base_addr + PI_DREQ_MASK);
+ priv->tx_start = jiffies;
+ delay(dev, priv->param.txdelay);
+ }
+
+ restore_flags(flags);
+
+ dev_kfree_skb(skb, FREE_WRITE);
+
+ priv->tx_sem = 0;
+ return 0;
+}
+
+
+static struct enet_statistics *scc_get_stats(struct device *dev)
+{
+ struct scc_priv *priv = dev->priv;
+
+ return &priv->stats;
+}
+
+
+static int scc_set_mac_address(struct device *dev, void *sa)
+{
+ memcpy(dev->dev_addr, ((struct sockaddr *)sa)->sa_data, dev->addr_len);
+ return 0;
+}
+
+
+static void scc_isr(int irq, void *dev_id, struct pt_regs * regs)
+{
+ struct scc_info *info = dev_id;
+ int is, io = info->dev[0].base_addr;
+
+ /* We're a fast IRQ handler and are called with interrupts disabled */
+
+ /* IRQ sharing doesn't make sense due to ISA's edge-triggered
+ interrupts, hence it is safe to return if we have found and
+ processed a single device. */
+
+ /* Interrupt processing: We loop until we know that the IRQ line is
+ low. If another positive edge occurs afterwards during the ISR,
+ another interrupt will be triggered by the interrupt controller
+ as soon as the IRQ level is enabled again (see asm/irq.h). */
+
+ switch (info->type) {
+ case TYPE_PI:
+ case TYPE_PI2:
+ outb_p(0, io + PI_DREQ_MASK);
+ z8530_isr(info);
+ outb_p(1, io + PI_DREQ_MASK);
+ return;
+ case TYPE_TWIN:
+ while ((is = ~inb_p(io + TWIN_INT_REG)) &
+ TWIN_INT_MSK) {
+ if (is & TWIN_SCC_MSK) {
+ z8530_isr(info);
+ } else if (is & TWIN_TMR1_MSK) {
+ inb_p(io + TWIN_CLR_TMR1);
+ tm_isr(&info->dev[0]);
+ } else {
+ inb_p(io + TWIN_CLR_TMR2);
+ tm_isr(&info->dev[1]);
+ }
+ }
+ /* No interrupts pending from the PackeTwin */
+ return;
+ }
+}
+
+
+static inline void z8530_isr(struct scc_info *info)
+{
+ int is, a_cmd;
+
+ a_cmd = info->scc_base + SCCA_CMD;
+
+ while ((is = read_scc(a_cmd, R3))) {
+ if (is & CHARxIP) {
+ rx_isr(&info->dev[0]);
+ } else if (is & CHATxIP) {
+ tx_isr(&info->dev[0]);
+ } else if (is & CHAEXT) {
+ es_isr(&info->dev[0]);
+ } else if (is & CHBRxIP) {
+ rx_isr(&info->dev[1]);
+ } else if (is & CHBTxIP) {
+ tx_isr(&info->dev[1]);
+ } else {
+ es_isr(&info->dev[1]);
+ }
+ }
+ /* Ok, no interrupts pending from this 8530. The INT line should
+ be inactive now. */
+}
+
+
+static void rx_isr(struct device *dev)
+{
+ struct scc_priv *priv = dev->priv;
+ int cmd = priv->cmd;
+
+ if (dev->dma) {
+ /* Check special condition and perform error reset. See 2.4.7.5. */
+ special_condition(dev, read_scc(cmd, R1));
+ write_scc(cmd, R0, ERR_RES);
+ } else {
+ /* Check special condition for each character. Error reset not necessary.
+ Same algorithm for SCC and ESCC. See 2.4.7.1 and 2.4.7.4. */
+ int rc;
+ while (read_scc(cmd, R0) & Rx_CH_AV) {
+ rc = read_scc(cmd, R1);
+ if (priv->rx_ptr < BUF_SIZE)
+ priv->rx_buf[priv->rx_head][priv->rx_ptr++] = read_scc(cmd, R8);
+ else {
+ priv->rx_over = 2;
+ read_scc(cmd, R8);
+ }
+ special_condition(dev, rc);
+ }
+ }
+}
+
+
+static void special_condition(struct device *dev, int rc)
+{
+ struct scc_priv *priv = dev->priv;
+ int cb, cmd = priv->cmd;
+
+ /* See Figure 2-15. Only overrun and EOF need to be checked. */
+
+ if (rc & Rx_OVR) {
+ /* Receiver overrun */
+ priv->rx_over = 1;
+ if (!dev->dma) write_scc(cmd, R0, ERR_RES);
+ } else if (rc & END_FR) {
+ /* End of frame. Get byte count */
+ if (dev->dma) {
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ cb = BUF_SIZE - get_dma_residue(dev->dma) - 2;
+ } else {
+ cb = priv->rx_ptr - 2;
+ }
+ if (priv->rx_over) {
+ /* We had an overrun */
+ priv->stats.rx_errors++;
+ if (priv->rx_over == 2) priv->stats.rx_length_errors++;
+ else priv->stats.rx_fifo_errors++;
+ priv->rx_over = 0;
+ } else if (rc & CRC_ERR) {
+ /* Count invalid CRC only if packet length >= minimum */
+ if (cb >= 8) {
+ priv->stats.rx_errors++;
+ priv->stats.rx_crc_errors++;
+ }
+ } else {
+ if (cb >= 8) {
+ /* Put good frame in FIFO */
+ priv->rx_len[priv->rx_head] = cb;
+ priv->rx_head = (priv->rx_head + 1) % NUM_RX_BUF;
+ priv->rx_count++;
+ if (priv->rx_count == NUM_RX_BUF) {
+ /* Disable receiver if FIFO full */
+ write_scc(cmd, R3, Rx8);
+ priv->stats.rx_errors++;
+ priv->stats.rx_over_errors++;
+ }
+ /* Mark bottom half handler */
+ queue_task(&priv->rx_task, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ }
+ }
+ /* Get ready for new frame */
+ if (dev->dma) {
+ set_dma_addr(dev->dma, (int) priv->rx_buf[priv->rx_head]);
+ set_dma_count(dev->dma, BUF_SIZE);
+ enable_dma(dev->dma);
+ } else {
+ priv->rx_ptr = 0;
+ }
+ }
+}
+
+
+static void rx_bh(void *arg)
+{
+ struct device *dev = arg;
+ struct scc_priv *priv = dev->priv;
+ struct scc_info *info = priv->info;
+ int cmd = priv->cmd;
+ int i = priv->rx_tail;
+ int cb;
+ unsigned long flags;
+ struct sk_buff *skb;
+ unsigned char *data;
+
+ save_flags(flags);
+ cli();
+
+ while (priv->rx_count) {
+ restore_flags(flags);
+ cb = priv->rx_len[i];
+ /* Allocate buffer */
+ skb = dev_alloc_skb(cb+1);
+ if (skb == NULL) {
+ /* Drop packet */
+ priv->stats.rx_dropped++;
+ } else {
+ /* Fill buffer */
+ data = skb_put(skb, cb+1);
+ data[0] = 0;
+ memcpy(&data[1], priv->rx_buf[i], cb);
+ skb->dev = dev;
+ skb->protocol = ntohs(ETH_P_AX25);
+ skb->mac.raw = skb->data;
+ netif_rx(skb);
+ priv->stats.rx_packets++;
+ }
+ save_flags(flags);
+ cli();
+ /* Enable receiver if RX buffers have been unavailable */
+ if ((priv->rx_count == NUM_RX_BUF) && (priv->status & DCD)) {
+ if (info->type <= TYPE_PI2) outb_p(0, dev->base_addr + PI_DREQ_MASK);
+ write_scc(cmd, R3, RxENABLE | Rx8 | RxCRC_ENAB);
+ if (info->type <= TYPE_PI2) outb_p(1, dev->base_addr + PI_DREQ_MASK);
+ }
+ /* Move tail */
+ priv->rx_tail = i = (i + 1) % NUM_RX_BUF;
+ priv->rx_count--;
+ }
+
+ restore_flags(flags);
+}
+
+
+static void tx_isr(struct device *dev)
+{
+ struct scc_priv *priv = dev->priv;
+ int cmd = priv->cmd;
+ int i = priv->tx_tail, p = priv->tx_ptr;
+
+ /* Suspend TX interrupts if we don't want to send anything.
+ See Figure 2-22. */
+ if (p == priv->tx_len[i]) {
+ write_scc(cmd, R0, RES_Tx_P);
+ return;
+ }
+
+ /* Write characters */
+ while ((read_scc(cmd, R0) & Tx_BUF_EMP) && p < priv->tx_len[i]) {
+ write_scc(cmd, R8, priv->tx_buf[i][p++]);
+ }
+ priv->tx_ptr = p;
+
+}
+
+
+static void es_isr(struct device *dev)
+{
+ struct scc_priv *priv = dev->priv;
+ struct scc_info *info = priv->info;
+ int i, cmd = priv->cmd;
+ int st, dst, res;
+
+ /* Read status and reset interrupt bit */
+ st = read_scc(cmd, R0);
+ write_scc(cmd, R0, RES_EXT_INT);
+ dst = priv->status ^ st;
+ priv->status = st;
+
+ /* Since the EOM latch is reset automatically, we assume that
+ it has been zero if and only if we are in the TX_ACTIVE state.
+ Otherwise we follow 2.4.9.6. */
+
+ /* Transmit underrun */
+ if ((priv->tx_state == TX_ACTIVE) && (st & TxEOM)) {
+ /* Get remaining bytes */
+ i = priv->tx_tail;
+ if (dev->dma) {
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ res = get_dma_residue(dev->dma);
+ } else {
+ res = priv->tx_len[i] - priv->tx_ptr;
+ if (res) write_scc(cmd, R0, RES_Tx_P);
+ priv->tx_ptr = 0;
+ }
+ /* Remove frame from FIFO */
+ priv->tx_tail = (i + 1) % NUM_TX_BUF;
+ priv->tx_count--;
+ dev->tbusy = 0;
+ /* Check if another frame is available and we are allowed to transmit */
+ if (priv->tx_count && (jiffies - priv->tx_start) < priv->param.txtime) {
+ if (dev->dma) {
+ set_dma_addr(dev->dma, (int) priv->tx_buf[priv->tx_tail]);
+ set_dma_count(dev->dma, priv->tx_len[priv->tx_tail]);
+ enable_dma(dev->dma);
+ } else {
+ /* If we have an ESCC, we are allowed to write data bytes
+ immediately. Otherwise we have to wait for the next
+ TX interrupt. See Figure 2-22. */
+ if (info->chip == Z85230) {
+ tx_isr(dev);
+ }
+ }
+ } else {
+ /* No frame available. Disable interrupts. */
+ priv->tx_state = TX_SQDELAY;
+ delay(dev, priv->param.sqdelay);
+ write_scc(cmd, R15, DCDIE | CTSIE | SYNCIE);
+ write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN);
+ }
+ /* Update packet statistics */
+ if (res) {
+ priv->stats.tx_errors++;
+ priv->stats.tx_fifo_errors++;
+ } else {
+ priv->stats.tx_packets++;
+ }
+ /* Inform upper layers */
+ mark_bh(NET_BH);
+ }
+
+ /* DCD transition */
+ if ((priv->tx_state < TX_TXDELAY) && (dst & DCD)) {
+ /* Transmitter state change */
+ priv->tx_state = TX_OFF;
+ /* Enable or disable receiver */
+ if (st & DCD) {
+ if (dev->dma) {
+ /* Program DMA controller */
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ set_dma_mode(dev->dma, DMA_MODE_READ);
+ set_dma_addr(dev->dma, (int) priv->rx_buf[priv->rx_head]);
+ set_dma_count(dev->dma, BUF_SIZE);
+ enable_dma(dev->dma);
+ /* Configure PackeTwin DMA */
+ if (info->type == TYPE_TWIN) {
+ outb_p((dev->dma == 1) ? TWIN_DMA_HDX_R1 : TWIN_DMA_HDX_R3,
+ dev->base_addr + TWIN_DMA_CFG);
+ }
+ /* Sp. cond. intr. only, ext int enable */
+ write_scc(cmd, R1, EXT_INT_ENAB | INT_ERR_Rx |
+ WT_RDY_RT | WT_FN_RDYFN | WT_RDY_ENAB);
+ } else {
+ /* Intr. on all Rx characters and Sp. cond., ext int enable */
+ write_scc(cmd, R1, EXT_INT_ENAB | INT_ALL_Rx | WT_RDY_RT |
+ WT_FN_RDYFN);
+ }
+ if (priv->rx_count < NUM_RX_BUF) {
+ /* Enable receiver */
+ write_scc(cmd, R3, RxENABLE | Rx8 | RxCRC_ENAB);
+ }
+ } else {
+ /* Disable DMA */
+ if (dev->dma) disable_dma(dev->dma);
+ /* Disable receiver */
+ write_scc(cmd, R3, Rx8);
+ /* DMA disable, RX int disable, Ext int enable */
+ write_scc(cmd, R1, EXT_INT_ENAB | WT_RDY_RT | WT_FN_RDYFN);
+ /* Transmitter state change */
+ if (random() > priv->param.persist)
+ delay(dev, priv->param.slottime);
+ else {
+ if (priv->tx_count) {
+ priv->tx_state = TX_TXDELAY;
+ write_scc(cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8);
+ priv->tx_start = jiffies;
+ delay(dev, priv->param.txdelay);
+ } else {
+ priv->tx_state = TX_IDLE;
+ }
+ }
+ }
+ }
+
+ /* CTS transition */
+ if ((info->type <= TYPE_PI2) && (dst & CTS) && (~st & CTS)) {
+ /* Timer has expired */
+ tm_isr(dev);
+ }
+
+ /* /SYNC/HUNT transition */
+ if ((dst & SYNC_HUNT) && (~st & SYNC_HUNT)) {
+ /* Reset current frame and clear RX FIFO */
+ while (read_scc(cmd, R0) & Rx_CH_AV) read_scc(cmd, R8);
+ priv->rx_over = 0;
+ if (dev->dma) {
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ set_dma_addr(dev->dma, (int) priv->rx_buf[priv->rx_head]);
+ set_dma_count(dev->dma, BUF_SIZE);
+ enable_dma(dev->dma);
+ } else {
+ priv->rx_ptr = 0;
+ }
+ }
+}
+
+
+static void tm_isr(struct device *dev)
+{
+ struct scc_priv *priv = dev->priv;
+ struct scc_info *info = priv->info;
+ int cmd = priv->cmd;
+
+ switch (priv->tx_state) {
+ case TX_OFF:
+ if (~priv->status & DCD) {
+ if (random() > priv->param.persist) delay(dev, priv->param.slottime);
+ else {
+ if (priv->tx_count) {
+ priv->tx_state = TX_TXDELAY;
+ write_scc(cmd, R5, TxCRC_ENAB | RTS | TxENAB | Tx8);
+ priv->tx_start = jiffies;
+ delay(dev, priv->param.txdelay);
+ } else {
+ priv->tx_state = TX_IDLE;
+ }
+ }
+ }
+ break;
+ case TX_TXDELAY:
+ priv->tx_state = TX_ACTIVE;
+ if (dev->dma) {
+ /* Program DMA controller */
+ disable_dma(dev->dma);
+ clear_dma_ff(dev->dma);
+ set_dma_mode(dev->dma, DMA_MODE_WRITE);
+ set_dma_addr(dev->dma, (int) priv->tx_buf[priv->tx_tail]);
+ set_dma_count(dev->dma, priv->tx_len[priv->tx_tail]);
+ enable_dma(dev->dma);
+ /* Configure PackeTwin DMA */
+ if (info->type == TYPE_TWIN) {
+ outb_p((dev->dma == 1) ? TWIN_DMA_HDX_T1 : TWIN_DMA_HDX_T3,
+ dev->base_addr + TWIN_DMA_CFG);
+ }
+ /* Enable interrupts and DMA. On the PackeTwin, the DTR//REQ pin
+ is used for TX DMA requests, but we enable the WAIT/DMA request
+ pin, anyway */
+ write_scc(cmd, R15, TxUIE | DCDIE | CTSIE | SYNCIE);
+ write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN | WT_RDY_ENAB);
+ } else {
+ write_scc(cmd, R15, TxUIE | DCDIE | CTSIE | SYNCIE);
+ write_scc(cmd, R1, EXT_INT_ENAB | WT_FN_RDYFN | TxINT_ENAB);
+ tx_isr(dev);
+ }
+ if (info->chip == Z8530) write_scc(cmd, R0, RES_EOM_L);
+ break;
+ case TX_SQDELAY:
+ /* Disable transmitter */
+ write_scc(cmd, R5, TxCRC_ENAB | Tx8);
+ /* Transmitter state change: Switch to TX_OFF and wait at least
+ 1 slottime. */
+ priv->tx_state = TX_OFF;
+ if (~priv->status & DCD) delay(dev, priv->param.waittime);
+ }
+}
+
+
+static inline void delay(struct device *dev, int t)
+{
+ struct scc_priv *priv = dev->priv;
+ int tmr = priv->tmr;
+
+ outb_p(t & 0xFF, tmr);
+ outb_p((t >> 8) & 0xFF, tmr);
+}
+
+
+static inline unsigned char random(void)
+{
+ /* See "Numerical Recipes in C", second edition, p. 284 */
+ rand = rand * 1664525L + 1013904223L;
+ return (unsigned char) (rand >> 24);
+}
+
+
diff --git a/drivers/net/hdlcdrv.c b/drivers/net/hdlcdrv.c
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/hdlcdrv.c
+++ /dev/null
diff --git a/drivers/net/mkiss.c b/drivers/net/mkiss.c
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/mkiss.c
+++ /dev/null
diff --git a/drivers/net/mkiss.h b/drivers/net/mkiss.h
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/mkiss.h
+++ /dev/null
diff --git a/drivers/net/pi2.c b/drivers/net/pi2.c
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/pi2.c
+++ /dev/null
diff --git a/drivers/net/pt.c b/drivers/net/pt.c
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/pt.c
+++ /dev/null
diff --git a/drivers/net/scc.c b/drivers/net/scc.c
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/scc.c
+++ /dev/null
diff --git a/drivers/net/soundmodem/.cvsignore b/drivers/net/soundmodem/.cvsignore
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/soundmodem/.cvsignore
+++ /dev/null
diff --git a/drivers/net/soundmodem/Makefile b/drivers/net/soundmodem/Makefile
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/soundmodem/Makefile
+++ /dev/null
diff --git a/drivers/net/soundmodem/gentbl.c b/drivers/net/soundmodem/gentbl.c
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/soundmodem/gentbl.c
+++ /dev/null
diff --git a/drivers/net/soundmodem/sm.c b/drivers/net/soundmodem/sm.c
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/soundmodem/sm.c
+++ /dev/null
diff --git a/drivers/net/soundmodem/sm.h b/drivers/net/soundmodem/sm.h
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/soundmodem/sm.h
+++ /dev/null
diff --git a/drivers/net/soundmodem/sm_afsk1200.c b/drivers/net/soundmodem/sm_afsk1200.c
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/soundmodem/sm_afsk1200.c
+++ /dev/null
diff --git a/drivers/net/soundmodem/sm_afsk2400_7.c b/drivers/net/soundmodem/sm_afsk2400_7.c
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/soundmodem/sm_afsk2400_7.c
+++ /dev/null
diff --git a/drivers/net/soundmodem/sm_afsk2400_8.c b/drivers/net/soundmodem/sm_afsk2400_8.c
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/soundmodem/sm_afsk2400_8.c
+++ /dev/null
diff --git a/drivers/net/soundmodem/sm_fsk9600.c b/drivers/net/soundmodem/sm_fsk9600.c
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/soundmodem/sm_fsk9600.c
+++ /dev/null
diff --git a/drivers/net/soundmodem/sm_hapn4800.c b/drivers/net/soundmodem/sm_hapn4800.c
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/soundmodem/sm_hapn4800.c
+++ /dev/null
diff --git a/drivers/net/soundmodem/sm_sbc.c b/drivers/net/soundmodem/sm_sbc.c
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/soundmodem/sm_sbc.c
+++ /dev/null
diff --git a/drivers/net/soundmodem/sm_wss.c b/drivers/net/soundmodem/sm_wss.c
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/soundmodem/sm_wss.c
+++ /dev/null
diff --git a/drivers/net/soundmodem/smdma.h b/drivers/net/soundmodem/smdma.h
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/soundmodem/smdma.h
+++ /dev/null
diff --git a/drivers/net/z8530.h b/drivers/net/z8530.h
deleted file mode 100644
index e69de29bb..000000000
--- a/drivers/net/z8530.h
+++ /dev/null
diff --git a/drivers/sgi/char/Config.in b/drivers/sgi/char/Config.in
new file mode 100644
index 000000000..e216e4328
--- /dev/null
+++ b/drivers/sgi/char/Config.in
@@ -0,0 +1,12 @@
+#
+# Character device configuration
+#
+mainmenu_option next_comment
+comment 'SGI Character devices'
+
+bool 'SGI Zilog85C30 serial support' CONFIG_SGI_SERIAL
+if [ "$CONFIG_SGI_SERIAL" != "n" ]; then
+ define_bool CONFIG_SERIAL y
+fi
+
+endmenu