summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-06-17 14:08:29 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-06-17 14:08:29 +0000
commit57d569635c05dc4ea9b9f1f8dcec69b9ddc989b2 (patch)
tree1f703abf7d95dcd50ee52da3b96eb1b4b2b4ea53 /drivers
parent59223edaa18759982db0a8aced0e77457d10c68e (diff)
The rest of 2.3.6.
Diffstat (limited to 'drivers')
-rw-r--r--drivers/block/MAKEDEV-IDE4599
-rw-r--r--drivers/block/MAKEDEV-IDE6799
-rw-r--r--drivers/block/aec6210.c63
-rw-r--r--drivers/block/alim15x3.c348
-rw-r--r--drivers/block/blkpg.c290
-rw-r--r--drivers/block/buddha.c161
-rw-r--r--drivers/block/cy82c693.c432
-rw-r--r--drivers/block/falconide.c66
-rw-r--r--drivers/block/gayle.c169
-rw-r--r--drivers/block/hpt343.c392
-rw-r--r--drivers/block/macide.c167
-rw-r--r--drivers/block/pdc202xx.c567
-rw-r--r--drivers/block/piix.c294
-rw-r--r--drivers/char/defkeymap.c265
-rw-r--r--drivers/char/i2c-parport.c149
-rw-r--r--drivers/i2o/Config.in12
-rw-r--r--drivers/i2o/Makefile75
-rw-r--r--drivers/i2o/README78
-rw-r--r--drivers/i2o/README.ioctl398
-rw-r--r--drivers/i2o/README.lan38
-rw-r--r--drivers/i2o/i2o_block.c1071
-rw-r--r--drivers/i2o/i2o_config.c613
-rw-r--r--drivers/i2o/i2o_core.c2053
-rw-r--r--drivers/i2o/i2o_lan.c853
-rw-r--r--drivers/i2o/i2o_lan.h112
-rw-r--r--drivers/i2o/i2o_pci.c243
-rw-r--r--drivers/i2o/i2o_proc.c2382
-rw-r--r--drivers/i2o/i2o_proc.h141
-rw-r--r--drivers/i2o/i2o_scsi.c871
-rw-r--r--drivers/i2o/i2o_scsi.h48
-rw-r--r--drivers/isdn/eicon/Makefile13
-rw-r--r--drivers/isdn/eicon/eicon.h528
-rw-r--r--drivers/isdn/eicon/eicon_dsp.h304
-rw-r--r--drivers/isdn/eicon/eicon_idi.c1479
-rw-r--r--drivers/isdn/eicon/eicon_idi.h248
-rw-r--r--drivers/isdn/eicon/eicon_io.c755
-rw-r--r--drivers/isdn/eicon/eicon_isa.c432
-rw-r--r--drivers/isdn/eicon/eicon_isa.h144
-rw-r--r--drivers/isdn/eicon/eicon_mod.c1210
-rw-r--r--drivers/isdn/eicon/eicon_pci.c952
-rw-r--r--drivers/isdn/eicon/eicon_pci.h188
-rw-r--r--drivers/isdn/hisax/avm_a1p.c334
-rw-r--r--drivers/isdn/hisax/avm_pci.c865
-rw-r--r--drivers/isdn/hisax/cert.c55
-rw-r--r--drivers/isdn/hisax/elsa_ser.c749
-rw-r--r--drivers/isdn/hisax/isar.c937
-rw-r--r--drivers/isdn/hisax/isar.h91
-rw-r--r--drivers/isdn/hisax/md5sums.asc29
-rw-r--r--drivers/isdn/hisax/s0box.c277
-rw-r--r--drivers/isdn/hisax/telespci.c372
-rw-r--r--drivers/isdn/isdn_bsdcomp.c934
-rw-r--r--drivers/isdn/isdn_budget.c206
-rw-r--r--drivers/net/cycx_drv.c663
-rw-r--r--drivers/net/cycx_main.c377
-rw-r--r--drivers/net/cycx_x25.c1538
-rw-r--r--drivers/net/irda/litelink.c206
-rw-r--r--drivers/net/irda/smc-ircc.c969
-rw-r--r--drivers/net/irda/toshoboe.c901
-rw-r--r--drivers/net/sk_mca.c1143
-rw-r--r--drivers/net/sk_mca.h174
-rw-r--r--drivers/sbus/char/aurora.c2373
-rw-r--r--drivers/sbus/char/aurora.h278
-rw-r--r--drivers/sbus/char/cd180.h240
-rw-r--r--drivers/sound/cmpci.c2391
-rw-r--r--drivers/usb/acm.c293
-rw-r--r--drivers/usb/cpia.c1267
-rw-r--r--drivers/usb/cpia.h139
-rw-r--r--drivers/usb/keymap-mac.c50
-rw-r--r--drivers/usb/maps/mac.map350
-rw-r--r--drivers/usb/mkmap.adb88
-rw-r--r--drivers/usb/printer.c413
-rw-r--r--drivers/usb/usb-core.c96
-rw-r--r--drivers/usb/usb_scsi.c1098
-rw-r--r--drivers/usb/usb_scsi.h145
-rw-r--r--drivers/usb/usb_scsi_debug.c104
-rw-r--r--drivers/usb/usb_scsi_dt.c4
-rw-r--r--drivers/video/cyber2000fb.c1057
-rw-r--r--drivers/video/cyber2000fb.h78
-rw-r--r--drivers/video/fbcon-vga-planes.c364
-rw-r--r--drivers/video/vga16fb.c1064
80 files changed, 41514 insertions, 0 deletions
diff --git a/drivers/block/MAKEDEV-IDE45 b/drivers/block/MAKEDEV-IDE45
new file mode 100644
index 000000000..c55a9b3cf
--- /dev/null
+++ b/drivers/block/MAKEDEV-IDE45
@@ -0,0 +1,99 @@
+#!/bin/sh
+#
+# Andre Hedrick <hedrick@astro.dyer.vanderbilt.edu>
+#
+# The song goes, "I did it the hard way..........."
+#
+
+if [ ! -f /dev/hdi ]; then \
+ echo "Making IDE4 Primary Devices hdi's"; \
+ mknod /dev/hdi b 56 0; \
+ mknod /dev/hdi1 b 56 1; \
+ mknod /dev/hdi2 b 56 2; \
+ mknod /dev/hdi3 b 56 3; \
+ mknod /dev/hdi4 b 56 4; \
+ mknod /dev/hdi5 b 56 5; \
+ mknod /dev/hdi6 b 56 6; \
+ mknod /dev/hdi7 b 56 7; \
+ mknod /dev/hdi8 b 56 8; \
+ mknod /dev/hdi9 b 56 9; \
+ mknod /dev/hdi10 b 56 10; \
+ mknod /dev/hdi11 b 56 11; \
+ mknod /dev/hdi12 b 56 12; \
+ mknod /dev/hdi13 b 56 13; \
+ mknod /dev/hdi14 b 56 14; \
+ mknod /dev/hdi15 b 56 15; \
+ mknod /dev/hdi16 b 56 16; \
+ chown root.disk /dev/hdi*; \
+ chmod 660 /dev/hdi*; \
+fi
+
+if [ ! -f /dev/hdj ]; then \
+ echo "Making IDE4 Secondary Devices hdj's"; \
+ mknod /dev/hdj b 56 64; \
+ mknod /dev/hdj1 b 56 65; \
+ mknod /dev/hdj2 b 56 66; \
+ mknod /dev/hdj3 b 56 67; \
+ mknod /dev/hdj4 b 56 68; \
+ mknod /dev/hdj5 b 56 69; \
+ mknod /dev/hdj6 b 56 70; \
+ mknod /dev/hdj7 b 56 71; \
+ mknod /dev/hdj8 b 56 72; \
+ mknod /dev/hdj9 b 56 73; \
+ mknod /dev/hdj10 b 56 74; \
+ mknod /dev/hdj11 b 56 75; \
+ mknod /dev/hdj12 b 56 76; \
+ mknod /dev/hdj13 b 56 77; \
+ mknod /dev/hdj14 b 56 78; \
+ mknod /dev/hdj15 b 56 79; \
+ mknod /dev/hdj16 b 56 80; \
+ chown root.disk /dev/hdj*; \
+ chmod 660 /dev/hdj*; \
+fi
+
+if [ ! -f /dev/hdk ]; then \
+ echo "Making IDE5 Primary Devices hdk's"; \
+ mknod /dev/hdk b 57 0; \
+ mknod /dev/hdk1 b 57 1; \
+ mknod /dev/hdk2 b 57 2; \
+ mknod /dev/hdk3 b 57 3; \
+ mknod /dev/hdk4 b 57 4; \
+ mknod /dev/hdk5 b 57 5; \
+ mknod /dev/hdk6 b 57 6; \
+ mknod /dev/hdk7 b 57 7; \
+ mknod /dev/hdk8 b 57 8; \
+ mknod /dev/hdk9 b 57 9; \
+ mknod /dev/hdk10 b 57 10; \
+ mknod /dev/hdk11 b 57 11; \
+ mknod /dev/hdk12 b 57 12; \
+ mknod /dev/hdk13 b 57 13; \
+ mknod /dev/hdk14 b 57 14; \
+ mknod /dev/hdk15 b 57 15; \
+ mknod /dev/hdk16 b 57 16; \
+ chown root.disk /dev/hdk*; \
+ chmod 660 /dev/hdk*; \
+fi
+
+if [ ! -f /dev/hdl ]; then \
+ echo "Making IDE5 Secondary Devices hdl's"; \
+ mknod /dev/hdl b 57 64; \
+ mknod /dev/hdl1 b 57 65; \
+ mknod /dev/hdl2 b 57 66; \
+ mknod /dev/hdl3 b 57 67; \
+ mknod /dev/hdl4 b 57 68; \
+ mknod /dev/hdl5 b 57 69; \
+ mknod /dev/hdl6 b 57 70; \
+ mknod /dev/hdl7 b 57 71; \
+ mknod /dev/hdl8 b 57 72; \
+ mknod /dev/hdl9 b 57 73; \
+ mknod /dev/hdl10 b 57 74; \
+ mknod /dev/hdl11 b 57 75; \
+ mknod /dev/hdl12 b 57 76; \
+ mknod /dev/hdl13 b 57 77; \
+ mknod /dev/hdl14 b 57 78; \
+ mknod /dev/hdl15 b 57 79; \
+ mknod /dev/hdl16 b 57 80; \
+ chown root.disk /dev/hdl*; \
+ chmod 660 /dev/hdl*; \
+fi
+
diff --git a/drivers/block/MAKEDEV-IDE67 b/drivers/block/MAKEDEV-IDE67
new file mode 100644
index 000000000..27728be28
--- /dev/null
+++ b/drivers/block/MAKEDEV-IDE67
@@ -0,0 +1,99 @@
+#!/bin/sh
+#
+# Andre Hedrick <hedrick@astro.dyer.vanderbilt.edu>
+#
+# The song goes, "I did it the hard way..........."
+#
+
+if [ ! -f /dev/hdm ]; then \
+ echo "Making IDE6 Primary Devices hdm's"; \
+ mknod /dev/hdm b 88 0; \
+ mknod /dev/hdm1 b 88 1; \
+ mknod /dev/hdm2 b 88 2; \
+ mknod /dev/hdm3 b 88 3; \
+ mknod /dev/hdm4 b 88 4; \
+ mknod /dev/hdm5 b 88 5; \
+ mknod /dev/hdm6 b 88 6; \
+ mknod /dev/hdm7 b 88 7; \
+ mknod /dev/hdm8 b 88 8; \
+ mknod /dev/hdm9 b 88 9; \
+ mknod /dev/hdm10 b 88 10; \
+ mknod /dev/hdm11 b 88 11; \
+ mknod /dev/hdm12 b 88 12; \
+ mknod /dev/hdm13 b 88 13; \
+ mknod /dev/hdm14 b 88 14; \
+ mknod /dev/hdm15 b 88 15; \
+ mknod /dev/hdm16 b 88 16; \
+ chown root.disk /dev/hdm*; \
+ chmod 660 /dev/hdm*; \
+fi
+
+if [ ! -f /dev/hdn ]; then \
+ echo "Making IDE6 Secondary Devices hdn's"; \
+ mknod /dev/hdn b 88 64; \
+ mknod /dev/hdn1 b 88 65; \
+ mknod /dev/hdn2 b 88 66; \
+ mknod /dev/hdn3 b 88 67; \
+ mknod /dev/hdn4 b 88 68; \
+ mknod /dev/hdn5 b 88 69; \
+ mknod /dev/hdn6 b 88 70; \
+ mknod /dev/hdn7 b 88 71; \
+ mknod /dev/hdn8 b 88 72; \
+ mknod /dev/hdn9 b 88 73; \
+ mknod /dev/hdn10 b 88 74; \
+ mknod /dev/hdn11 b 88 75; \
+ mknod /dev/hdn12 b 88 76; \
+ mknod /dev/hdn13 b 88 77; \
+ mknod /dev/hdn14 b 88 78; \
+ mknod /dev/hdn15 b 88 79; \
+ mknod /dev/hdn16 b 88 80; \
+ chown root.disk /dev/hdn*; \
+ chmod 660 /dev/hdn*; \
+fi
+
+if [ ! -f /dev/hdo ]; then \
+ echo "Making IDE7 Primary Devices hdo's"; \
+ mknod /dev/hdo b 89 0; \
+ mknod /dev/hdo1 b 89 1; \
+ mknod /dev/hdo2 b 89 2; \
+ mknod /dev/hdo3 b 89 3; \
+ mknod /dev/hdo4 b 89 4; \
+ mknod /dev/hdo5 b 89 5; \
+ mknod /dev/hdo6 b 89 6; \
+ mknod /dev/hdo7 b 89 7; \
+ mknod /dev/hdo8 b 89 8; \
+ mknod /dev/hdo9 b 89 9; \
+ mknod /dev/hdo10 b 89 10; \
+ mknod /dev/hdo11 b 89 11; \
+ mknod /dev/hdo12 b 89 12; \
+ mknod /dev/hdo13 b 89 13; \
+ mknod /dev/hdo14 b 89 14; \
+ mknod /dev/hdo15 b 89 15; \
+ mknod /dev/hdo16 b 89 16; \
+ chown root.disk /dev/hdo*; \
+ chmod 660 /dev/hdo*; \
+fi
+
+if [ ! -f /dev/hdp ]; then \
+ echo "Making IDE7 Secondary Devices hdp's"; \
+ mknod /dev/hdp b 89 64; \
+ mknod /dev/hdp1 b 89 65; \
+ mknod /dev/hdp2 b 89 66; \
+ mknod /dev/hdp3 b 89 67; \
+ mknod /dev/hdp4 b 89 68; \
+ mknod /dev/hdp5 b 89 69; \
+ mknod /dev/hdp6 b 89 70; \
+ mknod /dev/hdp7 b 89 71; \
+ mknod /dev/hdp8 b 89 72; \
+ mknod /dev/hdp9 b 89 73; \
+ mknod /dev/hdp10 b 89 74; \
+ mknod /dev/hdp11 b 89 75; \
+ mknod /dev/hdp12 b 89 76; \
+ mknod /dev/hdp13 b 89 77; \
+ mknod /dev/hdp14 b 89 78; \
+ mknod /dev/hdp15 b 89 79; \
+ mknod /dev/hdp16 b 89 80; \
+ chown root.disk /dev/hdp*; \
+ chmod 660 /dev/hdp*; \
+fi
+
diff --git a/drivers/block/aec6210.c b/drivers/block/aec6210.c
new file mode 100644
index 000000000..03ef37c90
--- /dev/null
+++ b/drivers/block/aec6210.c
@@ -0,0 +1,63 @@
+/*
+ * linux/drivers/block/aec6210.c Version 0.01 Nov 17, 1998
+ *
+ * Copyright (C) 1998 Andre Hedrick (hedrick@astro.dyer.vanderbilt.edu)
+ *
+ * pio 0 :: 40: 00 07 00 00 00 00 00 00 02 07 a6 04 00 02 00 02
+ * pio 1 :: 40: 0a 07 00 00 00 00 00 00 02 07 a6 05 00 02 00 02
+ * pio 2 :: 40: 08 07 00 00 00 00 00 00 02 07 a6 05 00 02 00 02
+ * pio 3 :: 40: 03 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ * pio 4 :: 40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ * dma 0 :: 40: 0a 07 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ * dma 1 :: 40: 02 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ * dma 2 :: 40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ * 50: ff ff ff ff 00 06 04 00 00 00 00 00 00 00 00 00
+ *
+ * udma 0 :: 40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ * 50: ff ff ff ff 01 06 04 00 00 00 00 00 00 00 00 00
+ *
+ * udma 1 :: 40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ * 50: ff ff ff ff 01 06 04 00 00 00 00 00 00 00 00 00
+ *
+ * udma 2 :: 40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ * 50: ff ff ff ff 02 06 04 00 00 00 00 00 00 00 00 00
+ *
+ * auto :: 40: 01 04 00 00 00 00 00 00 02 05 a6 05 00 02 00 02
+ * 50: ff ff ff ff 02 06 04 00 00 00 00 00 00 00 00 00
+ *
+ * auto :: 40: 01 04 01 04 01 04 01 04 02 05 a6 cf 00 02 00 02
+ * 50: ff ff ff ff aa 06 04 00 00 00 00 00 00 00 00 00
+ *
+ * NO-Devices
+ * 40: 00 00 00 00 00 00 00 00 02 05 a6 00 00 02 00 02
+ * 50: ff ff ff ff 00 06 00 00 00 00 00 00 00 00 00 00
+
+ */
+
+#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 <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+__initfunc(unsigned int pci_init_aec6210 (struct pci_dev *dev, const char *name))
+{
+ if (dev->rom_address) {
+ pci_write_config_dword(dev, PCI_ROM_ADDRESS,
+ dev->rom_address | PCI_ROM_ADDRESS_ENABLE);
+ printk("%s: ROM enabled at 0x%08lx\n",
+ name, dev->rom_address);
+ }
+ return dev->irq;
+}
diff --git a/drivers/block/alim15x3.c b/drivers/block/alim15x3.c
new file mode 100644
index 000000000..ce5d0cb63
--- /dev/null
+++ b/drivers/block/alim15x3.c
@@ -0,0 +1,348 @@
+/*
+ * linux/drivers/block/alim15x3.c Version 0.04 Feb. 8, 1999
+ *
+ * Copyright (C) 1998-99 Michel Aubry, Maintainer
+ * Copyright (C) 1998-99 Andrzej Krzysztofowicz, Maintainer
+ * Copyright (C) 1998-99 Andre Hedrick, Integrater and Maintainer
+ *
+ * (U)DMA capable version of ali 1533/1543(C)
+ *
+ * Default disable (U)DMA on all devices execpt hard disks.
+ * This measure of overkill is needed to stablize the chipset code.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#define DISPLAY_ALI_TIMINGS
+
+#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS)
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+
+static int ali_get_info(char *buffer, char **addr, off_t offset, int count, int dummy);
+extern int (*ali_display_info)(char *, char **, off_t, int, int); /* ide-proc.c */
+struct pci_dev *bmide_dev;
+
+char *fifo[4] = {
+ "FIFO Off",
+ "FIFO On ",
+ "DMA mode",
+ "PIO mode" };
+
+char *udmaT[8] = {
+ "1.5T",
+ " 2T",
+ "2.5T",
+ " 3T",
+ "3.5T",
+ " 4T",
+ " 6T",
+ " 8T"
+};
+
+char *channel_status[8] = {
+ "OK ",
+ "busy ",
+ "DRQ ",
+ "DRQ busy ",
+ "error ",
+ "error busy ",
+ "error DRQ ",
+ "error DRQ busy"
+};
+#endif /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+__initfunc(unsigned int pci_init_ali15x3 (struct pci_dev *dev, const char *name))
+{
+ byte confreg0 = 0, confreg1 =0, progif = 0;
+ int errors = 0;
+
+ if (pci_read_config_byte(dev, 0x50, &confreg1))
+ goto veryspecialsettingserror;
+ if (!(confreg1 & 0x02))
+ if (pci_write_config_byte(dev, 0x50, confreg1 | 0x02))
+ goto veryspecialsettingserror;
+
+ if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif))
+ goto veryspecialsettingserror;
+ if (!(progif & 0x40)) {
+ /*
+ * The way to enable them is to set progif
+ * writable at 0x4Dh register, and set bit 6
+ * of progif to 1:
+ */
+ if (pci_read_config_byte(dev, 0x4d, &confreg0))
+ goto veryspecialsettingserror;
+ if (confreg0 & 0x80)
+ if (pci_write_config_byte(dev, 0x4d, confreg0 & ~0x80))
+ goto veryspecialsettingserror;
+ if (pci_write_config_byte(dev, PCI_CLASS_PROG, progif | 0x40))
+ goto veryspecialsettingserror;
+ if (confreg0 & 0x80)
+ if (pci_write_config_byte(dev, 0x4d, confreg0))
+ errors++;
+ }
+
+ if ((pci_read_config_byte(dev, PCI_CLASS_PROG, &progif)) || (!(progif & 0x40)))
+ goto veryspecialsettingserror;
+
+ printk("%s: enabled read of IDE channels state (en/dis-abled) %s.\n",
+ name, errors ? "with Error(s)" : "Succeeded" );
+ return 0;
+
+veryspecialsettingserror:
+ printk("%s: impossible to enable read of IDE channels state (en/dis-abled)!\n", name);
+ return 0;
+}
+
+int ali15x3_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ switch (func) {
+ case ide_dma_check:
+ if (drive->media == ide_cdrom) {
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ struct hd_driveid *id = drive->id;
+ byte cd_dma_fifo = 0;
+
+ pci_read_config_byte(dev, 0x53, &cd_dma_fifo);
+
+ if (((id->field_valid & 4) || (id->field_valid & 2)) &&
+ (id->capability & 1) && hwif->autodma) {
+ unsigned long dma_set_bit = hwif->dma_base + 2;
+#if 0
+ if (cd_dma_fifo & 0x02)
+ pci_write_config_byte(dev, 0x53, cd_dma_fifo & ~0x02);
+ pci_write_config_byte(dev, 0x53, cd_dma_fifo|0x01);
+#else
+ pci_write_config_byte(dev, 0x53, cd_dma_fifo|0x01|0x02);
+#endif
+ if (drive->select.b.unit & 0x01) {
+ outb(inb(dma_set_bit)|0x40, dma_set_bit);
+ } else {
+ outb(inb(dma_set_bit)|0x20, dma_set_bit);
+ }
+ } else {
+ if (cd_dma_fifo & 0x01)
+ pci_write_config_byte(dev, 0x53, cd_dma_fifo & ~0x01);
+ pci_write_config_byte(dev, 0x53, cd_dma_fifo|0x02);
+ }
+ } else if (drive->media != ide_disk) {
+ return ide_dmaproc(ide_dma_off_quietly, drive);
+ }
+ default:
+ break;
+ }
+ return ide_dmaproc(func, drive); /* use standard DMA stuff */
+}
+
+__initfunc(void ide_init_ali15x3 (ide_hwif_t *hwif))
+{
+ struct pci_dev *dev;
+ byte ideic, inmir;
+ byte irq_routing_table[] = { -1, 9, 3, 10, 4, 5, 7, 6,
+ 1, 11, 0, 12, 0, 14, 0, 15 };
+ hwif->irq = hwif->channel ? 15 : 14;
+ for (dev = pci_devices; dev; dev=dev->next) /* look for ISA bridge */
+ if (dev->vendor==PCI_VENDOR_ID_AL &&
+ dev->device==PCI_DEVICE_ID_AL_M1533)
+ break;
+ if (dev) {
+ pci_read_config_byte(dev, 0x58, &ideic);
+ ideic = ideic & 0x03;
+ if ((hwif->channel && ideic == 0x03) ||
+ (!hwif->channel && !ideic)) {
+ pci_read_config_byte(dev, 0x44, &inmir);
+ inmir = inmir & 0x0f;
+ hwif->irq = irq_routing_table[inmir];
+ } else
+ if (hwif->channel && !(ideic & 0x01)) {
+ pci_read_config_byte(dev, 0x75, &inmir);
+ inmir = inmir & 0x0f;
+ hwif->irq = irq_routing_table[inmir];
+ }
+ }
+#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS)
+ bmide_dev = hwif->pci_dev;
+ ali_display_info = &ali_get_info;
+#endif /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */
+
+ if (hwif->dma_base)
+ hwif->dmaproc = &ali15x3_dmaproc;
+ return;
+}
+
+#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS)
+static int ali_get_info(char *buffer, char **addr, off_t offset, int count, int dummy)
+{
+ byte reg53h, reg5xh, reg5yh, reg5xh1, reg5yh1;
+ unsigned int bibma;
+ byte c0, c1;
+ byte rev, tmp;
+ char *p = buffer;
+ char *q;
+
+ /* fetch rev. */
+ pci_read_config_byte(bmide_dev, 0x08, &rev);
+ if (rev >= 0xc1) /* M1543C or newer */
+ udmaT[7] = " ???";
+ else
+ fifo[3] = " ??? ";
+
+ /* first fetch bibma: */
+ pci_read_config_dword(bmide_dev, 0x20, &bibma);
+ bibma = (bibma & 0xfff0) ;
+ /*
+ * at that point bibma+0x2 et bibma+0xa are byte
+ * registers to investigate:
+ */
+ c0 = inb((unsigned short)bibma + 0x02);
+ c1 = inb((unsigned short)bibma + 0x0a);
+
+ p += sprintf(p,
+ "\n Ali M15x3 Chipset.\n");
+ p += sprintf(p,
+ " ------------------\n");
+ pci_read_config_byte(bmide_dev, 0x78, &reg53h);
+ p += sprintf(p, "PCI Clock: %d.\n", reg53h);
+
+ pci_read_config_byte(bmide_dev, 0x53, &reg53h);
+ p += sprintf(p,
+ "CD_ROM FIFO:%s, CD_ROM DMA:%s\n",
+ (reg53h & 0x02) ? "Yes" : "No ",
+ (reg53h & 0x01) ? "Yes" : "No " );
+ pci_read_config_byte(bmide_dev, 0x74, &reg53h);
+ p += sprintf(p,
+ "FIFO Status: contains %d Words, runs%s%s\n\n",
+ (reg53h & 0x3f),
+ (reg53h & 0x40) ? " OVERWR" : "",
+ (reg53h & 0x80) ? " OVERRD." : "." );
+
+ p += sprintf(p,
+ "-------------------primary channel-------------------secondary channel---------\n\n");
+
+ pci_read_config_byte(bmide_dev, 0x09, &reg53h);
+ p += sprintf(p,
+ "channel status: %s %s\n",
+ (reg53h & 0x20) ? "On " : "Off",
+ (reg53h & 0x10) ? "On " : "Off" );
+
+ p += sprintf(p,
+ "both channels togth: %s %s\n",
+ (c0&0x80) ? "No " : "Yes",
+ (c1&0x80) ? "No " : "Yes" );
+
+ pci_read_config_byte(bmide_dev, 0x76, &reg53h);
+ p += sprintf(p,
+ "Channel state: %s %s\n",
+ channel_status[reg53h & 0x07],
+ channel_status[(reg53h & 0x70) >> 4] );
+
+ pci_read_config_byte(bmide_dev, 0x58, &reg5xh);
+ pci_read_config_byte(bmide_dev, 0x5c, &reg5yh);
+ p += sprintf(p,
+ "Add. Setup Timing: %dT %dT\n",
+ (reg5xh & 0x07) ? (reg5xh & 0x07) : 8,
+ (reg5yh & 0x07) ? (reg5yh & 0x07) : 8 );
+
+ pci_read_config_byte(bmide_dev, 0x59, &reg5xh);
+ pci_read_config_byte(bmide_dev, 0x5d, &reg5yh);
+ p += sprintf(p,
+ "Command Act. Count: %dT %dT\n"
+ "Command Rec. Count: %dT %dT\n\n",
+ (reg5xh & 0x70) ? ((reg5xh & 0x70) >> 4) : 8,
+ (reg5yh & 0x70) ? ((reg5yh & 0x70) >> 4) : 8,
+ (reg5xh & 0x0f) ? (reg5xh & 0x0f) : 16,
+ (reg5yh & 0x0f) ? (reg5yh & 0x0f) : 16 );
+
+ p += sprintf(p,
+ "----------------drive0-----------drive1------------drive0-----------drive1------\n\n");
+ p += sprintf(p,
+ "DMA enabled: %s %s %s %s\n",
+ (c0&0x20) ? "Yes" : "No ",
+ (c0&0x40) ? "Yes" : "No ",
+ (c1&0x20) ? "Yes" : "No ",
+ (c1&0x40) ? "Yes" : "No " );
+
+ pci_read_config_byte(bmide_dev, 0x54, &reg5xh);
+ pci_read_config_byte(bmide_dev, 0x55, &reg5yh);
+ q = "FIFO threshold: %2d Words %2d Words %2d Words %2d Words\n";
+ if (rev < 0xc1) {
+ if ((rev == 0x20) && (pci_read_config_byte(bmide_dev, 0x4f, &tmp), (tmp &= 0x20))) {
+ p += sprintf(p, q, 8, 8, 8, 8);
+ } else {
+ p += sprintf(p, q,
+ (reg5xh & 0x03) + 12,
+ ((reg5xh & 0x30)>>4) + 12,
+ (reg5yh & 0x03) + 12,
+ ((reg5yh & 0x30)>>4) + 12 );
+ }
+ } else {
+ p += sprintf(p, q,
+ (tmp = (reg5xh & 0x03)) ? (tmp << 3) : 4,
+ (tmp = ((reg5xh & 0x30)>>4)) ? (tmp << 3) : 4,
+ (tmp = (reg5yh & 0x03)) ? (tmp << 3) : 4,
+ (tmp = ((reg5yh & 0x30)>>4)) ? (tmp << 3) : 4 );
+ }
+
+#if 0
+ p += sprintf(p,
+ "FIFO threshold: %2d Words %2d Words %2d Words %2d Words\n",
+ (reg5xh & 0x03) + 12,
+ ((reg5xh & 0x30)>>4) + 12,
+ (reg5yh & 0x03) + 12,
+ ((reg5yh & 0x30)>>4) + 12 );
+#endif
+
+ p += sprintf(p,
+ "FIFO mode: %s %s %s %s\n",
+ fifo[((reg5xh & 0x0c) >> 2)],
+ fifo[((reg5xh & 0xc0) >> 6)],
+ fifo[((reg5yh & 0x0c) >> 2)],
+ fifo[((reg5yh & 0xc0) >> 6)] );
+
+ pci_read_config_byte(bmide_dev, 0x5a, &reg5xh);
+ pci_read_config_byte(bmide_dev, 0x5b, &reg5xh1);
+ pci_read_config_byte(bmide_dev, 0x5e, &reg5yh);
+ pci_read_config_byte(bmide_dev, 0x5f, &reg5yh1);
+
+ p += sprintf(p,/*
+ "------------------drive0-----------drive1------------drive0-----------drive1------\n")*/
+ "Dt RW act. Cnt %2dT %2dT %2dT %2dT\n"
+ "Dt RW rec. Cnt %2dT %2dT %2dT %2dT\n\n",
+ (reg5xh & 0x70) ? ((reg5xh & 0x70) >> 4) : 8,
+ (reg5xh1 & 0x70) ? ((reg5xh1 & 0x70) >> 4) : 8,
+ (reg5yh & 0x70) ? ((reg5yh & 0x70) >> 4) : 8,
+ (reg5yh1 & 0x70) ? ((reg5yh1 & 0x70) >> 4) : 8,
+ (reg5xh & 0x0f) ? (reg5xh & 0x0f) : 16,
+ (reg5xh1 & 0x0f) ? (reg5xh1 & 0x0f) : 16,
+ (reg5yh & 0x0f) ? (reg5yh & 0x0f) : 16,
+ (reg5yh1 & 0x0f) ? (reg5yh1 & 0x0f) : 16 );
+
+ p += sprintf(p,
+ "-----------------------------------UDMA Timings--------------------------------\n\n");
+
+ pci_read_config_byte(bmide_dev, 0x56, &reg5xh);
+ pci_read_config_byte(bmide_dev, 0x57, &reg5yh);
+ p += sprintf(p,
+ "UDMA: %s %s %s %s\n"
+ "UDMA timings: %s %s %s %s\n\n",
+ (reg5xh & 0x08) ? "OK" : "No",
+ (reg5xh & 0x80) ? "OK" : "No",
+ (reg5yh & 0x08) ? "OK" : "No",
+ (reg5yh & 0x80) ? "OK" : "No",
+ udmaT[(reg5xh & 0x07)],
+ udmaT[(reg5xh & 0x70) >> 4],
+ udmaT[reg5yh & 0x07],
+ udmaT[(reg5yh & 0x70) >> 4] );
+
+ return p-buffer; /* => must be less than 4k! */
+}
+#endif /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */
diff --git a/drivers/block/blkpg.c b/drivers/block/blkpg.c
new file mode 100644
index 000000000..6f5674072
--- /dev/null
+++ b/drivers/block/blkpg.c
@@ -0,0 +1,290 @@
+/*
+ * Partition table and disk geometry handling
+ *
+ * This obsoletes the partition-handling code in genhd.c:
+ * Userspace can look at a disk in arbitrary format and tell
+ * the kernel what partitions there are on the disk, and how
+ * these should be numbered.
+ * It also allows one to repartition a disk that is being used.
+ *
+ * A single ioctl with lots of subfunctions:
+ *
+ * Device number stuff:
+ * get_whole_disk() (given the device number of a partition, find
+ * the device number of the encompassing disk)
+ * get_all_partitions() (given the device number of a disk, return the
+ * device numbers of all its known partitions)
+ *
+ * Partition stuff:
+ * add_partition()
+ * delete_partition()
+ * test_partition_in_use() (also for test_disk_in_use)
+ *
+ * Geometry stuff:
+ * get_geometry()
+ * set_geometry()
+ * get_bios_drivedata()
+ *
+ * For today, only the partition stuff - aeb, 990515
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h> /* for BLKRASET, ... */
+#include <linux/sched.h> /* for capable() */
+#include <linux/blk.h> /* for set_device_ro() */
+#include <linux/blkpg.h>
+#include <linux/genhd.h>
+#include <linux/swap.h> /* for is_swap_partition() */
+#include <linux/module.h> /* for EXPORT_SYMBOL */
+
+#include <asm/uaccess.h>
+
+/*
+ * What is the data describing a partition?
+ *
+ * 1. a device number (kdev_t)
+ * 2. a starting sector and number of sectors (hd_struct)
+ * given in the part[] array of the gendisk structure for the drive.
+ *
+ * The number of sectors is replicated in the sizes[] array of
+ * the gendisk structure for the major, which again is copied to
+ * the blk_size[][] array.
+ * (However, hd_struct has the number of 512-byte sectors,
+ * g->sizes[] and blk_size[][] have the number of 1024-byte blocks.)
+ * Note that several drives may have the same major.
+ */
+
+/* a linear search, superfluous when dev is a pointer */
+static struct gendisk *get_gendisk(kdev_t dev) {
+ struct gendisk *g;
+ int m = MAJOR(dev);
+
+ for (g = gendisk_head; g; g = g->next)
+ if (g->major == m)
+ break;
+ return g;
+}
+
+/* moved here from md.c - will be discarded later */
+char *partition_name (kdev_t dev) {
+ static char name[40]; /* kdevname returns 32 bytes */
+ /* disk_name requires 32 bytes */
+ struct gendisk *hd = get_gendisk (dev);
+
+ if (!hd) {
+ sprintf (name, "[dev %s]", kdevname(dev));
+ return (name);
+ }
+
+ return disk_name (hd, MINOR(dev), name); /* routine in genhd.c */
+}
+
+/*
+ * Add a partition.
+ *
+ * returns: EINVAL: bad parameters
+ * ENXIO: cannot find drive
+ * EBUSY: proposed partition overlaps an existing one
+ * or has the same number as an existing one
+ * 0: all OK.
+ */
+int add_partition(kdev_t dev, struct blkpg_partition *p) {
+ struct gendisk *g;
+ long long ppstart, pplength;
+ long pstart, plength;
+ int i, drive, first_minor, end_minor, minor;
+
+ /* convert bytes to sectors, check for fit in a hd_struct */
+ ppstart = (p->start >> 9);
+ pplength = (p->length >> 9);
+ pstart = ppstart;
+ plength = pplength;
+ if (pstart != ppstart || plength != pplength
+ || pstart < 0 || plength < 0)
+ return -EINVAL;
+
+ /* find the drive major */
+ g = get_gendisk(dev);
+ if (!g)
+ return -ENXIO;
+
+ /* existing drive? */
+ drive = (MINOR(dev) >> g->minor_shift);
+ first_minor = (drive << g->minor_shift);
+ end_minor = first_minor + g->max_p;
+ if (drive >= g->nr_real)
+ return -ENXIO;
+
+ /* drive and partition number OK? */
+ if (first_minor != MINOR(dev) || p->pno <= 0 || p->pno >= g->max_p)
+ return -EINVAL;
+
+ /* partition number in use? */
+ minor = first_minor + p->pno;
+ if (g->part[minor].nr_sects != 0)
+ return -EBUSY;
+
+ /* overlap? */
+ for (i=first_minor+1; i<end_minor; i++)
+ if (!(pstart+plength <= g->part[i].start_sect ||
+ pstart >= g->part[i].start_sect + g->part[i].nr_sects))
+ return -EBUSY;
+
+ /* all seems OK */
+ g->part[minor].start_sect = pstart;
+ g->part[minor].nr_sects = plength;
+ if (g->sizes)
+ g->sizes[minor] = (plength >> (BLOCK_SIZE_BITS - 9));
+ return 0;
+}
+
+/*
+ * Delete a partition given by partition number
+ *
+ * returns: EINVAL: bad parameters
+ * ENXIO: cannot find partition
+ * EBUSY: partition is busy
+ * 0: all OK.
+ *
+ * Note that the dev argument refers to the entire disk, not the partition.
+ */
+int del_partition(kdev_t dev, struct blkpg_partition *p) {
+ struct gendisk *g;
+ kdev_t devp;
+ int drive, first_minor, minor;
+
+ /* find the drive major */
+ g = get_gendisk(dev);
+ if (!g)
+ return -ENXIO;
+
+ /* drive and partition number OK? */
+ drive = (MINOR(dev) >> g->minor_shift);
+ first_minor = (drive << g->minor_shift);
+ if (first_minor != MINOR(dev) || p->pno <= 0 || p->pno >= g->max_p)
+ return -EINVAL;
+
+ /* existing drive and partition? */
+ minor = first_minor + p->pno;
+ if (drive >= g->nr_real || g->part[minor].nr_sects == 0)
+ return -ENXIO;
+
+ /* partition in use? Incomplete check for now. */
+ devp = MKDEV(MAJOR(dev), minor);
+ if (get_super(devp) || /* mounted? */
+ is_swap_partition(devp))
+ return -EBUSY;
+
+ /* all seems OK */
+ fsync_dev(devp);
+ invalidate_buffers(devp);
+
+ g->part[minor].start_sect = 0;
+ g->part[minor].nr_sects = 0;
+ if (g->sizes)
+ g->sizes[minor] = 0;
+
+ return 0;
+}
+
+int blkpg_ioctl(kdev_t dev, struct blkpg_ioctl_arg *arg)
+{
+ struct blkpg_ioctl_arg a;
+ struct blkpg_partition p;
+ int len;
+
+ if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg)))
+ return -EFAULT;
+
+ switch (a.op) {
+ case BLKPG_ADD_PARTITION:
+ case BLKPG_DEL_PARTITION:
+ len = a.datalen;
+ if (len < sizeof(struct blkpg_partition))
+ return -EINVAL;
+ if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition)))
+ return -EFAULT;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ if (a.op == BLKPG_ADD_PARTITION)
+ return add_partition(dev, &p);
+ else
+ return del_partition(dev, &p);
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * Common ioctl's for block devices
+ */
+
+int blk_ioctl(kdev_t dev, unsigned int cmd, unsigned long arg)
+{
+ int intval;
+
+ switch (cmd) {
+ case BLKROSET:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ if (get_user(intval, (int *)(arg)))
+ return -EFAULT;
+ set_device_ro(dev, intval);
+ return 0;
+ case BLKROGET:
+ intval = (is_read_only(dev) != 0);
+ return put_user(intval, (int *)(arg));
+
+ case BLKRASET:
+ if(!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ if(!dev || arg > 0xff)
+ return -EINVAL;
+ read_ahead[MAJOR(dev)] = arg;
+ return 0;
+ case BLKRAGET:
+ if (!arg)
+ return -EINVAL;
+ return put_user(read_ahead[MAJOR(dev)], (long *) arg);
+
+ case BLKFLSBUF:
+ if(!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ if (!dev)
+ return -EINVAL;
+ fsync_dev(dev);
+ invalidate_buffers(dev);
+ return 0;
+
+ case BLKSSZGET:
+ /* get block device sector size as needed e.g. by fdisk */
+ intval = get_hardsect_size(dev);
+ return put_user(intval, (int *) arg);
+
+#if 0
+ case BLKGETSIZE:
+ /* Today get_gendisk() requires a linear scan;
+ add this when dev has pointer type. */
+ g = get_gendisk(dev);
+ if (!g)
+ longval = 0;
+ else
+ longval = g->part[MINOR(dev)].nr_sects;
+ return put_user(longval, (long *) arg);
+#endif
+#if 0
+ case BLKRRPART: /* Re-read partition tables */
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ return reread_partitions(dev, 1);
+#endif
+
+ case BLKPG:
+ return blkpg_ioctl(dev, (struct blkpg_ioctl_arg *) arg);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+EXPORT_SYMBOL(blk_ioctl);
diff --git a/drivers/block/buddha.c b/drivers/block/buddha.c
new file mode 100644
index 000000000..8086dac56
--- /dev/null
+++ b/drivers/block/buddha.c
@@ -0,0 +1,161 @@
+/*
+ * linux/drivers/block/buddha.c -- Amiga Buddha and Catweasel IDE Driver
+ *
+ * Copyright (C) 1997 by Geert Uytterhoeven
+ *
+ * This driver was written by based on the specifications in README.buddha.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ * TODO:
+ * - test it :-)
+ * - tune the timings using the speed-register
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/zorro.h>
+#include <linux/ide.h>
+
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+
+
+ /*
+ * The Buddha has 2 IDE interfaces, the Catweasel has 3
+ */
+
+#define BUDDHA_NUM_HWIFS 2
+#define CATWEASEL_NUM_HWIFS 3
+
+
+ /*
+ * Bases of the IDE interfaces (relative to the board address)
+ */
+
+#define BUDDHA_BASE1 0x800
+#define BUDDHA_BASE2 0xa00
+#define BUDDHA_BASE3 0xc00
+
+static const u_int buddha_bases[CATWEASEL_NUM_HWIFS] = {
+ BUDDHA_BASE1, BUDDHA_BASE2, BUDDHA_BASE3
+};
+
+
+ /*
+ * Offsets from one of the above bases
+ */
+
+#define BUDDHA_DATA 0x00
+#define BUDDHA_ERROR 0x06 /* see err-bits */
+#define BUDDHA_NSECTOR 0x0a /* nr of sectors to read/write */
+#define BUDDHA_SECTOR 0x0e /* starting sector */
+#define BUDDHA_LCYL 0x12 /* starting cylinder */
+#define BUDDHA_HCYL 0x16 /* high byte of starting cyl */
+#define BUDDHA_SELECT 0x1a /* 101dhhhh , d=drive, hhhh=head */
+#define BUDDHA_STATUS 0x1e /* see status-bits */
+#define BUDDHA_CONTROL 0x11a
+
+static int buddha_offsets[IDE_NR_PORTS] = {
+ BUDDHA_DATA, BUDDHA_ERROR, BUDDHA_NSECTOR, BUDDHA_SECTOR, BUDDHA_LCYL,
+ BUDDHA_HCYL, BUDDHA_SELECT, BUDDHA_STATUS, BUDDHA_CONTROL
+};
+
+
+ /*
+ * Other registers
+ */
+
+#define BUDDHA_IRQ1 0xf00 /* MSB = 1, Harddisk is source of */
+#define BUDDHA_IRQ2 0xf40 /* interrupt */
+#define BUDDHA_IRQ3 0xf80
+
+static const int buddha_irqports[CATWEASEL_NUM_HWIFS] = {
+ BUDDHA_IRQ1, BUDDHA_IRQ2, BUDDHA_IRQ3
+};
+
+#define BUDDHA_IRQ_MR 0xfc0 /* master interrupt enable */
+
+
+ /*
+ * Board information
+ */
+
+static u_long buddha_board = 0;
+static int buddha_num_hwifs = -1;
+
+
+ /*
+ * Check and acknowledge the interrupt status
+ */
+
+static int buddha_ack_intr(ide_hwif_t *hwif)
+{
+ unsigned char ch;
+
+ ch = inb(hwif->io_ports[IDE_IRQ_OFFSET]);
+ if (!(ch & 0x80))
+ return 0;
+ return 1;
+}
+
+
+ /*
+ * Any Buddha or Catweasel boards present?
+ */
+
+static int find_buddha(void)
+{
+ u_int key;
+ const struct ConfigDev *cd;
+
+ buddha_num_hwifs = 0;
+ if ((key = zorro_find(ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA, 0, 0)))
+ buddha_num_hwifs = BUDDHA_NUM_HWIFS;
+ else if ((key = zorro_find(ZORRO_PROD_INDIVIDUAL_COMPUTERS_CATWEASEL, 0,
+ 0)))
+ buddha_num_hwifs = CATWEASEL_NUM_HWIFS;
+ if (key) {
+ cd = zorro_get_board(key);
+ buddha_board = (u_long)cd->cd_BoardAddr;
+ if (buddha_board) {
+ buddha_board = ZTWO_VADDR(buddha_board);
+ /* write to BUDDHA_IRQ_MR to enable the board IRQ */
+ *(char *)(buddha_board+BUDDHA_IRQ_MR) = 0;
+ zorro_config_board(key, 0);
+ }
+ }
+ return buddha_num_hwifs;
+}
+
+
+ /*
+ * Probe for a Buddha or Catweasel IDE interface
+ * We support only _one_ of them, no multiple boards!
+ */
+
+void buddha_init(void)
+{
+ hw_regs_t hw;
+ int i, index;
+
+ if (buddha_num_hwifs < 0 && !find_buddha())
+ return;
+
+ for (i = 0; i < buddha_num_hwifs; i++) {
+ ide_setup_ports(&hw, (ide_ioreg_t)(buddha_board+buddha_bases[i]),
+ buddha_offsets, 0,
+ (ide_ioreg_t)(buddha_board+buddha_irqports[i]),
+ buddha_ack_intr, IRQ_AMIGA_PORTS);
+ index = ide_register_hw(&hw, NULL);
+ if (index != -1)
+ printk("ide%d: %s IDE interface\n", index,
+ buddha_num_hwifs == BUDDHA_NUM_HWIFS ? "Buddha" :
+ "Catweasel");
+ }
+}
diff --git a/drivers/block/cy82c693.c b/drivers/block/cy82c693.c
new file mode 100644
index 000000000..e1e46ea8a
--- /dev/null
+++ b/drivers/block/cy82c693.c
@@ -0,0 +1,432 @@
+/*
+ * linux/drivers/block/cy82c693.c Version 0.33 Jan. 23, 1999
+ *
+ * Copyright (C) 1998, 1999 Andreas S. Krebs (akrebs@altavista.net), Maintainer
+ * Copyright (C) 1998 Andre Hedrick, Integrater
+ *
+ * CYPRESS CY82C693 chipset IDE controller
+ *
+ * The CY82C693 chipset is used on Digital's PC-Alpha 164SX boards.
+ * Writting the driver was quite simple, since most of the job is
+ * done by the generic pci-ide support.
+ * The hard part was finding the CY82C693's datasheet on Cypress's
+ * web page :-(. But Altavista solved this problem :-).
+ *
+ *
+ * Notes:
+ * - I recently got a 16.8G IBM DTTA, so I was able to test it with
+ * a large and fast disk - the results look great, so I'd say the
+ * driver is working fine :-)
+ * hdparm -t reports 8.17 MB/sec at about 6% CPU usage for the DTTA
+ * - this is my first linux driver, so there's probably a lot of room
+ * for optimizations and bug fixing, so feel free to do it.
+ * - use idebus=xx parameter to set PCI bus speed - needed to calc
+ * timings for PIO modes (default will be 40)
+ * - if using PIO mode it's a good idea to set the PIO mode and
+ * 32-bit I/O support (if possible), e.g. hdparm -p2 -c1 /dev/hda
+ * - I had some problems with my IBM DHEA with PIO modes < 2
+ * (lost interrupts) ?????
+ * - first tests with DMA look okay, they seem to work, but there is a
+ * problem with sound - the BusMaster IDE TimeOut should fixed this
+ *
+ *
+ * History:
+ * ASK@1999-01-23: v0.33 made a few minor code clean ups
+ * removed DMA clock speed setting by default
+ * added boot message
+ * ASK@1998-11-01: v0.32 added support to set BusMaster IDE TimeOut
+ * added support to set DMA Controller Clock Speed
+ * ASK@1998-10-31: v0.31 fixed problem with setting to high DMA modes on some drive
+ * ASK@1998-10-29: v0.3 added support to set DMA modes
+ * ASK@1998-10-28: v0.2 added support to set PIO modes
+ * ASK@1998-10-27: v0.1 first version - chipset detection
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+/* the current version */
+#define CY82_VERSION "CY82C693U driver v0.33 99-01-23 Andreas S. Krebs (akrebs@altavista.net)"
+
+/*
+ * The following are used to debug the driver.
+ */
+#define CY82C693_DEBUG_LOGS 0
+#define CY82C693_DEBUG_INFO 0
+
+/* define CY82C693_SETDMA_CLOCK to set DMA Controller Clock Speed to ATCLK */
+#undef CY82C693_SETDMA_CLOCK
+
+/*
+ * note: the value for busmaster timeout is tricky and i got it by trial and error !
+ * using a to low value will cause DMA timeouts and drop IDE performance
+ * using a to high value will cause audio playback to scatter
+ * if you know a better value or how to calc it, please let me know
+ */
+#define BUSMASTER_TIMEOUT 0x50 /* twice the value written in cy82c693ub datasheet */
+/*
+ * the value above was tested on my machine and it seems to work okay
+ */
+
+/* here are the offset definitions for the registers */
+#define CY82_IDE_CMDREG 0x04
+#define CY82_IDE_ADDRSETUP 0x48
+#define CY82_IDE_MASTER_IOR 0x4C
+#define CY82_IDE_MASTER_IOW 0x4D
+#define CY82_IDE_SLAVE_IOR 0x4E
+#define CY82_IDE_SLAVE_IOW 0x4F
+#define CY82_IDE_MASTER_8BIT 0x50
+#define CY82_IDE_SLAVE_8BIT 0x51
+
+#define CY82_INDEX_PORT 0x22
+#define CY82_DATA_PORT 0x23
+
+#define CY82_INDEX_CTRLREG1 0x01
+#define CY82_INDEX_CHANNEL0 0x30
+#define CY82_INDEX_CHANNEL1 0x31
+#define CY82_INDEX_TIMEOUT 0x32
+
+/* the max PIO mode - from datasheet */
+#define CY82C693_MAX_PIO 4
+
+/* the min and max PCI bus speed in MHz - from datasheet */
+#define CY82C963_MIN_BUS_SPEED 25
+#define CY82C963_MAX_BUS_SPEED 33
+
+/* the struct for the PIO mode timings */
+typedef struct pio_clocks_s {
+ byte address_time; /* Address setup (clocks) */
+ byte time_16r; /* clocks for 16bit IOR (0xF0=Active/data, 0x0F=Recovery) */
+ byte time_16w; /* clocks for 16bit IOW (0xF0=Active/data, 0x0F=Recovery) */
+ byte time_8; /* clocks for 8bit (0xF0=Active/data, 0x0F=Recovery) */
+} pio_clocks_t;
+
+/*
+ * calc clocks using bus_speed
+ * returns (rounded up) time in bus clocks for time in ns
+ */
+static int calc_clk (int time, int bus_speed)
+{
+ int clocks;
+
+ clocks = (time*bus_speed+999)/1000 -1;
+
+ if (clocks < 0)
+ clocks = 0;
+
+ if (clocks > 0x0F)
+ clocks = 0x0F;
+
+ return clocks;
+}
+
+/*
+ * compute the values for the clock registers for PIO
+ * mode and pci_clk [MHz] speed
+ *
+ * NOTE: for mode 0,1 and 2 drives 8-bit IDE command control registers are used
+ * for mode 3 and 4 drives 8 and 16-bit timings are the same
+ *
+ */
+static void compute_clocks (byte pio, pio_clocks_t *p_pclk)
+{
+ int clk1, clk2;
+ int bus_speed;
+
+ bus_speed = ide_system_bus_speed(); /* get speed of PCI bus */
+ /* we don't check against CY82C693's min and max speed,
+ * so you can play with the idebus=xx parameter
+ */
+
+ if (pio > CY82C693_MAX_PIO)
+ pio = CY82C693_MAX_PIO;
+
+ /* let's calc the address setup time clocks */
+ p_pclk->address_time = (byte)calc_clk(ide_pio_timings[pio].setup_time, bus_speed);
+
+ /* let's calc the active and recovery time clocks */
+ clk1 = calc_clk(ide_pio_timings[pio].active_time, bus_speed);
+
+ /* calc recovery timing */
+ clk2 = ide_pio_timings[pio].cycle_time -
+ ide_pio_timings[pio].active_time -
+ ide_pio_timings[pio].setup_time;
+
+ clk2 = calc_clk(clk2, bus_speed);
+
+ clk1 = (clk1<<4)|clk2; /* combine active and recovery clocks */
+
+ /* note: we use the same values for 16bit IOR and IOW
+ * those are all the same, since I don't have other
+ * timings than those from ide_modes.h
+ */
+
+ p_pclk->time_16r = (byte)clk1;
+ p_pclk->time_16w = (byte)clk1;
+
+ /* what are good values for 8bit ?? */
+ p_pclk->time_8 = (byte)clk1;
+}
+
+/*
+ * set DMA mode a specific channel for CY82C693
+ */
+static void cy82c693_dma_enable (ide_drive_t *drive, int mode, int single)
+{
+ byte index;
+ byte data;
+
+ if (mode>2) /* make sure we set a valid mode */
+ mode = 2;
+
+ if (mode > drive->id->tDMA) /* to be absolutly sure we have a valid mode */
+ mode = drive->id->tDMA;
+
+ index = (HWIF(drive)->channel==0) ? CY82_INDEX_CHANNEL0 : CY82_INDEX_CHANNEL1;
+
+#if CY82C693_DEBUG_LOGS
+ /* for debug let's show the previous values */
+
+ OUT_BYTE(index, CY82_INDEX_PORT);
+ data = IN_BYTE(CY82_DATA_PORT);
+
+ printk (KERN_INFO "%s (ch=%d, dev=%d): DMA mode is %d (single=%d)\n", drive->name, HWIF(drive)->channel, drive->select.b.unit, (data&0x3), ((data>>2)&1));
+#endif /* CY82C693_DEBUG_LOGS */
+
+ data = (byte)mode|(byte)(single<<2);
+
+ OUT_BYTE(index, CY82_INDEX_PORT);
+ OUT_BYTE(data, CY82_DATA_PORT);
+
+#if CY82C693_DEBUG_INFO
+ printk (KERN_INFO "%s (ch=%d, dev=%d): set DMA mode to %d (single=%d)\n", drive->name, HWIF(drive)->channel, drive->select.b.unit, mode, single);
+#endif /* CY82C693_DEBUG_INFO */
+
+ /*
+ * note: below we set the value for Bus Master IDE TimeOut Register
+ * I'm not absolutly sure what this does, but it solved my problem
+ * with IDE DMA and sound, so I now can play sound and work with
+ * my IDE driver at the same time :-)
+ *
+ * If you know the correct (best) value for this register please
+ * let me know - ASK
+ */
+
+ data = BUSMASTER_TIMEOUT;
+ OUT_BYTE(CY82_INDEX_TIMEOUT, CY82_INDEX_PORT);
+ OUT_BYTE(data, CY82_DATA_PORT);
+
+#if CY82C693_DEBUG_INFO
+ printk (KERN_INFO "%s: Set IDE Bus Master TimeOut Register to 0x%X\n", drive->name, data);
+#endif /* CY82C693_DEBUG_INFO */
+}
+
+/*
+ * used to set DMA mode for CY82C693 (single and multi modes)
+ */
+static int cy82c693_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+{
+ /*
+ * if the function is dma on, set dma mode for drive everything
+ * else is done by the defaul func
+ */
+ if (func == ide_dma_on) {
+ struct hd_driveid *id = drive->id;
+
+#if CY82C693_DEBUG_INFO
+ printk (KERN_INFO "dma_on: %s\n", drive->name);
+#endif /* CY82C693_DEBUG_INFO */
+
+ if (id != NULL) {
+ /* Enable DMA on any drive that has DMA (multi or single) enabled */
+ if (id->field_valid & 2) { /* regular DMA */
+ int mmode, smode;
+
+ mmode = id->dma_mword & (id->dma_mword >> 8);
+ smode = id->dma_1word & (id->dma_1word >> 8);
+
+ if (mmode != 0)
+ cy82c693_dma_enable(drive, (mmode >> 1), 0); /* enable multi */
+ else if (smode != 0)
+ cy82c693_dma_enable(drive, (smode >> 1), 1); /* enable single */
+ }
+ }
+ }
+ return ide_dmaproc(func, drive);
+}
+
+/*
+ * tune ide drive - set PIO mode
+ */
+static void cy82c693_tune_drive (ide_drive_t *drive, byte pio)
+{
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+ pio_clocks_t pclk;
+ unsigned int addrCtrl;
+
+ /* select primary or secondary channel */
+ if (hwif->index > 0) /* drive is on the secondary channel */
+ dev = dev->next;
+
+#if CY82C693_DEBUG_LOGS
+ /* for debug let's show the register values */
+
+ if (drive->select.b.unit == 0) {
+ /*
+ * get master drive registers
+ * address setup control register
+ * is 32 bit !!!
+ */
+ pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
+ addrCtrl &= 0x0F;
+
+ /* now let's get the remaining registers */
+ pci_read_config_byte(dev, CY82_IDE_MASTER_IOR, &pclk.time_16r);
+ pci_read_config_byte(dev, CY82_IDE_MASTER_IOW, &pclk.time_16w);
+ pci_read_config_byte(dev, CY82_IDE_MASTER_8BIT, &pclk.time_8);
+ } else {
+ /*
+ * set slave drive registers
+ * address setup control register
+ * is 32 bit !!!
+ */
+ pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
+
+ addrCtrl &= 0xF0;
+ addrCtrl >>= 4;
+
+ /* now let's get the remaining registers */
+ pci_read_config_byte(dev, CY82_IDE_SLAVE_IOR, &pclk.time_16r);
+ pci_read_config_byte(dev, CY82_IDE_SLAVE_IOW, &pclk.time_16w);
+ pci_read_config_byte(dev, CY82_IDE_SLAVE_8BIT, &pclk.time_8);
+ }
+
+ printk (KERN_INFO "%s (ch=%d, dev=%d): PIO timing is (addr=0x%X, ior=0x%X, iow=0x%X, 8bit=0x%X)\n", drive->name, hwif->channel, drive->select.b.unit, addrCtrl, pclk.time_16r, pclk.time_16w, pclk.time_8);
+#endif /* CY82C693_DEBUG_LOGS */
+
+ /* first let's calc the pio modes */
+ pio = ide_get_best_pio_mode(drive, pio, CY82C693_MAX_PIO, NULL);
+
+#if CY82C693_DEBUG_INFO
+ printk (KERN_INFO "%s: Selected PIO mode %d\n", drive->name, pio);
+#endif /* CY82C693_DEBUG_INFO */
+
+ compute_clocks(pio, &pclk); /* let's calc the values for this PIO mode */
+
+ /* now let's write the clocks registers */
+ if (drive->select.b.unit == 0) {
+ /*
+ * set master drive
+ * address setup control register
+ * is 32 bit !!!
+ */
+ pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
+
+ addrCtrl &= (~0xF);
+ addrCtrl |= (unsigned int)pclk.address_time;
+ pci_write_config_dword(dev, CY82_IDE_ADDRSETUP, addrCtrl);
+
+ /* now let's set the remaining registers */
+ pci_write_config_byte(dev, CY82_IDE_MASTER_IOR, pclk.time_16r);
+ pci_write_config_byte(dev, CY82_IDE_MASTER_IOW, pclk.time_16w);
+ pci_write_config_byte(dev, CY82_IDE_MASTER_8BIT, pclk.time_8);
+
+ addrCtrl &= 0xF;
+ } else {
+ /*
+ * set slave drive
+ * address setup control register
+ * is 32 bit !!!
+ */
+ pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
+
+ addrCtrl &= (~0xF0);
+ addrCtrl |= ((unsigned int)pclk.address_time<<4);
+ pci_write_config_dword(dev, CY82_IDE_ADDRSETUP, addrCtrl);
+
+ /* now let's set the remaining registers */
+ pci_write_config_byte(dev, CY82_IDE_SLAVE_IOR, pclk.time_16r);
+ pci_write_config_byte(dev, CY82_IDE_SLAVE_IOW, pclk.time_16w);
+ pci_write_config_byte(dev, CY82_IDE_SLAVE_8BIT, pclk.time_8);
+
+ addrCtrl >>= 4;
+ addrCtrl &= 0xF;
+ }
+
+#if CY82C693_DEBUG_INFO
+ printk (KERN_INFO "%s (ch=%d, dev=%d): set PIO timing to (addr=0x%X, ior=0x%X, iow=0x%X, 8bit=0x%X)\n", drive->name, hwif->channel, drive->select.b.unit, addrCtrl, pclk.time_16r, pclk.time_16w, pclk.time_8);
+#endif /* CY82C693_DEBUG_INFO */
+}
+
+/*
+ * this function is called during init and is used to setup the cy82c693 chip
+ */
+static void init_cy82c693_chip (struct pci_dev *dev)
+{
+ static int initDone = 0;
+#ifdef CY82C693_SETDMA_CLOCK
+ byte data;
+#endif /* CY82C693_SETDMA_CLOCK */
+
+ if (initDone != 0) /* only perform setup once */
+ return;
+ initDone = 1;
+
+ /* write info about this verion of the driver */
+ printk (KERN_INFO CY82_VERSION "\n");
+
+#ifdef CY82C693_SETDMA_CLOCK
+ /* okay let's set the DMA clock speed */
+
+ OUT_BYTE(CY82_INDEX_CTRLREG1, CY82_INDEX_PORT);
+ data = IN_BYTE(CY82_DATA_PORT);
+
+#if CY82C693_DEBUG_INFO
+ printk (KERN_INFO "CY82U693: Peripheral Configuration Register: 0x%X\n", data);
+#endif /* CY82C693_DEBUG_INFO */
+
+ /*
+ * for some reason sometimes the DMA controller
+ * speed is set to ATCLK/2 ???? - we fix this here
+ *
+ * note: i don't know what causes this strange behaviour,
+ * but even changing the dma speed doesn't solve it :-(
+ * the ide performance is still only half the normal speed
+ *
+ * if anybody knows what goes wrong with my machine, please
+ * let me know - ASK
+ */
+
+ data |= 0x03;
+
+ OUT_BYTE(CY82_INDEX_CTRLREG1, CY82_INDEX_PORT);
+ OUT_BYTE(data, CY82_DATA_PORT);
+
+#if CY82C693_DEBUG_INFO
+ printk (KERN_INFO "CY82U693: New Peripheral Configuration Register: 0x%X\n", data);
+#endif /* CY82C693_DEBUG_INFO */
+
+#endif /* CY82C693_SETDMA_CLOCK */
+}
+
+/*
+ * the init function - called for each ide channel once
+ */
+__initfunc(void ide_init_cy82c693(ide_hwif_t *hwif))
+{
+ hwif->chipset = ide_cy82c693;
+ if (hwif->dma_base)
+ hwif->dmaproc = &cy82c693_dmaproc;
+ hwif->tuneproc = &cy82c693_tune_drive;
+
+ init_cy82c693_chip(hwif->pci_dev);
+}
+
diff --git a/drivers/block/falconide.c b/drivers/block/falconide.c
new file mode 100644
index 000000000..321569eb1
--- /dev/null
+++ b/drivers/block/falconide.c
@@ -0,0 +1,66 @@
+/*
+ * linux/drivers/block/falconide.c -- Atari Falcon IDE Driver
+ *
+ * Created 12 Jul 1997 by Geert Uytterhoeven
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/atarihw.h>
+#include <asm/atariints.h>
+#include <asm/atari_stdma.h>
+
+
+ /*
+ * Base of the IDE interface
+ */
+
+#define ATA_HD_BASE 0xfff00000
+
+ /*
+ * Offsets from the above base
+ */
+
+#define ATA_HD_DATA 0x00
+#define ATA_HD_ERROR 0x05 /* see err-bits */
+#define ATA_HD_NSECTOR 0x09 /* nr of sectors to read/write */
+#define ATA_HD_SECTOR 0x0d /* starting sector */
+#define ATA_HD_LCYL 0x11 /* starting cylinder */
+#define ATA_HD_HCYL 0x15 /* high byte of starting cyl */
+#define ATA_HD_SELECT 0x19 /* 101dhhhh , d=drive, hhhh=head */
+#define ATA_HD_STATUS 0x1d /* see status-bits */
+#define ATA_HD_CONTROL 0x39
+
+static int falconide_offsets[IDE_NR_PORTS] = {
+ ATA_HD_DATA, ATA_HD_ERROR, ATA_HD_NSECTOR, ATA_HD_SECTOR, ATA_HD_LCYL,
+ ATA_HD_HCYL, ATA_HD_SELECT, ATA_HD_STATUS, ATA_HD_CONTROL, -1
+};
+
+
+ /*
+ * Probe for a Falcon IDE interface
+ */
+
+void falconide_init(void)
+{
+ if (MACH_IS_ATARI && ATARIHW_PRESENT(IDE)) {
+ hw_regs_t hw;
+ int index;
+
+ ide_setup_ports(&hw, (ide_ioreg_t)ATA_HD_BASE, falconide_offsets,
+ 0, 0, NULL, IRQ_MFP_IDE);
+ index = ide_register_hw(&hw, NULL);
+
+ if (index != -1)
+ printk("ide%d: Falcon IDE interface\n", index);
+ }
+}
diff --git a/drivers/block/gayle.c b/drivers/block/gayle.c
new file mode 100644
index 000000000..920f2ee0a
--- /dev/null
+++ b/drivers/block/gayle.c
@@ -0,0 +1,169 @@
+/*
+ * linux/drivers/block/gayle.c -- Amiga Gayle IDE Driver
+ *
+ * Created 9 Jul 1997 by Geert Uytterhoeven
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/amigahw.h>
+#include <asm/amigaints.h>
+
+
+ /*
+ * Bases of the IDE interfaces
+ */
+
+#define GAYLE_BASE_4000 0xdd2020 /* A4000/A4000T */
+#define GAYLE_BASE_1200 0xda0000 /* A1200/A600 */
+
+ /*
+ * Offsets from one of the above bases
+ */
+
+#define GAYLE_DATA 0x00
+#define GAYLE_ERROR 0x06 /* see err-bits */
+#define GAYLE_NSECTOR 0x0a /* nr of sectors to read/write */
+#define GAYLE_SECTOR 0x0e /* starting sector */
+#define GAYLE_LCYL 0x12 /* starting cylinder */
+#define GAYLE_HCYL 0x16 /* high byte of starting cyl */
+#define GAYLE_SELECT 0x1a /* 101dhhhh , d=drive, hhhh=head */
+#define GAYLE_STATUS 0x1e /* see status-bits */
+#define GAYLE_CONTROL 0x101a
+
+static int gayle_offsets[IDE_NR_PORTS] = {
+ GAYLE_DATA, GAYLE_ERROR, GAYLE_NSECTOR, GAYLE_SECTOR, GAYLE_LCYL,
+ GAYLE_HCYL, GAYLE_SELECT, GAYLE_STATUS, -1, -1
+};
+
+
+ /*
+ * These are at different offsets from the base
+ */
+
+#define GAYLE_IRQ_4000 0xdd3020 /* MSB = 1, Harddisk is source of */
+#define GAYLE_IRQ_1200 0xda9000 /* interrupt */
+
+
+ /*
+ * Offset of the secondary port for IDE doublers
+ * Note that GAYLE_CONTROL is NOT available then!
+ */
+
+#define GAYLE_NEXT_PORT 0x1000
+
+#ifndef CONFIG_BLK_DEV_IDEDOUBLER
+#define GAYLE_NUM_HWIFS 1
+#define GAYLE_NUM_PROBE_HWIFS GAYLE_NUM_HWIFS
+#define GAYLE_HAS_CONTROL_REG 1
+#else /* CONFIG_BLK_DEV_IDEDOUBLER */
+#define GAYLE_NUM_HWIFS 2
+#define GAYLE_NUM_PROBE_HWIFS (ide_doubler ? GAYLE_NUM_HWIFS : \
+ GAYLE_NUM_HWIFS-1)
+#define GAYLE_HAS_CONTROL_REG (!ide_doubler)
+int ide_doubler = 0; /* support IDE doublers? */
+#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
+
+
+ /*
+ * Check and acknowledge the interrupt status
+ */
+
+static int gayle_ack_intr_a4000(ide_hwif_t *hwif)
+{
+ unsigned char ch;
+
+ ch = inb(hwif->io_ports[IDE_IRQ_OFFSET]);
+ if (!(ch & 0x80))
+ return 0;
+ return 1;
+}
+
+static int gayle_ack_intr_a1200(ide_hwif_t *hwif)
+{
+ unsigned char ch;
+
+ ch = inb(hwif->io_ports[IDE_IRQ_OFFSET]);
+ if (!(ch & 0x80))
+ return 0;
+ (void)inb(hwif->io_ports[IDE_STATUS_OFFSET]);
+ outb(0x7c | (ch & 0x03), hwif->io_ports[IDE_IRQ_OFFSET]);
+ return 1;
+}
+
+ /*
+ * Probe for a Gayle IDE interface (and optionally for an IDE doubler)
+ */
+
+void gayle_init(void)
+{
+ int a4000, i;
+
+ if (!MACH_IS_AMIGA)
+ return;
+
+ if (!(a4000 = AMIGAHW_PRESENT(A4000_IDE)) && !AMIGAHW_PRESENT(A1200_IDE))
+ return;
+
+ for (i = 0; i < GAYLE_NUM_PROBE_HWIFS; i++) {
+ ide_ioreg_t base, ctrlport, irqport;
+ ide_ack_intr_t *ack_intr;
+ hw_regs_t hw;
+ int index;
+
+ if (a4000) {
+ base = (ide_ioreg_t)ZTWO_VADDR(GAYLE_BASE_4000);
+ irqport = (ide_ioreg_t)ZTWO_VADDR(GAYLE_IRQ_4000);
+ ack_intr = gayle_ack_intr_a4000;
+ } else {
+ base = (ide_ioreg_t)ZTWO_VADDR(GAYLE_BASE_1200);
+ irqport = (ide_ioreg_t)ZTWO_VADDR(GAYLE_IRQ_1200);
+ ack_intr = gayle_ack_intr_a1200;
+ }
+
+ if (GAYLE_HAS_CONTROL_REG)
+ ctrlport = base + GAYLE_CONTROL;
+ else
+ ctrlport = 0;
+
+ base += i*GAYLE_NEXT_PORT;
+
+ ide_setup_ports(&hw, base, gayle_offsets,
+ ctrlport, irqport, ack_intr, IRQ_AMIGA_PORTS);
+
+ index = ide_register_hw(&hw, NULL);
+ if (index != -1) {
+ switch (i) {
+ case 0:
+ printk("ide%d: Gayle IDE interface (A%d style)\n", index,
+ a4000 ? 4000 : 1200);
+ break;
+#ifdef CONFIG_BLK_DEV_IDEDOUBLER
+ case 1:
+ printk("ide%d: IDE doubler\n", index);
+ break;
+#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
+ }
+ }
+#if 1 /* TESTING */
+ if (i == 1) {
+ volatile u_short *addr = (u_short *)base;
+ u_short data;
+ printk("+++ Probing for IDE doubler... ");
+ *addr = 0xffff;
+ data = *addr;
+ printk("probe returned 0x%02x (PLEASE REPORT THIS!!)\n", data);
+ }
+#endif /* TESTING */
+ }
+}
diff --git a/drivers/block/hpt343.c b/drivers/block/hpt343.c
new file mode 100644
index 000000000..f759cbf8a
--- /dev/null
+++ b/drivers/block/hpt343.c
@@ -0,0 +1,392 @@
+/*
+ * linux/drivers/block/hpt343.c Version 0.23 May 12, 1999
+ *
+ * Copyright (C) 1998-99 Andre Hedrick
+ * (hedrick@astro.dyer.vanderbilt.edu)
+ *
+ * 00:12.0 Unknown mass storage controller:
+ * Triones Technologies, Inc.
+ * Unknown device 0003 (rev 01)
+ *
+ * hde: UDMA 2 (0x0000 0x0002) (0x0000 0x0010)
+ * hdf: UDMA 2 (0x0002 0x0012) (0x0010 0x0030)
+ * hde: DMA 2 (0x0000 0x0002) (0x0000 0x0010)
+ * hdf: DMA 2 (0x0002 0x0012) (0x0010 0x0030)
+ * hdg: DMA 1 (0x0012 0x0052) (0x0030 0x0070)
+ * hdh: DMA 1 (0x0052 0x0252) (0x0070 0x00f0)
+ *
+ * drive_number
+ * = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ * = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ */
+
+#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 <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include "ide_modes.h"
+
+#ifndef SPLIT_BYTE
+#define SPLIT_BYTE(B,H,L) ((H)=(B>>4), (L)=(B-((B>>4)<<4)))
+#endif
+
+#define HPT343_DEBUG_DRIVE_INFO 0
+#define HPT343_DISABLE_ALL_DMAING 0
+#define HPT343_DMA_DISK_ONLY 0
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+static void hpt343_clear_chipset (ide_drive_t *drive)
+{
+ int drive_number = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ unsigned int reg1 = 0, tmp1 = 0;
+ unsigned int reg2 = 0, tmp2 = 0;
+
+ pci_read_config_dword(HWIF(drive)->pci_dev, 0x44, &reg1);
+ pci_read_config_dword(HWIF(drive)->pci_dev, 0x48, &reg2);
+ tmp1 = ((0x00 << (3*drive_number)) | (reg1 & ~(7 << (3*drive_number))));
+ tmp2 = ((0x00 << drive_number) | reg2);
+ pci_write_config_dword(HWIF(drive)->pci_dev, 0x44, tmp1);
+ pci_write_config_dword(HWIF(drive)->pci_dev, 0x48, tmp2);
+}
+
+static int hpt343_tune_chipset (ide_drive_t *drive, byte speed)
+{
+ int err;
+ byte hi_speed, lo_speed;
+ int drive_number = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ unsigned int reg1 = 0, tmp1 = 0;
+ unsigned int reg2 = 0, tmp2 = 0;
+
+ SPLIT_BYTE(speed, hi_speed, lo_speed);
+
+ if (hi_speed & 7) {
+ hi_speed = (hi_speed & 4) ? 0x01 : 0x10;
+ } else {
+ lo_speed <<= 5;
+ lo_speed >>= 5;
+ }
+
+ pci_read_config_dword(HWIF(drive)->pci_dev, 0x44, &reg1);
+ pci_read_config_dword(HWIF(drive)->pci_dev, 0x48, &reg2);
+ tmp1 = ((lo_speed << (3*drive_number)) | (reg1 & ~(7 << (3*drive_number))));
+ tmp2 = ((hi_speed << drive_number) | reg2);
+ err = ide_wait_cmd(drive, WIN_SETFEATURES, speed, SETFEATURES_XFER, 0, NULL);
+ pci_write_config_dword(HWIF(drive)->pci_dev, 0x44, tmp1);
+ pci_write_config_dword(HWIF(drive)->pci_dev, 0x48, tmp2);
+
+#if HPT343_DEBUG_DRIVE_INFO
+ printk("%s: %s drive%d (0x%04x 0x%04x) (0x%04x 0x%04x)" \
+ " (0x%02x 0x%02x) 0x%04x\n",
+ drive->name, ide_xfer_verbose(speed),
+ drive_number, reg1, tmp1, reg2, tmp2,
+ hi_speed, lo_speed, err);
+#endif /* HPT343_DEBUG_DRIVE_INFO */
+
+ return(err);
+}
+
+/*
+ * This allows the configuration of ide_pci chipset registers
+ * for cards that learn about the drive's UDMA, DMA, PIO capabilities
+ * after the drive is reported by the OS. Initally for designed for
+ * HPT343 UDMA chipset by HighPoint|Triones Technologies, Inc.
+ */
+static int config_chipset_for_dma (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ byte speed = 0x00;
+
+#if HPT343_DISABLE_ALL_DMAING
+ return ((int) ide_dma_off);
+#elif HPT343_DMA_DISK_ONLY
+ if (drive->media != ide_disk)
+ return ((int) ide_dma_off_quietly);
+#endif /* HPT343_DISABLE_ALL_DMAING */
+
+ if (id->dma_ultra & 0x0004) {
+ if (!((id->dma_ultra >> 8) & 4)) {
+ drive->id->dma_ultra &= ~0x0F00;
+ drive->id->dma_ultra |= 0x0404;
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ speed = XFER_UDMA_2;
+ } else if (id->dma_ultra & 0x0002) {
+ if (!((id->dma_ultra >> 8) & 2)) {
+ drive->id->dma_ultra &= ~0x0F00;
+ drive->id->dma_ultra |= 0x0202;
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ speed = XFER_UDMA_1;
+ } else if (id->dma_ultra & 0x0001) {
+ if (!((id->dma_ultra >> 8) & 1)) {
+ drive->id->dma_ultra &= ~0x0F00;
+ drive->id->dma_ultra |= 0x0101;
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ speed = XFER_UDMA_0;
+ } else if (id->dma_mword & 0x0004) {
+ if (!((id->dma_mword >> 8) & 4)) {
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_mword |= 0x0404;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ speed = XFER_MW_DMA_2;
+ } else if (id->dma_mword & 0x0002) {
+ if (!((id->dma_mword >> 8) & 2)) {
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_mword |= 0x0202;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ speed = XFER_MW_DMA_1;
+ } else if (id->dma_mword & 0x0001) {
+ if (!((id->dma_mword >> 8) & 1)) {
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_mword |= 0x0101;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ speed = XFER_MW_DMA_0;
+ } else if (id->dma_1word & 0x0004) {
+ if (!((id->dma_1word >> 8) & 4)) {
+ drive->id->dma_1word &= ~0x0F00;
+ drive->id->dma_1word |= 0x0404;
+ drive->id->dma_mword &= ~0x0F00;
+ }
+ speed = XFER_SW_DMA_2;
+ } else if (id->dma_1word & 0x0002) {
+ if (!((id->dma_1word >> 8) & 2)) {
+ drive->id->dma_1word &= ~0x0F00;
+ drive->id->dma_1word |= 0x0202;
+ drive->id->dma_mword &= ~0x0F00;
+ }
+ speed = XFER_SW_DMA_1;
+ } else if (id->dma_1word & 0x0001) {
+ if (!((id->dma_1word >> 8) & 1)) {
+ drive->id->dma_1word &= ~0x0F00;
+ drive->id->dma_1word |= 0x0101;
+ drive->id->dma_mword &= ~0x0F00;
+ }
+ speed = XFER_SW_DMA_0;
+ } else {
+ return ((int) ide_dma_off_quietly);
+ }
+
+ (void) hpt343_tune_chipset(drive, speed);
+
+ return ((int) ((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+ ((id->dma_mword >> 8) & 7) ? ide_dma_on :
+ ((id->dma_1word >> 8) & 7) ? ide_dma_on :
+ ide_dma_off_quietly);
+}
+
+static void config_chipset_for_pio (ide_drive_t *drive)
+{
+ unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90};
+ unsigned short xfer_pio = drive->id->eide_pio_modes;
+
+ byte timing, speed, pio;
+
+ pio = ide_get_best_pio_mode(drive, 255, 5, NULL);
+
+ if (xfer_pio> 4)
+ xfer_pio = 0;
+
+ if (drive->id->eide_pio_iordy > 0) {
+ for (xfer_pio = 5;
+ xfer_pio>0 &&
+ drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio];
+ xfer_pio--);
+ } else {
+ xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 :
+ (drive->id->eide_pio_modes & 2) ? 0x04 :
+ (drive->id->eide_pio_modes & 1) ? 0x03 : xfer_pio;
+ }
+
+ timing = (xfer_pio >= pio) ? xfer_pio : pio;
+
+ switch(timing) {
+ case 4: speed = XFER_PIO_4;break;
+ case 3: speed = XFER_PIO_3;break;
+ case 2: speed = XFER_PIO_2;break;
+ case 1: speed = XFER_PIO_1;break;
+ default:
+ speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW;
+ break;
+ }
+
+ (void) hpt343_tune_chipset(drive, speed);
+}
+
+#if 0
+static void hpt343_tune_drive (ide_drive_t *drive, byte pio)
+{
+}
+#endif
+
+static int config_drive_xfer_rate (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ ide_dma_action_t dma_func = ide_dma_on;
+
+ if (id && (id->capability & 1) && HWIF(drive)->autodma) {
+ /* Consult the list of known "bad" drives */
+ if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+ dma_func = ide_dma_off;
+ goto fast_ata_pio;
+ }
+ dma_func = ide_dma_off_quietly;
+ if (id->field_valid & 4) {
+ if (id->dma_ultra & 0x0007) {
+ /* Force if Capable UltraDMA */
+ dma_func = config_chipset_for_dma(drive);
+ if ((id->field_valid & 2) &&
+ (dma_func != ide_dma_on))
+ goto try_dma_modes;
+ }
+ } else if (id->field_valid & 2) {
+try_dma_modes:
+ if ((id->dma_mword & 0x0007) ||
+ (id->dma_1word & 0x0007)) {
+ /* Force if Capable regular DMA modes */
+ dma_func = config_chipset_for_dma(drive);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ }
+ } else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+ if (id->eide_dma_time > 150) {
+ goto no_dma_set;
+ }
+ /* Consult the list of known "good" drives */
+ dma_func = config_chipset_for_dma(drive);
+ if (dma_func != ide_dma_on)
+ goto no_dma_set;
+ } else {
+ goto fast_ata_pio;
+ }
+ } else if ((id->capability & 8) || (id->field_valid & 2)) {
+fast_ata_pio:
+ dma_func = ide_dma_off_quietly;
+no_dma_set:
+
+ config_chipset_for_pio(drive);
+ }
+ return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+/*
+ * hpt343_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
+ *
+ * This is specific to the HPT343 UDMA bios-less chipset
+ * and HPT345 UDMA bios chipset (stamped HPT363)
+ * by HighPoint|Triones Technologies, Inc.
+ */
+
+int hpt343_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ switch (func) {
+ case ide_dma_check:
+ hpt343_clear_chipset(drive);
+ return config_drive_xfer_rate(drive);
+#if 0
+ case ide_dma_off:
+ case ide_dma_off_quietly:
+ case ide_dma_on:
+ case ide_dma_check:
+ return config_drive_xfer_rate(drive);
+ case ide_dma_read:
+ case ide_dma_write:
+ case ide_dma_begin:
+ case ide_dma_end:
+ case ide_dma_test_irq:
+#endif
+ default:
+ break;
+ }
+ return ide_dmaproc(func, drive); /* use standard DMA stuff */
+}
+
+/*
+ * If the BIOS does not set the IO base addaress to XX00, 343 will fail.
+ */
+#define HPT343_PCI_INIT_REG 0x80
+
+__initfunc(unsigned int pci_init_hpt343 (struct pci_dev *dev, const char *name))
+{
+ int i;
+ unsigned short cmd;
+ unsigned long hpt343IoBase = dev->base_address[4] & PCI_BASE_ADDRESS_IO_MASK;
+#if 0
+ unsigned char misc10 = inb(hpt343IoBase + 0x0010);
+ unsigned char misc11 = inb(hpt343IoBase + 0x0011);
+#endif
+
+ pci_write_config_byte(dev, HPT343_PCI_INIT_REG, 0x00);
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ pci_write_config_word(dev, PCI_COMMAND, cmd & ~PCI_COMMAND_IO);
+
+ dev->base_address[0] = (hpt343IoBase + 0x20);
+ dev->base_address[1] = (hpt343IoBase + 0x34);
+ dev->base_address[2] = (hpt343IoBase + 0x28);
+ dev->base_address[3] = (hpt343IoBase + 0x3c);
+
+ for(i=0; i<4; i++)
+ dev->base_address[i] |= PCI_BASE_ADDRESS_SPACE_IO;
+
+ /*
+ * Since 20-23 can be assigned and are R/W, we correct them.
+ */
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, dev->base_address[0]);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, dev->base_address[1]);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_2, dev->base_address[2]);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_3, dev->base_address[3]);
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+
+#if 0
+ outb(misc10|0x78, (hpt343IoBase + 0x0010));
+ outb(misc11, (hpt343IoBase + 0x0011));
+#endif
+
+#ifdef DEBUG
+ printk("%s: 0x%02x 0x%02x\n",
+ (pcicmd & PCI_COMMAND_MEMORY) ? "HPT345" : name,
+ inb(hpt343IoBase + 0x0010),
+ inb(hpt343IoBase + 0x0011));
+#endif
+
+ if (cmd & PCI_COMMAND_MEMORY) {
+ if (dev->rom_address) {
+ pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->rom_address | PCI_ROM_ADDRESS_ENABLE);
+ printk(KERN_INFO "HPT345: ROM enabled at 0x%08lx\n", dev->rom_address);
+ }
+ } else {
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20);
+ }
+ return dev->irq;
+}
+
+__initfunc(void ide_init_hpt343 (ide_hwif_t *hwif))
+{
+ if (hwif->dma_base) {
+ unsigned short pcicmd = 0;
+
+ pci_read_config_word(hwif->pci_dev, PCI_COMMAND, &pcicmd);
+ hwif->autodma = (pcicmd & PCI_COMMAND_MEMORY) ? 1 : 0;
+ hwif->dmaproc = &hpt343_dmaproc;
+ }
+}
diff --git a/drivers/block/macide.c b/drivers/block/macide.c
new file mode 100644
index 000000000..2771ea702
--- /dev/null
+++ b/drivers/block/macide.c
@@ -0,0 +1,167 @@
+/*
+ * linux/drivers/block/macide.c -- Macintosh IDE Driver
+ *
+ * Copyright (C) 1998 by Michael Schmitz
+ *
+ * This driver was written based on information obtained from the MacOS IDE
+ * driver binary by Mikael Forselius
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/zorro.h>
+#include <linux/ide.h>
+
+#include <asm/machw.h>
+#include <asm/macintosh.h>
+#include <asm/macints.h>
+
+ /*
+ * Base of the IDE interface (see ATAManager ROM code)
+ */
+
+#define MAC_HD_BASE 0x50f1a000
+
+ /*
+ * Offsets from the above base (scaling 4)
+ */
+
+#define MAC_HD_DATA 0x00
+#define MAC_HD_ERROR 0x04 /* see err-bits */
+#define MAC_HD_NSECTOR 0x08 /* nr of sectors to read/write */
+#define MAC_HD_SECTOR 0x0c /* starting sector */
+#define MAC_HD_LCYL 0x10 /* starting cylinder */
+#define MAC_HD_HCYL 0x14 /* high byte of starting cyl */
+#define MAC_HD_SELECT 0x18 /* 101dhhhh , d=drive, hhhh=head */
+#define MAC_HD_STATUS 0x1c /* see status-bits */
+#define MAC_HD_CONTROL 0x38 /* control/altstatus */
+
+static int macide_offsets[IDE_NR_PORTS] = {
+ MAC_HD_DATA, MAC_HD_ERROR, MAC_HD_NSECTOR, MAC_HD_SECTOR, MAC_HD_LCYL,
+ MAC_HD_HCYL, MAC_HD_SELECT, MAC_HD_STATUS, MAC_HD_CONTROL
+};
+
+ /*
+ * Other registers
+ */
+
+ /*
+ * IDE interrupt status register for both (?) hwifs on Quadra
+ * Initial setting: 0xc
+ * Guessing again:
+ * Bit 0+1: some interrupt flags
+ * Bit 2+3: some interrupt enable
+ * Bit 4: ??
+ * Bit 5: IDE interrupt flag (any hwif)
+ * Bit 6: maybe IDE interrupt enable (any hwif) ??
+ * Bit 7: Any interrupt condition
+ *
+ * Only relevant item: bit 5, to be checked by mac_ack_intr
+ */
+
+#define MAC_HD_ISR 0x101
+
+ /*
+ * IDE interrupt glue - seems to be wired to Nubus, Slot C?
+ * (ROM code disassembly again)
+ * First try: just use Nubus interrupt for Slot C. Have Nubus code call
+ * a wrapper to ide_intr that checks the ISR (see above).
+ * Need to #define IDE_IRQ_NUBUS though.
+ * Alternative method: set a mac_ide_hook function pointer to the wrapper
+ * here and have via_do_nubus call that hook if set.
+ *
+ * Quadra needs the hook, Powerbook can use Nubus slot C.
+ * Checking the ISR on Quadra is done by mac_ack_intr (see Amiga code). mac_ide_intr
+ * mac_ide_intr is obsolete except for providing the hwgroup argument.
+ */
+
+ /* The Mac hwif data, for passing hwgroup to ide_intr */
+static ide_hwif_t *mac_hwif = NULL;
+
+ /* The function pointer used in the Nubus handler */
+void (*mac_ide_intr_hook)(int, void *, struct pt_regs *) = NULL;
+
+ /*
+ * Only purpose: feeds the hwgroup to the main IDE handler.
+ * Obsolete as soon as Nubus code is fixed WRT pseudo slot C int.
+ * (should be the case on Powerbooks)
+ * Alas, second purpose: feed correct irq to IDE handler (I know,
+ * that's cheating) :-(((
+ * Fix needed for interrupt code: accept Nubus ints in the regular
+ * request_irq code, then register Powerbook IDE as Nubus slot C,
+ * Quadra as slot F (F for fictious).
+ */
+void mac_ide_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ ide_intr(mac_hwif->irq, mac_hwif->hwgroup, regs);
+}
+
+ /*
+ * Check the interrupt status
+ *
+ * Note: In 2.0 kernels, there have been timing problems with the
+ * Powerbook IDE interface (BUSY was asserted too long after the
+ * interrupt triggered). Result: repeated errors, recalibrate etc.
+ * Adding a wait loop to read_intr, write_intr and set_geom_intr
+ * fixed the problem (waits in read/write_intr were present for Amiga
+ * already).
+ * Powerbooks were not tested with 2.1 due to lack of FPU emulation
+ * (thanks Apple for using LC040). If the BUSY problem resurfaces in
+ * 2.1, my best bet would be to add the wait loop right here, afterr
+ * checking the interrupt register.
+ */
+
+static int mac_ack_intr(ide_hwif_t *hwif)
+{
+ unsigned char ch;
+
+ ch = inb(hwif->io_ports[IDE_IRQ_OFFSET]);
+ if (!(ch & 0x20))
+ return 0;
+ return 1;
+}
+
+ /*
+ * Probe for a Macintosh IDE interface
+ */
+
+void macide_init(void)
+{
+ hw_regs_t hw;
+ int index = -1;
+
+ if (MACH_IS_MAC) {
+ switch(macintosh_config->ide_type) {
+ case 0:
+ break;
+
+ case MAC_IDE_QUADRA:
+ ide_setup_ports(&hw, (ide_ioreg_t)MAC_HD_BASE, macide_offsets,
+ 0, (ide_ioreg_t)(MAC_HD_BASE+MAC_HD_ISR),
+ mac_ack_intr, IRQ_MAC_NUBUS);
+ index = ide_register_hw(&hw, &mac_hwif);
+ mac_ide_intr_hook = mac_ide_intr;
+ break;
+
+ default:
+ ide_setup_ports(&hw, (ide_ioreg_t)MAC_HD_BASE, macide_offsets,
+ 0, 0, NULL, IRQ_MAC_NUBUS);
+ index = ide_register_hw(&hw, &mac_hwif);
+ break;
+ }
+
+ if (index != -1) {
+ if (macintosh_config->ide_type == MAC_IDE_QUADRA)
+ printk("ide%d: Macintosh Quadra IDE interface\n", index);
+ else
+ printk("ide%d: Macintosh Powerbook IDE interface\n", index);
+ }
+ }
+}
diff --git a/drivers/block/pdc202xx.c b/drivers/block/pdc202xx.c
new file mode 100644
index 000000000..f0591397c
--- /dev/null
+++ b/drivers/block/pdc202xx.c
@@ -0,0 +1,567 @@
+/*
+ * linux/drivers/block/pdc202xx.c Version 0.26 May 12, 1999
+ *
+ * Copyright (C) 1998-99 Andre Hedrick
+ * (hedrick@astro.dyer.vanderbilt.edu)
+ *
+ * Promise Ultra33 cards with BIOS v1.20 through 1.28 will need this
+ * compiled into the kernel if you have more than one card installed.
+ * Note that BIOS v1.29 is reported to fix the problem. Since this is
+ * safe chipset tuning, including this support is harmless
+ *
+ * The latest chipset code will support the following ::
+ * Three Ultra33 controllers and 12 drives.
+ * 8 are UDMA supported and 4 are limited to DMA mode 2 multi-word.
+ * The 8/4 ratio is a BIOS code limit by promise.
+ *
+ * UNLESS you enable "PDC202XX_FORCE_BURST_BIT"
+ *
+ * There is only one BIOS in the three contollers.
+ *
+ * May 8 20:56:17 Orion kernel:
+ * Uniform Multi-Platform E-IDE driver Revision: 6.19
+ * PDC20246: IDE controller on PCI bus 00 dev a0
+ * PDC20246: not 100% native mode: will probe irqs later
+ * PDC20246: ROM enabled at 0xfebd0000
+ * PDC20246: (U)DMA Burst Bit ENABLED Primary PCI Mode Secondary PCI Mode.
+ * ide0: BM-DMA at 0xef80-0xef87, BIOS settings: hda:DMA, hdb:DMA
+ * ide1: BM-DMA at 0xef88-0xef8f, BIOS settings: hdc:pio, hdd:pio
+ * PDC20246: IDE controller on PCI bus 00 dev 98
+ * PDC20246: not 100% native mode: will probe irqs later
+ * PDC20246: ROM enabled at 0xfebc0000
+ * PDC20246: (U)DMA Burst Bit ENABLED Primary PCI Mode Secondary PCI Mode.
+ * ide2: BM-DMA at 0xef40-0xef47, BIOS settings: hde:DMA, hdf:DMA
+ * ide3: BM-DMA at 0xef48-0xef4f, BIOS settings: hdg:DMA, hdh:DMA
+ * PDC20246: IDE controller on PCI bus 00 dev 90
+ * PDC20246: not 100% native mode: will probe irqs later
+ * PDC20246: ROM enabled at 0xfebb0000
+ * PDC20246: (U)DMA Burst Bit DISABLED Primary PCI Mode Secondary PCI Mode.
+ * PDC20246: FORCING BURST BIT 0x00 -> 0x01 ACTIVE
+ * ide4: BM-DMA at 0xef00-0xef07, BIOS settings: hdi:DMA, hdj:pio
+ * ide5: BM-DMA at 0xef08-0xef0f, BIOS settings: hdk:pio, hdl:pio
+ * PIIX3: IDE controller on PCI bus 00 dev 39
+ * PIIX3: device not capable of full native PCI mode
+ *
+ * ide0 at 0xeff0-0xeff7,0xefe6 on irq 19
+ * ide1 at 0xefa8-0xefaf,0xebe6 on irq 19
+ * ide2 at 0xefa0-0xefa7,0xef7e on irq 18
+ * ide3 at 0xef68-0xef6f,0xef66 on irq 18
+ * ide4 at 0xef38-0xef3f,0xef62 on irq 17
+ * hda: QUANTUM FIREBALL ST6.4A, 6149MB w/81kB Cache, CHS=13328/15/63, UDMA(33)
+ * hdb: QUANTUM FIREBALL ST3.2A, 3079MB w/81kB Cache, CHS=6256/16/63, UDMA(33)
+ * hde: Maxtor 72004 AP, 1916MB w/128kB Cache, CHS=3893/16/63, DMA
+ * hdf: Maxtor 71626 A, 1554MB w/64kB Cache, CHS=3158/16/63, DMA
+ * hdi: Maxtor 90680D4, 6485MB w/256kB Cache, CHS=13176/16/63, UDMA(33)
+ * hdj: Maxtor 90680D4, 6485MB w/256kB Cache, CHS=13176/16/63, UDMA(33)
+ *
+ * Promise Ultra66 cards with BIOS v1.11 this
+ * compiled into the kernel if you have more than one card installed.
+ *
+ * PDC20262: IDE controller on PCI bus 00 dev a0
+ * PDC20262: not 100% native mode: will probe irqs later
+ * PDC20262: ROM enabled at 0xfebb0000
+ * PDC20262: (U)DMA Burst Bit ENABLED Primary PCI Mode Secondary PCI Mode.
+ * ide0: BM-DMA at 0xef00-0xef07, BIOS settings: hda:pio, hdb:pio
+ * ide1: BM-DMA at 0xef08-0xef0f, BIOS settings: hdc:pio, hdd:pio
+ *
+ * UDMA 4/2 and UDMA 3/1 only differ by the testing bit 13 in word93.
+ * Chipset timing speeds must be identical
+ *
+ * drive_number
+ * = ((HWIF(drive)->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ * = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ */
+
+#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 <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#define PDC202XX_DEBUG_DRIVE_INFO 0
+#define PDC202XX_DECODE_REGISTER_INFO 0
+#define PDC202XX_FORCE_BURST_BIT 0
+#define PDC202XX_FORCE_MASTER_MODE 0
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+/* A Register */
+#define SYNC_ERRDY_EN 0xC0
+
+#define SYNC_IN 0x80 /* control bit, different for master vs. slave drives */
+#define ERRDY_EN 0x40 /* control bit, different for master vs. slave drives */
+#define IORDY_EN 0x20 /* PIO: IOREADY */
+#define PREFETCH_EN 0x10 /* PIO: PREFETCH */
+
+#define PA3 0x08 /* PIO"A" timing */
+#define PA2 0x04 /* PIO"A" timing */
+#define PA1 0x02 /* PIO"A" timing */
+#define PA0 0x01 /* PIO"A" timing */
+
+/* B Register */
+
+#define MB2 0x80 /* DMA"B" timing */
+#define MB1 0x40 /* DMA"B" timing */
+#define MB0 0x20 /* DMA"B" timing */
+
+#define PB4 0x10 /* PIO_FORCE 1:0 */
+
+#define PB3 0x08 /* PIO"B" timing */ /* PIO flow Control mode */
+#define PB2 0x04 /* PIO"B" timing */ /* PIO 4 */
+#define PB1 0x02 /* PIO"B" timing */ /* PIO 3 half */
+#define PB0 0x01 /* PIO"B" timing */ /* PIO 3 other half */
+
+/* C Register */
+#define IORDYp_NO_SPEED 0x4F
+#define SPEED_DIS 0x0F
+
+#define DMARQp 0x80
+#define IORDYp 0x40
+#define DMAR_EN 0x20
+#define DMAW_EN 0x10
+
+#define MC3 0x08 /* DMA"C" timing */
+#define MC2 0x04 /* DMA"C" timing */
+#define MC1 0x02 /* DMA"C" timing */
+#define MC0 0x01 /* DMA"C" timing */
+
+#if PDC202XX_DECODE_REGISTER_INFO
+
+#define REG_A 0x01
+#define REG_B 0x02
+#define REG_C 0x04
+#define REG_D 0x08
+
+static void decode_registers (byte registers, byte value)
+{
+ byte bit = 0, bit1 = 0, bit2 = 0;
+
+ switch(registers) {
+ case REG_A:
+ bit2 = 0;
+ printk("A Register ");
+ if (value & 0x80) printk("SYNC_IN ");
+ if (value & 0x40) printk("ERRDY_EN ");
+ if (value & 0x20) printk("IORDY_EN ");
+ if (value & 0x10) printk("PREFETCH_EN ");
+ if (value & 0x08) { printk("PA3 ");bit2 |= 0x08; }
+ if (value & 0x04) { printk("PA2 ");bit2 |= 0x04; }
+ if (value & 0x02) { printk("PA1 ");bit2 |= 0x02; }
+ if (value & 0x01) { printk("PA0 ");bit2 |= 0x01; }
+ printk("PIO(A) = %d ", bit2);
+ break;
+ case REG_B:
+ bit1 = 0;bit2 = 0;
+ printk("B Register ");
+ if (value & 0x80) { printk("MB2 ");bit1 |= 0x80; }
+ if (value & 0x40) { printk("MB1 ");bit1 |= 0x40; }
+ if (value & 0x20) { printk("MB0 ");bit1 |= 0x20; }
+ printk("DMA(B) = %d ", bit1 >> 5);
+ if (value & 0x10) printk("PIO_FORCED/PB4 ");
+ if (value & 0x08) { printk("PB3 ");bit2 |= 0x08; }
+ if (value & 0x04) { printk("PB2 ");bit2 |= 0x04; }
+ if (value & 0x02) { printk("PB1 ");bit2 |= 0x02; }
+ if (value & 0x01) { printk("PB0 ");bit2 |= 0x01; }
+ printk("PIO(B) = %d ", bit2);
+ break;
+ case REG_C:
+ bit2 = 0;
+ printk("C Register ");
+ if (value & 0x80) printk("DMARQp ");
+ if (value & 0x40) printk("IORDYp ");
+ if (value & 0x20) printk("DMAR_EN ");
+ if (value & 0x10) printk("DMAW_EN ");
+
+ if (value & 0x08) { printk("MC3 ");bit2 |= 0x08; }
+ if (value & 0x04) { printk("MC2 ");bit2 |= 0x04; }
+ if (value & 0x02) { printk("MC1 ");bit2 |= 0x02; }
+ if (value & 0x01) { printk("MC0 ");bit2 |= 0x01; }
+ printk("DMA(C) = %d ", bit2);
+ break;
+ case REG_D:
+ printk("D Register ");
+ break;
+ default:
+ return;
+ }
+ printk("\n %s ", (registers & REG_D) ? "DP" :
+ (registers & REG_C) ? "CP" :
+ (registers & REG_B) ? "BP" :
+ (registers & REG_A) ? "AP" : "ERROR");
+ for (bit=128;bit>0;bit/=2)
+ printk("%s", (value & bit) ? "1" : "0");
+ printk("\n");
+}
+
+#endif /* PDC202XX_DECODE_REGISTER_INFO */
+
+static int config_chipset_for_dma (ide_drive_t *drive, byte ultra)
+{
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+
+ int err;
+ unsigned int drive_conf;
+ byte drive_pci;
+ byte test1, test2, speed;
+ byte AP, BP, CP, DP, EP;
+ int drive_number = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ byte udma_66 = ((id->word93 & 0x2000) && (dev->device == PCI_DEVICE_ID_PROMISE_20262)) ? 1 : 0;
+ byte udma_33 = ultra ? (inb((dev->base_address[4] & PCI_BASE_ADDRESS_IO_MASK) + 0x001f) & 1) : 0;
+
+ pci_read_config_byte(dev, 0x50, &EP);
+
+ switch(drive_number) {
+ case 0: drive_pci = 0x60;
+ pci_read_config_dword(dev, drive_pci, &drive_conf);
+ if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
+ goto chipset_is_set;
+ pci_read_config_byte(dev, (drive_pci), &test1);
+ if (!(test1 & SYNC_ERRDY_EN))
+ pci_write_config_byte(dev, (drive_pci), test1|SYNC_ERRDY_EN);
+ break;
+ case 1: drive_pci = 0x64;
+ pci_read_config_dword(dev, drive_pci, &drive_conf);
+ if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
+ goto chipset_is_set;
+ pci_read_config_byte(dev, 0x60, &test1);
+ pci_read_config_byte(dev, (drive_pci), &test2);
+ if ((test1 & SYNC_ERRDY_EN) && !(test2 & SYNC_ERRDY_EN))
+ pci_write_config_byte(dev, (drive_pci), test2|SYNC_ERRDY_EN);
+ break;
+ case 2: drive_pci = 0x68;
+ pci_read_config_dword(dev, drive_pci, &drive_conf);
+ if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
+ goto chipset_is_set;
+ pci_read_config_byte(dev, (drive_pci), &test1);
+ if (!(test1 & SYNC_ERRDY_EN))
+ pci_write_config_byte(dev, (drive_pci), test1|SYNC_ERRDY_EN);
+ break;
+ case 3: drive_pci = 0x6c;
+ pci_read_config_dword(dev, drive_pci, &drive_conf);
+ if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4))
+ goto chipset_is_set;
+ pci_read_config_byte(dev, 0x68, &test1);
+ pci_read_config_byte(dev, (drive_pci), &test2);
+ if ((test1 & SYNC_ERRDY_EN) && !(test2 & SYNC_ERRDY_EN))
+ pci_write_config_byte(dev, (drive_pci), test2|SYNC_ERRDY_EN);
+ break;
+ default:
+ return ide_dma_off;
+ }
+
+ if (drive->media != ide_disk)
+ return ide_dma_off_quietly;
+
+ pci_read_config_byte(dev, (drive_pci), &AP);
+ pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+ pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
+ pci_read_config_byte(dev, (drive_pci)|0x03, &DP);
+
+ if (id->capability & 4) { /* IORDY_EN */
+ pci_write_config_byte(dev, (drive_pci), AP|IORDY_EN);
+ pci_read_config_byte(dev, (drive_pci), &AP);
+ }
+
+ if (drive->media == ide_disk) { /* PREFETCH_EN */
+ pci_write_config_byte(dev, (drive_pci), AP|PREFETCH_EN);
+ pci_read_config_byte(dev, (drive_pci), &AP);
+ }
+
+ if ((BP & 0xF0) && (CP & 0x0F)) {
+ /* clear DMA modes of upper 842 bits of B Register */
+ /* clear PIO forced mode upper 1 bit of B Register */
+ pci_write_config_byte(dev, (drive_pci)|0x01, BP & ~0xF0);
+ pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+
+ /* clear DMA modes of lower 8421 bits of C Register */
+ pci_write_config_byte(dev, (drive_pci)|0x02, CP & ~0x0F);
+ pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
+ }
+
+ pci_read_config_byte(dev, (drive_pci), &AP);
+ pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+ pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
+
+ if ((id->dma_ultra & 0x0010) && (udma_66) && (udma_33)) {
+ if (!((id->dma_ultra >> 8) & 16)) {
+ drive->id->dma_ultra &= ~0xFF00;
+ drive->id->dma_ultra |= 0x1010;
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ /* speed 8 == UDMA mode 4 == speed 6 plus cable */
+ pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x20);
+ pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x01);
+ speed = XFER_UDMA_4;
+ } else if ((id->dma_ultra & 0x0008) && (udma_66) && (udma_33)) {
+ if (!((id->dma_ultra >> 8) & 8)) {
+ drive->id->dma_ultra &= ~0xFF00;
+ drive->id->dma_ultra |= 0x0808;
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ /* speed 7 == UDMA mode 3 == speed 5 plus cable */
+ pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x40);
+ pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x02);
+ speed = XFER_UDMA_3;
+ } else if ((id->dma_ultra & 0x0004) && (udma_33)) {
+ if (!((id->dma_ultra >> 8) & 4)) {
+ drive->id->dma_ultra &= ~0x0F00;
+ drive->id->dma_ultra |= 0x0404;
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ /* speed 6 == UDMA mode 2 */
+ pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x20);
+ pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x01);
+ speed = XFER_UDMA_2;
+ } else if ((id->dma_ultra & 0x0002) && (udma_33)) {
+ if (!((id->dma_ultra >> 8) & 2)) {
+ drive->id->dma_ultra &= ~0x0F00;
+ drive->id->dma_ultra |= 0x0202;
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ /* speed 5 == UDMA mode 1 */
+ pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x40);
+ pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x02);
+ speed = XFER_UDMA_1;
+ } else if ((id->dma_ultra & 0x0001) && (udma_33)) {
+ if (!((id->dma_ultra >> 8) & 1)) {
+ drive->id->dma_ultra &= ~0x0F00;
+ drive->id->dma_ultra |= 0x0101;
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ /* speed 4 == UDMA mode 0 */
+ pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x60);
+ pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x03);
+ speed = XFER_UDMA_0;
+ } else if (id->dma_mword & 0x0004) {
+ if (!((id->dma_mword >> 8) & 4)) {
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_mword |= 0x0404;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ /* speed 4 == DMA mode 2 multi-word */
+ pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x60);
+ pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x03);
+ speed = XFER_MW_DMA_2;
+ } else if (id->dma_mword & 0x0002) {
+ if (!((id->dma_mword >> 8) & 2)) {
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_mword |= 0x0202;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ /* speed 3 == DMA mode 1 multi-word */
+ pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x60);
+ pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x04);
+ speed = XFER_MW_DMA_1;
+ } else if (id->dma_mword & 0x0001) {
+ if (!((id->dma_mword >> 8) & 1)) {
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_mword |= 0x0101;
+ drive->id->dma_1word &= ~0x0F00;
+ }
+ /* speed 2 == DMA mode 0 multi-word */
+ pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x60);
+ pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x05);
+ speed = XFER_MW_DMA_0;
+ } else if (id->dma_1word & 0x0004) {
+ if (!((id->dma_1word >> 8) & 4)) {
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+ drive->id->dma_1word |= 0x0404;
+ }
+ /* speed 2 == DMA mode 2 single-word */
+ pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x60);
+ pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x05);
+ speed = XFER_SW_DMA_2;
+ } else if (id->dma_1word & 0x0002) {
+ if (!((id->dma_1word >> 8) & 2)) {
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+ drive->id->dma_1word |= 0x0202;
+ }
+ /* speed 1 == DMA mode 1 single-word */
+ pci_write_config_byte(dev, (drive_pci)|0x01, BP|0x80);
+ pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x06);
+ speed = XFER_SW_DMA_1;
+ } else if (id->dma_1word & 0x0001) {
+ if (!((id->dma_1word >> 8) & 1)) {
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+ drive->id->dma_1word |= 0x0101;
+ }
+ /* speed 0 == DMA mode 0 single-word */
+ pci_write_config_byte(dev, (drive_pci)|0x01, BP|0xC0);
+ pci_write_config_byte(dev, (drive_pci)|0x02, CP|0x0B);
+ speed = XFER_SW_DMA_0;
+ } else {
+ /* restore original pci-config space */
+ pci_write_config_dword(dev, drive_pci, drive_conf);
+ return ide_dma_off_quietly;
+ }
+
+ err = ide_wait_cmd(drive, WIN_SETFEATURES, speed, SETFEATURES_XFER, 0, NULL);
+
+#if PDC202XX_DECODE_REGISTER_INFO
+ pci_read_config_byte(dev, (drive_pci), &AP);
+ pci_read_config_byte(dev, (drive_pci)|0x01, &BP);
+ pci_read_config_byte(dev, (drive_pci)|0x02, &CP);
+
+ decode_registers(REG_A, AP);
+ decode_registers(REG_B, BP);
+ decode_registers(REG_C, CP);
+ decode_registers(REG_D, DP);
+#endif /* PDC202XX_DECODE_REGISTER_INFO */
+
+#if PDC202XX_DEBUG_DRIVE_INFO
+ printk("%s: %s drive%d 0x%08x ",
+ drive->name, ide_xfer_verbose(speed),
+ drive_number, drive_conf);
+ pci_read_config_dword(dev, drive_pci, &drive_conf);
+ printk("0x%08x\n", drive_conf);
+#endif /* PDC202XX_DEBUG_DRIVE_INFO */
+
+chipset_is_set:
+
+ return ((int) ((id->dma_ultra >> 11) & 3) ? ide_dma_on :
+ ((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+ ((id->dma_mword >> 8) & 7) ? ide_dma_on :
+ ((id->dma_1word >> 8) & 7) ? ide_dma_on :
+ ide_dma_off_quietly);
+}
+
+/* 0 1 2 3 4 5 6 7 8
+ * 960, 480, 390, 300, 240, 180, 120, 90, 60
+ * 180, 150, 120, 90, 60
+ * DMA_Speed
+ * 180, 120, 90, 90, 90, 60, 30
+ * 11, 5, 4, 3, 2, 1, 0
+ */
+
+static int config_drive_xfer_rate (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+ ide_dma_action_t dma_func = ide_dma_off_quietly;
+
+ if (id && (id->capability & 1) && hwif->autodma) {
+ /* Consult the list of known "bad" drives */
+ if (ide_dmaproc(ide_dma_bad_drive, drive)) {
+ return HWIF(drive)->dmaproc(ide_dma_off, drive);
+ }
+
+ if (id->field_valid & 4) {
+ if (id->dma_ultra & 0x001F) {
+ /* Force if Capable UltraDMA */
+ dma_func = config_chipset_for_dma(drive, 1);
+ if ((id->field_valid & 2) &&
+ (dma_func != ide_dma_on))
+ goto try_dma_modes;
+ }
+ } else if (id->field_valid & 2) {
+try_dma_modes:
+ if ((id->dma_mword & 0x0004) ||
+ (id->dma_1word & 0x0004)) {
+ /* Force if Capable regular DMA modes */
+ dma_func = config_chipset_for_dma(drive, 0);
+ }
+ } else if ((ide_dmaproc(ide_dma_good_drive, drive)) &&
+ (id->eide_dma_time > 150)) {
+ /* Consult the list of known "good" drives */
+ dma_func = config_chipset_for_dma(drive, 0);
+ }
+ }
+ return HWIF(drive)->dmaproc(dma_func, drive);
+}
+
+/*
+ * pdc202xx_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
+ */
+int pdc202xx_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ switch (func) {
+ case ide_dma_check:
+ return config_drive_xfer_rate(drive);
+ default:
+ break;
+ }
+ return ide_dmaproc(func, drive); /* use standard DMA stuff */
+}
+
+__initfunc(unsigned int pci_init_pdc202xx (struct pci_dev *dev, const char *name))
+{
+ unsigned long high_16 = dev->base_address[4] & PCI_BASE_ADDRESS_IO_MASK;
+ byte udma_speed_flag = inb(high_16 + 0x001f);
+ byte primary_mode = inb(high_16 + 0x001a);
+ byte secondary_mode = inb(high_16 + 0x001b);
+
+ if (dev->rom_address) {
+ pci_write_config_dword(dev, PCI_ROM_ADDRESS, dev->rom_address | PCI_ROM_ADDRESS_ENABLE);
+ printk("%s: ROM enabled at 0x%08lx\n", name, dev->rom_address);
+ }
+
+ if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE) {
+ byte irq = 0, irq2 = 0;
+ pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
+ pci_read_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, &irq2); /* 0xbc */
+ if (irq != irq2) {
+ pci_write_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, irq); /* 0xbc */
+ printk("%s: pci-config space interrupt mirror fixed.\n", name);
+ }
+ }
+
+ printk("%s: (U)DMA Burst Bit %sABLED " \
+ "Primary %s Mode " \
+ "Secondary %s Mode.\n",
+ name,
+ (udma_speed_flag & 1) ? "EN" : "DIS",
+ (primary_mode & 1) ? "MASTER" : "PCI",
+ (secondary_mode & 1) ? "MASTER" : "PCI" );
+
+#if PDC202XX_FORCE_BURST_BIT
+ if (!(udma_speed_flag & 1)) {
+ printk("%s: FORCING BURST BIT 0x%02x -> 0x%02x ", name, udma_speed_flag, (udma_speed_flag|1));
+ outb(udma_speed_flag|1, high_16 + 0x001f);
+ printk("%sCTIVE\n", (inb(high_16 + 0x001f) & 1) ? "A" : "INA");
+ }
+#endif /* PDC202XX_FORCE_BURST_BIT */
+
+#if PDC202XX_FORCE_MASTER_MODE
+ if (!(primary_mode & 1)) {
+ printk("%s: FORCING PRIMARY MODE BIT 0x%02x -> 0x%02x ",
+ name, primary_mode, (primary_mode|1));
+ outb(primary_mode|1, high_16 + 0x001a);
+ printk("%s\n", (inb(high_16 + 0x001a) & 1) ? "MASTER" : "PCI");
+ }
+
+ if (!(secondary_mode & 1)) {
+ printk("%s: FORCING SECONDARY MODE BIT 0x%02x -> 0x%02x ",
+ name, secondary_mode, (secondary_mode|1));
+ outb(secondary_mode|1, high_16 + 0x001b);
+ printk("%s\n", (inb(high_16 + 0x001b) & 1) ? "MASTER" : "PCI");
+ }
+#endif /* PDC202XX_FORCE_MASTER_MODE */
+ return dev->irq;
+}
+
+__initfunc(void ide_init_pdc202xx (ide_hwif_t *hwif))
+{
+ if (hwif->dma_base) {
+ hwif->dmaproc = &pdc202xx_dmaproc;
+ }
+}
diff --git a/drivers/block/piix.c b/drivers/block/piix.c
new file mode 100644
index 000000000..e89e5dfb6
--- /dev/null
+++ b/drivers/block/piix.c
@@ -0,0 +1,294 @@
+/*
+ * linux/drivers/block/piix.c Version 0.23 May 29, 1999
+ *
+ * Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer
+ * Copyright (C) 1998-1999 Andre Hedrick, Author and Maintainer
+ *
+ * PIO mode setting function for Intel chipsets.
+ * For use instead of BIOS settings.
+ *
+ * 40-41
+ * 42-43
+ *
+ * 41
+ * 43
+ *
+ * | PIO 0 | c0 | 80 | 0 | piix_tune_drive(drive, 0);
+ * | PIO 2 | SW2 | d0 | 90 | 4 | piix_tune_drive(drive, 2);
+ * | PIO 3 | MW1 | e1 | a1 | 9 | piix_tune_drive(drive, 3);
+ * | PIO 4 | MW2 | e3 | a3 | b | piix_tune_drive(drive, 4);
+ *
+ * sitre = word40 & 0x4000; primary
+ * sitre = word42 & 0x4000; secondary
+ *
+ * 44 8421|8421 hdd|hdb
+ *
+ * 48 8421 hdd|hdc|hdb|hda udma enabled
+ *
+ * 0001 hda
+ * 0010 hdb
+ * 0100 hdc
+ * 1000 hdd
+ *
+ * 4a 84|21 hdb|hda
+ * 4b 84|21 hdd|hdc
+ *
+ * 00|00 udma 0
+ * 01|01 udma 1
+ * 10|10 udma 2
+ * 11|11 reserved
+ *
+ * pci_read_config_word(HWIF(drive)->pci_dev, 0x40, &reg40);
+ * pci_read_config_word(HWIF(drive)->pci_dev, 0x42, &reg42);
+ * pci_read_config_word(HWIF(drive)->pci_dev, 0x44, &reg44);
+ * pci_read_config_word(HWIF(drive)->pci_dev, 0x48, &reg48);
+ * pci_read_config_word(HWIF(drive)->pci_dev, 0x4a, &reg4a);
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+
+#include <asm/delay.h>
+#include <asm/io.h>
+
+#include "ide_modes.h"
+
+#define PIIX_DEBUG_DRIVE_INFO 0
+
+extern char *ide_xfer_verbose (byte xfer_rate);
+
+/*
+ *
+ */
+static byte piix_dma_2_pio (byte xfer_rate) {
+ switch(xfer_rate) {
+ case XFER_UDMA_4:
+ case XFER_UDMA_3:
+ case XFER_UDMA_2:
+ case XFER_UDMA_1:
+ case XFER_UDMA_0:
+ case XFER_MW_DMA_2:
+ case XFER_PIO_4:
+ return 4;
+ case XFER_MW_DMA_1:
+ case XFER_PIO_3:
+ return 3;
+ case XFER_SW_DMA_2:
+ case XFER_PIO_2:
+ return 2;
+ case XFER_MW_DMA_0:
+ case XFER_SW_DMA_1:
+ case XFER_SW_DMA_0:
+ case XFER_PIO_1:
+ case XFER_PIO_0:
+ case XFER_PIO_SLOW:
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Based on settings done by AMI BIOS
+ * (might be usefull if drive is not registered in CMOS for any reason).
+ */
+static void piix_tune_drive (ide_drive_t *drive, byte pio)
+{
+ unsigned long flags;
+ u16 master_data;
+ byte slave_data;
+ int is_slave = (&HWIF(drive)->drives[1] == drive);
+ int master_port = HWIF(drive)->index ? 0x42 : 0x40;
+ int slave_port = 0x44;
+ /* ISP RTC */
+ byte timings[][2] = { { 0, 0 },
+ { 0, 0 },
+ { 1, 0 },
+ { 2, 1 },
+ { 2, 3 }, };
+
+#if 1
+ pio = ide_get_best_pio_mode(drive, pio, 5, NULL);
+#else
+ pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
+#endif
+ pci_read_config_word(HWIF(drive)->pci_dev, master_port, &master_data);
+ if (is_slave) {
+ master_data = master_data | 0x4000;
+ if (pio > 1)
+ /* enable PPE, IE and TIME */
+ master_data = master_data | 0x0070;
+ pci_read_config_byte(HWIF(drive)->pci_dev, slave_port, &slave_data);
+ slave_data = slave_data & (HWIF(drive)->index ? 0x0f : 0xf0);
+ slave_data = slave_data | ((timings[pio][0] << 2) | (timings[pio][1]
+ << (HWIF(drive)->index ? 4 : 0)));
+ } else {
+ master_data = master_data & 0xccf8;
+ if (pio > 1)
+ /* enable PPE, IE and TIME */
+ master_data = master_data | 0x0007;
+ master_data = master_data | (timings[pio][0] << 12) |
+ (timings[pio][1] << 8);
+ }
+ save_flags(flags);
+ cli();
+ pci_write_config_word(HWIF(drive)->pci_dev, master_port, master_data);
+ if (is_slave)
+ pci_write_config_byte(HWIF(drive)->pci_dev, slave_port, slave_data);
+ restore_flags(flags);
+}
+
+static int piix_config_drive_for_dma(ide_drive_t *drive, int ultra)
+{
+ struct hd_driveid *id = drive->id;
+ ide_hwif_t *hwif = HWIF(drive);
+ struct pci_dev *dev = hwif->pci_dev;
+
+ unsigned long flags;
+ int sitre;
+ short reg4042, reg44, reg48, reg4a;
+ byte speed;
+ int u_speed;
+ byte maslave = hwif->channel ? 0x42 : 0x40;
+ int drive_number = ((hwif->channel ? 2 : 0) + (drive->select.b.unit & 0x01));
+ int a_speed = 2 << (drive_number * 4);
+ int u_flag = 1 << drive_number;
+
+ pci_read_config_word(dev, maslave, &reg4042);
+ sitre = (reg4042 & 0x4000) ? 1 : 0;
+ pci_read_config_word(dev, 0x44, &reg44);
+ pci_read_config_word(dev, 0x48, &reg48);
+ pci_read_config_word(dev, 0x4a, &reg4a);
+
+ save_flags(flags);
+ cli();
+
+ if (id->dma_ultra && (ultra)) {
+ if (!(reg48 & u_flag)) {
+ pci_write_config_word(dev, 0x48, reg48|u_flag);
+ }
+ } else {
+ if (reg48 & u_flag) {
+ pci_write_config_word(dev, 0x48, reg48 & ~u_flag);
+ }
+ }
+
+ if ((id->dma_ultra & 0x0004) && (ultra)) {
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+ if (!((id->dma_ultra >> 8) & 4)) {
+ drive->id->dma_ultra &= ~0x0F00;
+ drive->id->dma_ultra |= 0x0404;
+ }
+ u_speed = 2 << (drive_number * 4);
+ if (!(reg4a & u_speed)) {
+ pci_write_config_word(dev, 0x4a, reg4a|u_speed);
+ }
+ speed = XFER_UDMA_2;
+ } else if ((id->dma_ultra & 0x0002) && (ultra)) {
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+ if (!((id->dma_ultra >> 8) & 2)) {
+ drive->id->dma_ultra &= ~0x0F00;
+ drive->id->dma_ultra |= 0x0202;
+ }
+ u_speed = 1 << (drive_number * 4);
+ if (!(reg4a & u_speed)) {
+ pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
+ pci_write_config_word(dev, 0x4a, reg4a|u_speed);
+ }
+ speed = XFER_UDMA_1;
+ } else if ((id->dma_ultra & 0x0001) && (ultra)) {
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+ if (!((id->dma_ultra >> 8) & 1)) {
+ drive->id->dma_ultra &= ~0x0F00;
+ drive->id->dma_ultra |= 0x0101;
+ }
+ u_speed = 0 << (drive_number * 4);
+ if (!(reg4a & u_speed)) {
+ pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
+ pci_write_config_word(dev, 0x4a, reg4a|u_speed);
+ }
+ speed = XFER_UDMA_0;
+ } else if (id->dma_mword & 0x0004) {
+ if (reg4a & a_speed)
+ pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
+ drive->id->dma_ultra &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+ if (!((id->dma_mword >> 8) & 4)) {
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_mword |= 0x0404;
+ }
+ speed = XFER_MW_DMA_2;
+ } else if (id->dma_mword & 0x0002) {
+ if (reg4a & a_speed)
+ pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
+ drive->id->dma_ultra &= ~0x0F00;
+ drive->id->dma_1word &= ~0x0F00;
+ if (!((id->dma_mword >> 8) & 2)) {
+ drive->id->dma_mword &= ~0x0F00;
+ drive->id->dma_mword |= 0x0202;
+ }
+ speed = XFER_MW_DMA_1;
+ } else if (id->dma_1word & 0x0004) {
+ if (reg4a & a_speed)
+ pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
+ drive->id->dma_ultra &= ~0x0F00;
+ drive->id->dma_mword &= ~0x0F00;
+ if (!((id->dma_1word >> 8) & 4)) {
+ drive->id->dma_1word &= ~0x0F00;
+ drive->id->dma_1word |= 0x0404;
+ }
+ speed = XFER_SW_DMA_2;
+ } else {
+#if 0
+ speed = XFER_PIO_0;
+#else
+ speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL);
+#endif
+ }
+
+ restore_flags(flags);
+ piix_tune_drive(drive, piix_dma_2_pio(speed));
+
+ (void) ide_wait_cmd(drive, WIN_SETFEATURES, speed, SETFEATURES_XFER, 0, NULL);
+
+#if PIIX_DEBUG_DRIVE_INFO
+ printk("%s: %s drive%d ",
+ drive->name,
+ ide_xfer_verbose(speed),
+ drive_number);
+ printk("\n");
+#endif /* PIIX_DEBUG_DRIVE_INFO */
+
+ return ((int) ((id->dma_ultra >> 8) & 7) ? ide_dma_on :
+ ((id->dma_mword >> 8) & 7) ? ide_dma_on :
+ ((id->dma_1word >> 8) & 7) ? ide_dma_on :
+ ide_dma_off_quietly);
+}
+
+static int piix_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+{
+ int ultra = (HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_INTEL_82371AB) ? 1 : 0;
+ switch (func) {
+ case ide_dma_check:
+ return ide_dmaproc((ide_dma_action_t) piix_config_drive_for_dma(drive, ultra), drive);
+ default :
+ break;
+ }
+ /* Other cases are done by generic IDE-DMA code. */
+ return ide_dmaproc(func, drive);
+}
+
+void ide_init_piix (ide_hwif_t *hwif)
+{
+ hwif->tuneproc = &piix_tune_drive;
+ if (hwif->dma_base) {
+ hwif->dmaproc = &piix_dmaproc;
+ }
+}
diff --git a/drivers/char/defkeymap.c b/drivers/char/defkeymap.c
new file mode 100644
index 000000000..5d8b98e55
--- /dev/null
+++ b/drivers/char/defkeymap.c
@@ -0,0 +1,265 @@
+
+/* Do not edit this file! It was automatically generated by */
+/* loadkeys --mktable defkeymap.map > defkeymap.c */
+
+#include <linux/types.h>
+#include <linux/keyboard.h>
+#include <linux/kd.h>
+
+u_short plain_map[NR_KEYS] = {
+ 0xf200, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036,
+ 0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009,
+ 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69,
+ 0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb61, 0xfb73,
+ 0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b,
+ 0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76,
+ 0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf30c,
+ 0xf703, 0xf020, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104,
+ 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307,
+ 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
+ 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03c, 0xf10a,
+ 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
+ 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
+ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+static u_short shift_map[NR_KEYS] = {
+ 0xf200, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e,
+ 0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009,
+ 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49,
+ 0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb41, 0xfb53,
+ 0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a,
+ 0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56,
+ 0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c,
+ 0xf703, 0xf020, 0xf207, 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf10e,
+ 0xf10f, 0xf110, 0xf111, 0xf112, 0xf113, 0xf213, 0xf203, 0xf307,
+ 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
+ 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf10a,
+ 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
+ 0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116,
+ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+static u_short altgr_map[NR_KEYS] = {
+ 0xf200, 0xf200, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200,
+ 0xf07b, 0xf05b, 0xf05d, 0xf07d, 0xf05c, 0xf200, 0xf200, 0xf200,
+ 0xfb71, 0xfb77, 0xf918, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69,
+ 0xfb6f, 0xfb70, 0xf200, 0xf07e, 0xf201, 0xf702, 0xf914, 0xfb73,
+ 0xf917, 0xf919, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf200,
+ 0xf200, 0xf200, 0xf700, 0xf200, 0xfb7a, 0xfb78, 0xf916, 0xfb76,
+ 0xf915, 0xfb6e, 0xfb6d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c,
+ 0xf703, 0xf200, 0xf207, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510,
+ 0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf202, 0xf911,
+ 0xf912, 0xf913, 0xf30b, 0xf90e, 0xf90f, 0xf910, 0xf30a, 0xf90b,
+ 0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516,
+ 0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
+ 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
+ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+static u_short ctrl_map[NR_KEYS] = {
+ 0xf200, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e,
+ 0xf01f, 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf200,
+ 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009,
+ 0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf201, 0xf702, 0xf001, 0xf013,
+ 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200,
+ 0xf007, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016,
+ 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c,
+ 0xf703, 0xf000, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104,
+ 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf204, 0xf307,
+ 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
+ 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf10a,
+ 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
+ 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
+ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+static u_short shift_ctrl_map[NR_KEYS] = {
+ 0xf200, 0xf200, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200,
+ 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009,
+ 0xf00f, 0xf010, 0xf200, 0xf200, 0xf201, 0xf702, 0xf001, 0xf013,
+ 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200,
+ 0xf200, 0xf200, 0xf700, 0xf200, 0xf01a, 0xf018, 0xf003, 0xf016,
+ 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c,
+ 0xf703, 0xf200, 0xf207, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf208, 0xf200, 0xf307,
+ 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
+ 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
+ 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
+ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+static u_short alt_map[NR_KEYS] = {
+ 0xf200, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836,
+ 0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809,
+ 0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869,
+ 0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873,
+ 0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b,
+ 0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876,
+ 0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c,
+ 0xf703, 0xf820, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504,
+ 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf209, 0xf907,
+ 0xf908, 0xf909, 0xf30b, 0xf904, 0xf905, 0xf906, 0xf30a, 0xf901,
+ 0xf902, 0xf903, 0xf900, 0xf310, 0xf206, 0xf200, 0xf83c, 0xf50a,
+ 0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
+ 0xf118, 0xf210, 0xf211, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116,
+ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+static u_short ctrl_alt_map[NR_KEYS] = {
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809,
+ 0xf80f, 0xf810, 0xf200, 0xf200, 0xf201, 0xf702, 0xf801, 0xf813,
+ 0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200,
+ 0xf200, 0xf200, 0xf700, 0xf200, 0xf81a, 0xf818, 0xf803, 0xf816,
+ 0xf802, 0xf80e, 0xf80d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c,
+ 0xf703, 0xf200, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504,
+ 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf200, 0xf307,
+ 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301,
+ 0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf200, 0xf50a,
+ 0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+ 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603,
+ 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c,
+ 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d,
+ 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
+};
+
+ushort *key_maps[MAX_NR_KEYMAPS] = {
+ plain_map, shift_map, altgr_map, 0,
+ ctrl_map, shift_ctrl_map, 0, 0,
+ alt_map, 0, 0, 0,
+ ctrl_alt_map, 0
+};
+
+unsigned int keymap_count = 7;
+
+
+/*
+ * Philosophy: most people do not define more strings, but they who do
+ * often want quite a lot of string space. So, we statically allocate
+ * the default and allocate dynamically in chunks of 512 bytes.
+ */
+
+char func_buf[] = {
+ '\033', '[', '[', 'A', 0,
+ '\033', '[', '[', 'B', 0,
+ '\033', '[', '[', 'C', 0,
+ '\033', '[', '[', 'D', 0,
+ '\033', '[', '[', 'E', 0,
+ '\033', '[', '1', '7', '~', 0,
+ '\033', '[', '1', '8', '~', 0,
+ '\033', '[', '1', '9', '~', 0,
+ '\033', '[', '2', '0', '~', 0,
+ '\033', '[', '2', '1', '~', 0,
+ '\033', '[', '2', '3', '~', 0,
+ '\033', '[', '2', '4', '~', 0,
+ '\033', '[', '2', '5', '~', 0,
+ '\033', '[', '2', '6', '~', 0,
+ '\033', '[', '2', '8', '~', 0,
+ '\033', '[', '2', '9', '~', 0,
+ '\033', '[', '3', '1', '~', 0,
+ '\033', '[', '3', '2', '~', 0,
+ '\033', '[', '3', '3', '~', 0,
+ '\033', '[', '3', '4', '~', 0,
+ '\033', '[', '1', '~', 0,
+ '\033', '[', '2', '~', 0,
+ '\033', '[', '3', '~', 0,
+ '\033', '[', '4', '~', 0,
+ '\033', '[', '5', '~', 0,
+ '\033', '[', '6', '~', 0,
+ '\033', '[', 'M', 0,
+ '\033', '[', 'P', 0,
+};
+
+
+char *funcbufptr = func_buf;
+int funcbufsize = sizeof(func_buf);
+int funcbufleft = 0; /* space left */
+
+char *func_table[MAX_NR_FUNC] = {
+ func_buf + 0,
+ func_buf + 5,
+ func_buf + 10,
+ func_buf + 15,
+ func_buf + 20,
+ func_buf + 25,
+ func_buf + 31,
+ func_buf + 37,
+ func_buf + 43,
+ func_buf + 49,
+ func_buf + 55,
+ func_buf + 61,
+ func_buf + 67,
+ func_buf + 73,
+ func_buf + 79,
+ func_buf + 85,
+ func_buf + 91,
+ func_buf + 97,
+ func_buf + 103,
+ func_buf + 109,
+ func_buf + 115,
+ func_buf + 120,
+ func_buf + 125,
+ func_buf + 130,
+ func_buf + 135,
+ func_buf + 140,
+ func_buf + 145,
+ 0,
+ 0,
+ func_buf + 149,
+ 0,
+};
+
+struct kbdiacr accent_table[MAX_DIACR] = {
+ {'`', 'A', '\300'}, {'`', 'a', '\340'},
+ {'\'', 'A', '\301'}, {'\'', 'a', '\341'},
+ {'^', 'A', '\302'}, {'^', 'a', '\342'},
+ {'~', 'A', '\303'}, {'~', 'a', '\343'},
+ {'"', 'A', '\304'}, {'"', 'a', '\344'},
+ {'O', 'A', '\305'}, {'o', 'a', '\345'},
+ {'0', 'A', '\305'}, {'0', 'a', '\345'},
+ {'A', 'A', '\305'}, {'a', 'a', '\345'},
+ {'A', 'E', '\306'}, {'a', 'e', '\346'},
+ {',', 'C', '\307'}, {',', 'c', '\347'},
+ {'`', 'E', '\310'}, {'`', 'e', '\350'},
+ {'\'', 'E', '\311'}, {'\'', 'e', '\351'},
+ {'^', 'E', '\312'}, {'^', 'e', '\352'},
+ {'"', 'E', '\313'}, {'"', 'e', '\353'},
+ {'`', 'I', '\314'}, {'`', 'i', '\354'},
+ {'\'', 'I', '\315'}, {'\'', 'i', '\355'},
+ {'^', 'I', '\316'}, {'^', 'i', '\356'},
+ {'"', 'I', '\317'}, {'"', 'i', '\357'},
+ {'-', 'D', '\320'}, {'-', 'd', '\360'},
+ {'~', 'N', '\321'}, {'~', 'n', '\361'},
+ {'`', 'O', '\322'}, {'`', 'o', '\362'},
+ {'\'', 'O', '\323'}, {'\'', 'o', '\363'},
+ {'^', 'O', '\324'}, {'^', 'o', '\364'},
+ {'~', 'O', '\325'}, {'~', 'o', '\365'},
+ {'"', 'O', '\326'}, {'"', 'o', '\366'},
+ {'/', 'O', '\330'}, {'/', 'o', '\370'},
+ {'`', 'U', '\331'}, {'`', 'u', '\371'},
+ {'\'', 'U', '\332'}, {'\'', 'u', '\372'},
+ {'^', 'U', '\333'}, {'^', 'u', '\373'},
+ {'"', 'U', '\334'}, {'"', 'u', '\374'},
+ {'\'', 'Y', '\335'}, {'\'', 'y', '\375'},
+ {'T', 'H', '\336'}, {'t', 'h', '\376'},
+ {'s', 's', '\337'}, {'"', 'y', '\377'},
+ {'s', 'z', '\337'}, {'i', 'j', '\377'},
+};
+
+unsigned int accent_table_size = 68;
diff --git a/drivers/char/i2c-parport.c b/drivers/char/i2c-parport.c
new file mode 100644
index 000000000..cafe38f37
--- /dev/null
+++ b/drivers/char/i2c-parport.c
@@ -0,0 +1,149 @@
+/*
+ * I2C driver for parallel port
+ *
+ * Author: Phil Blundell <philb@gnu.org>
+ *
+ * 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 driver implements a simple I2C protocol by bit-twiddling some
+ * signals on the parallel port. Since the outputs on the parallel port
+ * aren't open collector, three lines rather than two are used:
+ *
+ * D0 clock out
+ * D1 data out
+ * BUSY data in
+ */
+
+#include <linux/parport.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/spinlock.h>
+
+#define I2C_DELAY 10
+
+static int debug = 0;
+
+struct parport_i2c_bus
+{
+ struct i2c_bus i2c;
+ struct parport_i2c_bus *next;
+};
+
+static struct parport_i2c_bus *bus_list;
+
+#ifdef __SMP__
+static spinlock_t bus_list_lock = SPIN_LOCK_UNLOCKED;
+#endif
+
+/* software I2C functions */
+
+static void i2c_setlines(struct i2c_bus *bus, int clk, int data)
+{
+ struct parport *p = bus->data;
+ parport_write_data(p, (clk?1:0) | (data?2:0));
+ udelay(I2C_DELAY);
+}
+
+static int i2c_getdataline(struct i2c_bus *bus)
+{
+ struct parport *p = bus->data;
+ return (parport_read_status(p) & PARPORT_STATUS_BUSY) ? 0 : 1;
+}
+
+static struct i2c_bus parport_i2c_bus_template =
+{
+ "...",
+ I2C_BUSID_PARPORT,
+ NULL,
+
+ SPIN_LOCK_UNLOCKED,
+
+ NULL,
+ NULL,
+
+ i2c_setlines,
+ i2c_getdataline,
+ NULL,
+ NULL,
+};
+
+static void i2c_parport_attach(struct parport *port)
+{
+ struct parport_i2c_bus *b = kmalloc(sizeof(struct parport_i2c_bus),
+ GFP_KERNEL);
+ b->i2c = parport_i2c_bus_template;
+ b->i2c.data = port;
+ strncpy(b->i2c.name, port->name, 32);
+ spin_lock(&bus_list_lock);
+ b->next = bus_list;
+ bus_list = b;
+ spin_unlock(&bus_list_lock);
+ i2c_register_bus(&b->i2c);
+ if (debug)
+ printk(KERN_DEBUG "i2c: attached to %s\n", port->name);
+}
+
+static void i2c_parport_detach(struct parport *port)
+{
+ struct parport_i2c_bus *b, *old_b = NULL;
+ spin_lock(&bus_list_lock);
+ b = bus_list;
+ while (b)
+ {
+ if (b->i2c.data == port)
+ {
+ if (old_b)
+ old_b->next = b->next;
+ else
+ bus_list = b->next;
+ i2c_unregister_bus(&b->i2c);
+ kfree(b);
+ break;
+ }
+ old_b = b;
+ b = b->next;
+ }
+ spin_unlock(&bus_list_lock);
+ if (debug)
+ printk(KERN_DEBUG "i2c: detached from %s\n", port->name);
+}
+
+static struct parport_driver parport_i2c_driver =
+{
+ "i2c",
+ i2c_parport_attach,
+ i2c_parport_detach
+};
+
+#ifdef MODULE
+int init_module(void)
+#else
+int __init i2c_parport_init(void)
+#endif
+{
+ printk("I2C: driver for parallel port v0.1 philb@gnu.org\n");
+ parport_register_driver(&parport_i2c_driver);
+ return 0;
+}
+
+#ifdef MODULE
+MODULE_PARM(debug, "i");
+
+void cleanup_module(void)
+{
+ struct parport_i2c_bus *b = bus_list;
+ while (b)
+ {
+ struct parport_i2c_bus *next = b->next;
+ i2c_unregister_bus(&b->i2c);
+ kfree(b);
+ b = next;
+ }
+ parport_unregister_driver(&parport_i2c_driver);
+}
+#endif
diff --git a/drivers/i2o/Config.in b/drivers/i2o/Config.in
new file mode 100644
index 000000000..d6ae26f64
--- /dev/null
+++ b/drivers/i2o/Config.in
@@ -0,0 +1,12 @@
+mainmenu_option next_comment
+comment 'I2O device support'
+
+tristate 'I2O support' CONFIG_I2O
+
+dep_tristate 'I2O PCI support' CONFIG_I2O_PCI $CONFIG_I2O
+dep_tristate 'I2O Block OSM' CONFIG_I2O_BLOCK $CONFIG_I2O
+dep_tristate 'I2O LAN OSM' CONFIG_I2O_LAN $CONFIG_I2O
+dep_tristate 'I2O SCSI OSM' CONFIG_I2O_SCSI $CONFIG_I2O
+dep_tristate 'I2O /proc support' CONFIG_I2O_PROC $CONFIG_I2O
+
+endmenu
diff --git a/drivers/i2o/Makefile b/drivers/i2o/Makefile
new file mode 100644
index 000000000..d70b42310
--- /dev/null
+++ b/drivers/i2o/Makefile
@@ -0,0 +1,75 @@
+#
+# Makefile for the kernel I2O OSM.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now inherited from the
+# parent makefile.
+#
+
+#
+# Note : at this point, these files are compiled on all systems.
+# In the future, some of these should be built conditionally.
+#
+
+SUB_DIRS :=
+MOD_SUB_DIRS := $(SUB_DIRS)
+ALL_SUB_DIRS := $(SUB_DIRS)
+
+
+L_TARGET := i2o.a
+L_OBJS :=
+M_OBJS :=
+
+ifeq ($(CONFIG_I2O_PCI),y)
+L_OBJS += i2o_pci.o
+else
+ ifeq ($(CONFIG_I2O_PCI),m)
+ M_OBJS += i2o_pci.o
+ endif
+endif
+
+ifeq ($(CONFIG_I2O),y)
+LX_OBJS += i2o_core.o i2o_config.o
+else
+ ifeq ($(CONFIG_I2O),m)
+ MX_OBJS += i2o_core.o i2o_config.o
+ endif
+endif
+
+ifeq ($(CONFIG_I2O_BLOCK),y)
+LX_OBJS += i2o_block.o
+else
+ ifeq ($(CONFIG_I2O_BLOCK),m)
+ MX_OBJS += i2o_block.o
+ endif
+endif
+
+ifeq ($(CONFIG_I2O_LAN),y)
+LX_OBJS += i2o_lan.o
+else
+ ifeq ($(CONFIG_I2O_LAN),m)
+ MX_OBJS += i2o_lan.o
+ endif
+endif
+
+ifeq ($(CONFIG_I2O_SCSI),y)
+LX_OBJS += i2o_scsi.o
+else
+ ifeq ($(CONFIG_I2O_SCSI),m)
+ MX_OBJS += i2o_scsi.o
+ endif
+endif
+
+ifeq ($(CONFIG_I2O_PROC),y)
+LX_OBJS += i2o_proc.o
+else
+ ifeq ($(CONFIG_I2O_PROC),m)
+ MX_OBJS += i2o_proc.o
+ endif
+endif
+
+include $(TOPDIR)/Rules.make
+
diff --git a/drivers/i2o/README b/drivers/i2o/README
new file mode 100644
index 000000000..4e6d2c16d
--- /dev/null
+++ b/drivers/i2o/README
@@ -0,0 +1,78 @@
+
+ Linux I2O Support (c) Copyright 1999 Red Hat Software
+ and others.
+
+ 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.
+
+AUTHORS (so far)
+
+Alan Cox, Building Number Three Ltd.
+ Core code, SCSI and Block OSMs
+
+Steve Ralston, LSI Logic Corp.
+ Debugging SCSI and Block OSM
+
+Deepak Saxena, Intel Corp.
+ /proc interface, bug fixes
+ Ioctl interfaces for control
+
+Philip Rumpf
+ Fixed assorted dumb SMP locking bugs
+
+Juha Sievanen, University Of Helsinki Finland
+ LAN OSM
+ Bug fixes
+ Core code extensions
+
+CREDITS
+
+ This work was made possible by
+
+Red Hat Software
+ Funding for the Building #3 part of the project
+
+Symbios Logic (Now LSI)
+ Host adapters, hints, known to work platforms when I hit
+ compatibility problems
+
+BoxHill Corporation
+ Loan of initial FibreChannel disk array used for development work.
+
+STATUS:
+
+o The core setup works within limits.
+o The scsi layer seems to almost work. I'm still chasing down the hang
+ bug.
+o The block OSM is fairly minimal but does seem to work.
+
+
+TO DO:
+
+General:
+o Support multiple IOP's and tell them about each other
+o Provide hidden address space if asked
+o Long term message flow control
+o PCI IOP's without interrupts are not supported yet
+o Push FAIL handling into the core
+o DDM control interfaces for module load etc
+
+Block:
+o Real error handler
+o Multiple major numbers
+o Read ahead and cache handling stuff. Talk to Ingo and people
+o Power management
+o Finish Media changers
+
+SCSI:
+o Find the right way to associate drives/luns/busses
+
+Net:
+o Port the existing RCPCI work to the frame work or write a new
+ driver. This one is with the Finns
+
+Tape:
+o Anyone seen anything implementing this ?
+
diff --git a/drivers/i2o/README.ioctl b/drivers/i2o/README.ioctl
new file mode 100644
index 000000000..501c93af9
--- /dev/null
+++ b/drivers/i2o/README.ioctl
@@ -0,0 +1,398 @@
+
+Linux I2O User Space Interface
+rev 0.3 - 04/20/99
+
+=============================================================================
+Originally written by Deepak Saxena(deepak.saxena@intel.com)
+Currently maintained by Deepak Saxena(deepak.saxena@intel.com)
+=============================================================================
+
+I. Introduction
+
+The Linux I2O susbsytem provides a set of ioctl() commands than can be
+utilized by user space applications to communicate with IOPs and devices
+on individual IOPs. This document defines the specific ioctl() commands
+that are available to the user and provides examples of their uses.
+
+This document assumes the reader is familiar with or has access to the
+I2O specification as no I2O message parameters are outlined. For information
+on the specification, see http://www.i2osig.org
+
+This document and the I2O user space interface are currently maintained
+by Deepak Saxena. Please send all comments, errata, and bug fixes to
+deepak.saxena@intel.com
+
+II. IOP Access
+
+Access to the I2O subsystem is provided through the device file named
+/dev/i2octl. This file is a character file with major number 10 and minor
+number 166. It can be created through the following command:
+
+ mknod /dev/i2octl c 10 166
+
+III. Determining the IOP Count
+
+ SYNOPSIS
+
+ ioctl(fd, I2OGETIOPS, int *count);
+
+ u8 count[MAX_I2O_CONTROLLERS];
+
+ DESCRIPTION
+
+ This function returns the system's active IOP table. count should
+ point to a buffer containing MAX_I2O_CONTROLLERS entries. Upon
+ returning, each entry will contain a non-zero value if the given
+ IOP unit is active, and NULL if it is inactive or non-existent.
+
+ RETURN VALUE.
+
+ Returns 0 if no errors occur, and -1 otherwise. If an error occurs,
+ errno is set appropriately:
+
+ EIO Unkown error
+
+IV. ExecHrtGet Message
+
+ SYNOPSIS
+
+ ioctl(fd, I2OHRTGET, struct i2o_cmd_hrt *hrt);
+
+ struct i2o_cmd_hrtlct
+ {
+ u32 iop; /* IOP unit number */
+ void *resbuf; /* Buffer for result */
+ u32 *reslen; /* Buffer length in bytes */
+ };
+
+ DESCRIPTION
+
+ This function posts an ExecHrtHet message to the IOP specified by
+ hrt->iop and returns the data in the buffer pointed to by hrt->buf
+ The size of the data written is placed into the memory pointed to
+ by hrt->len.
+
+ RETURNS
+
+ This function returns 0 if no errors occur. If an error occurs, -1
+ is returned and errno is set appropriately:
+
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+ ENOBUFS Buffer not large enough. If this occurs, the required
+ buffer length is written into *(hrt->reslen)
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ EIO Unkown error
+
+V. ExecLctNotify Message
+
+ SYNOPSIS
+
+ ioctl(fd, I2OLCTGET, struct i2o_cmd_lct *lct);
+
+ struct i2o_cmd_hrtlct
+ {
+ u32 iop; /* IOP unit number */
+ void *resbuf; /* Buffer for result */
+ u32 *reslen; /* Buffer length in bytes */
+ };
+
+ DESCRIPTION
+
+ This function posts an ExecLctGet message to the IOP specified by
+ lct->iop and returns the data in the buffer pointed to by lct->buf
+ The size of the data written is placed into the memory pointed to
+ by lct->reslen.
+
+ RETURNS
+
+ This function returns 0 if no errors occur. If an error occurs, -1
+ is returned and errno is set appropriately:
+
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+ ENOBUFS Buffer not large enough. If this occurs, the required
+ buffer length is written into *(lct->reslen)
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ EIO Unkown error
+
+VI. UtilParamsSet Message
+
+ SYNOPSIS
+
+ ioctl(fd, I2OPARMSET, struct i2o_parm_setget *ops);
+
+ struct i2o_cmd_psetget
+ {
+ u32 iop; /* IOP unit number */
+ u32 tid; /* Target device TID */
+ void *opbuf; /* Operation List buffer */
+ u32 oplen; /* Operation List buffer length in bytes */
+ void *resbuf; /* Result List buffer */
+ u32 *reslen; /* Result List buffer length in bytes */
+ };
+
+ DESCRIPTION
+
+ This function posts a UtilParamsSet message to the device identified
+ by ops->iop and ops->tid. The operation list for the message is
+ sent through the ops->oplen buffer, and the result list is written
+ into the buffer pointed to by ops->oplen. The number of bytes
+ written is placed into *(ops->reslen).
+
+ RETURNS
+
+ The return value is the size in bytes of the data written into
+ ops->resbuf if no errors occur. If an error occurs, -1 is returned
+ and errno is set appropriatly:
+
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+ ENOBUFS Buffer not large enough. If this occurs, the required
+ buffer length is written into *(ops->reslen)
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ EIO Unkown error
+
+ A return value of 0 does not mean that the value was actually
+ changed properly on the IOP. The user should check the result
+ list to determine the specific status of the transaction.
+
+VII. UtilParamsGet Message
+
+ SYNOPSIS
+
+ ioctl(fd, I2OPARMGET, struct i2o_parm_setget *ops);
+
+ struct i2o_parm_setget
+ {
+ u32 iop; /* IOP unit number */
+ u32 tid; /* Target device TID */
+ void *opbuf; /* Operation List buffer */
+ u32 oplen; /* Operation List buffer length in bytes */
+ void *resbuf; /* Result List buffer */
+ u32 *reslen; /* Result List buffer length in bytes */
+ };
+
+ DESCRIPTION
+
+ This function posts a UtilParamsGet message to the device identified
+ by ops->iop and ops->tid. The operation list for the message is
+ sent through the ops->oplen buffer, and the result list is written
+ into the buffer pointed to by ops->oplen. The actual size of data
+ written is placed into *(ops->reslen).
+
+ RETURNS
+
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+ ENOBUFS Buffer not large enough. If this occurs, the required
+ buffer length is written into *(ops->reslen)
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ EIO Unkown error
+
+ A return value of 0 does not mean that the value was actually
+ properly retreived. The user should check the result list
+ to determine the specific status of the transaction.
+
+VIII. ExecSwDownload Message
+
+ SYNOPSIS
+
+ ioctl(fd, I2OSWDL, struct i2o_sw_xfer *sw);
+
+ struct i2o_sw_xfer
+ {
+ u32 iop; /* IOP unit number */
+ u8 dl_flags; /* DownLoadFlags field */
+ u8 sw_type; /* Software type */
+ u32 sw_id; /* Software ID */
+ void *buf; /* Pointer to software buffer */
+ u32 *swlen; /* Length of software data */
+ u32 *maxfrag; /* Number of fragments */
+ u32 *curfrag; /* Current fragment number */
+ };
+
+ DESCRIPTION
+
+ This function downloads the software pointed to by sw->buf to the
+ iop identified by sw->iop. The DownloadFlags, SwID, and SwType fields
+ of the ExecSwDownload message are filed in with the values of
+ sw->dl_flags, sw->sw_id, and sw->sw_type.
+
+ Once the ioctl() is called and software transfer begins, the
+ user can read the value *(sw->maxfrag) and *(sw->curfrag) to
+ determine the status of the software transfer. As the IOP
+ is very slow when it comes to SW transfers, this can be
+ used by a separate thread to report status to the user. The
+ user _should not_ write to this memory location until the ioctl()
+ has returned.
+
+ RETURNS
+
+ This function returns 0 no errors occur. If an error occurs, -1
+ is returned and errno is set appropriatly:
+
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+ ENOBUFS Buffer not large enough. If this occurs, the required
+ buffer length is written into *(ops->reslen)
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ EIO Unkown error
+
+IX. ExecSwUpload Message
+
+ SYNOPSIS
+
+ ioctl(fd, I2OSWUL, struct i2o_sw_xfer *sw);
+
+ struct i2o_sw_xfer
+ {
+ u32 iop; /* IOP unit number */
+ u8 flags; /* Unused */
+ u8 sw_type; /* Software type */
+ u32 sw_id; /* Software ID */
+ void *buf; /* Pointer to software buffer */
+ u32 *swlen; /* Length in bytes of software */
+ u32 *maxfrag; /* Number of fragments */
+ u32 *curfrag; /* Current fragment number */
+ };
+
+ DESCRIPTION
+
+ This function uploads software from the IOP identified by sw->iop
+ and places it in the buffer pointed to by sw->buf. The SwID, SwType
+ and SwSize fields of the ExecSwDownload message are filed in
+ with the values of sw->sw_id, sw->sw_type, sw->swlen, and. The
+ actual size of the module is written into *(sw->buflen).
+
+ Once the ioctl() is called and software transfer begins, the
+ user can read the value *(sw->maxfrag) and *(sw->curfrag) to
+ determine the status of the software transfer. As the IOP
+ is very slow when it comes to SW transfers, this can be
+ used by a separate thread to report status to the user. The
+ user _should not_ write to this memory location until the ioctl()
+ has returned.
+
+ RETURNS
+
+ This function returns 0 if no errors occur. If an error occurs, -1
+ is returned and errno is set appropriatly:
+
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+ ENOBUFS Buffer not large enough. If this occurs, the required
+ buffer length is written into *(ops->reslen)
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ EIO Unkown error
+
+X. ExecSwRemove Message
+
+ SYNOPSIS
+
+ ioctl(fd, I2OSWDEL, struct i2o_sw_xfer *sw);
+
+ struct i2o_sw_xfer
+ {
+ u32 iop; /* IOP unit number */
+ u8 flags; /* Unused */
+ u8 sw_type; /* Software type */
+ u32 sw_id; /* Software ID */
+ void *buf; /* Unused */
+ u32 *swlen; /* Length in bytes of software data */
+ u32 *maxfrag; /* Unused */
+ u32 *curfrag; /* Unused */
+ };
+
+ DESCRIPTION
+
+ This function uploads software from the IOP identified by sw->iop
+ and places it in the buffer pointed to by sw->buf. The SwID, SwType
+ and SwSize fields of the ExecSwDownload message are filed in
+ with the values of sw->dl_flags, sw->sw_id, and sw->sw_type. The
+ actual size of the module is written into *(sw->buflen).
+
+ RETURNS
+
+ This function returns 0 if no errors occur. If an error occurs, -1
+ is returned and errno is set appropriatly:
+
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+ ENOBUFS Buffer not large enough. If this occurs, the required
+ buffer length is written into *(ops->reslen)
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ EIO Unkown error
+
+X. UtilConfigDialog Message
+
+ SYNOPSIS
+
+ ioctl(fd, I2OHTML, struct i2o_html *htquery);
+
+ struct i2o_html
+ {
+ u32 iop; /* IOP unit number */
+ u32 tid; /* Target device ID */
+ u32 page; /* HTML page */
+ void *resbuf; /* Buffer for reply HTML page */
+ u32 *reslen; /* Length in bytes of reply buffer */
+ void *qbuf; /* Pointer to HTTP query string */
+ u32 qlen; /* Length in bytes of query string buffer */
+ };
+
+ DESCRIPTION
+
+ This function posts an UtilConfigDialog message to the device identified
+ by htquery->iop and htquery->tid. The requested HTML page number is
+ provided by the htquery->page field, and the resultant data is stored
+ in the buffer pointed to by htquery->resbuf. If there is an HTTP query
+ string that is to be sent to the device, it should be sent in the buffer
+ pointed to by htquery->qbuf. If there is no query string, this field
+ should be set to NULL. The actual size of the reply received is written
+ into *(htquery->reslen)
+
+ RETURNS
+
+ This function returns 0 if no error occur. If an error occurs, -1
+ is returned and J errno is set appropriatly:
+
+ ETIMEDOUT Timeout waiting for reply message
+ ENOMEM Kernel memory allocation error
+ ENOBUFS Buffer not large enough. If this occurs, the required
+ buffer length is written into *(ops->reslen)
+ EFAULT Invalid user space pointer was passed
+ ENXIO Invalid IOP number
+ EIO Unkown error
+
+XI. Events
+
+ In the process of determining this. Current idea is to have use
+ the select() interface to allow user apps to periodically poll
+ the /dev/i2octl device for events. When select() notifies the user
+ that an event is available, the user would call read() to retrieve
+ a list of all the events that are pending for the specific device.
+
+=============================================================================
+Revision History
+=============================================================================
+
+Rev 0.1 - 04/01/99
+- Initial revision
+
+Rev 0.2 - 04/06/99
+- Changed return values to match UNIX ioctl() standard. Only return values
+ are 0 and -1. All errors are reported through errno.
+- Added summary of proposed possible event interfaces
+
+Rev 0.3 - 04/20/99
+- Changed all ioctls() to use pointers to user data instead of actual data
+- Updated error values to match the code
+
+
diff --git a/drivers/i2o/README.lan b/drivers/i2o/README.lan
new file mode 100644
index 000000000..1d1ba0f14
--- /dev/null
+++ b/drivers/i2o/README.lan
@@ -0,0 +1,38 @@
+
+ Linux I2O LAN OSM
+ (c) University of Helsinki, Department of Computer Science
+
+ 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.
+
+AUTHORS
+Auvo Häkkinen, Auvo.Hakkinen@cs.Helsinki.FI
+Juha Sievänen, Juha.Sievanen@cs.Helsinki.FI
+
+CREDITS
+
+ This work was made possible by
+
+European Committee
+ Funding for the project
+
+SysKonnect
+ Loaning of FDDI cards
+
+ASUSTeK
+ I2O motherboard
+
+STATUS:
+o The FDDI part of LAN OSM is working to some extent.
+o Only packet per bucket is now supported.
+
+TO DO:
+
+LAN:
+o Add support for bactches
+o Find why big packets flow from I2O box out, but don't want to come in
+o Find the bug in i2o_set_multicast_list(), which kills interrupt
+ handler in i2o_wait_reply()
+o Add support for Ethernet, Token Ring, AnyLAN, Fibre Channel
diff --git a/drivers/i2o/i2o_block.c b/drivers/i2o/i2o_block.c
new file mode 100644
index 000000000..5d543b1cc
--- /dev/null
+++ b/drivers/i2o/i2o_block.c
@@ -0,0 +1,1071 @@
+/*
+ * I2O block device driver.
+ *
+ * (C) Copyright 1999 Red Hat Software
+ *
+ * Written by Alan Cox, Building Number Three Ltd
+ *
+ * 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 is an initial test release. Most of the good code was taken
+ * from the nbd driver by Pavel Machek, who in turn took some of it
+ * from loop.c. Isn't free software great for reusability 8)
+ *
+ * Fixes:
+ * Steve Ralston: Multiple device handling error fixes,
+ * Added a queue depth.
+ */
+
+#include <linux/major.h>
+
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/file.h>
+#include <linux/ioctl.h>
+#include <linux/i2o.h>
+#include <linux/blkdev.h>
+#include <linux/malloc.h>
+#include <linux/hdreg.h>
+
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+
+#define MAJOR_NR I2O_MAJOR
+
+#include <linux/blk.h>
+
+#define MAX_I2OB 16
+
+#define MAX_I2OB_DEPTH 4
+
+/*
+ * Some of these can be made smaller later
+ */
+
+static int i2ob_blksizes[MAX_I2OB<<4];
+static int i2ob_hardsizes[MAX_I2OB<<4];
+static int i2ob_sizes[MAX_I2OB<<4];
+static int i2ob_media_change_flag[MAX_I2OB];
+static u32 i2ob_max_sectors[MAX_I2OB<<4];
+
+static int i2ob_context;
+
+#ifdef __SMP__
+static spinlock_t i2ob_lock = SPIN_LOCK_UNLOCKED;
+#endif
+
+struct i2ob_device
+{
+ struct i2o_controller *controller;
+ struct i2o_device *i2odev;
+ int tid;
+ int flags;
+ int refcnt;
+ struct request *head, *tail;
+ int done_flag;
+};
+
+/*
+ * Each I2O disk is one of these.
+ */
+
+static struct i2ob_device i2ob_dev[MAX_I2OB<<4];
+static int i2ob_devices = 0;
+static struct hd_struct i2ob[MAX_I2OB<<4];
+static struct gendisk i2ob_gendisk; /* Declared later */
+
+static atomic_t queue_depth; /* For flow control later on */
+
+#define DEBUG( s )
+/* #define DEBUG( s ) printk( s )
+ */
+
+static int i2ob_install_device(struct i2o_controller *, struct i2o_device *, int);
+static void i2ob_end_request(struct request *);
+static void do_i2ob_request(void);
+
+/*
+ * Get a message
+ */
+
+static u32 i2ob_get(struct i2ob_device *dev)
+{
+ struct i2o_controller *c=dev->controller;
+ return I2O_POST_READ32(c);
+}
+
+/*
+ * Turn a Linux block request into an I2O block read/write.
+ */
+
+static int i2ob_send(u32 m, struct i2ob_device *dev, struct request *req, u32 base, int unit)
+{
+ struct i2o_controller *c = dev->controller;
+ int tid = dev->tid;
+ u32 *msg;
+ u32 *mptr;
+ u64 offset;
+ struct buffer_head *bh = req->bh;
+ static int old_qd = 2;
+ int count = req->nr_sectors<<9;
+
+ /*
+ * Build a message
+ */
+
+ msg = bus_to_virt(c->mem_offset + m);
+
+ msg[2] = i2ob_context|(unit<<8);
+ msg[3] = (u32)req; /* 64bit issue again here */
+ msg[5] = req->nr_sectors << 9;
+
+ /* This can be optimised later - just want to be sure its right for
+ starters */
+ offset = ((u64)(req->sector+base)) << 9;
+ msg[6] = offset & 0xFFFFFFFF;
+ msg[7] = (offset>>32);
+ mptr=msg+8;
+
+ if(req->cmd == READ)
+ {
+ msg[1] = I2O_CMD_BLOCK_READ<<24|HOST_TID<<12|tid;
+ /* We don't yet do cache/readahead and other magic */
+ msg[4] = 1<<16;
+ while(bh!=NULL)
+ {
+ *mptr++ = 0x10000000|(bh->b_size);
+ *mptr++ = virt_to_bus(bh->b_data);
+ count -= bh->b_size;
+ bh = bh->b_reqnext;
+ }
+ }
+ else if(req->cmd == WRITE)
+ {
+ msg[1] = I2O_CMD_BLOCK_WRITE<<24|HOST_TID<<12|tid;
+ msg[4] = 1<<16;
+ while(bh!=NULL)
+ {
+ *mptr++ = 0x14000000|(bh->b_size);
+ count -= bh->b_size;
+ *mptr++ = virt_to_bus(bh->b_data);
+ bh = bh->b_reqnext;
+ }
+ }
+ mptr[-2]|= 0xC0000000;
+ msg[0] = I2O_MESSAGE_SIZE(mptr-msg) | SGL_OFFSET_8;
+
+ if(req->current_nr_sectors > 8)
+ printk("Gathered sectors %ld.\n",
+ req->current_nr_sectors);
+
+ if(count != 0)
+ {
+ printk("Request count botched by %d.\n", count);
+ msg[5] -= count;
+ }
+
+// printk("Send for %p\n", req);
+
+ i2o_post_message(c,m);
+ atomic_inc(&queue_depth);
+ if(atomic_read(&queue_depth)>old_qd)
+ {
+ old_qd=atomic_read(&queue_depth);
+ printk("Depth now %d.\n", old_qd);
+ }
+ return 0;
+}
+
+/*
+ * Remove a request from the _locked_ request list. We update both the
+ * list chain and if this is the last item the tail pointer.
+ */
+
+static void i2ob_unhook_request(struct i2ob_device *dev, struct request *req)
+{
+ struct request **p = &dev->head;
+ struct request *nt = NULL;
+ static int crap = 0;
+
+ while(*p!=NULL)
+ {
+ if(*p==req)
+ {
+ if(dev->tail==req)
+ dev->tail = nt;
+ *p=req->next;
+ return;
+ }
+ nt=*p;
+ p=&(nt->next);
+ }
+ if(!crap++)
+ printk("i2o_block: request queue corrupt!\n");
+}
+
+/*
+ * Request completion handler
+ */
+
+static void i2ob_end_request(struct request *req)
+{
+ /*
+ * Loop until all of the buffers that are linked
+ * to this request have been marked updated and
+ * unlocked.
+ */
+ while (end_that_request_first( req, !req->errors, "i2o block" ));
+
+ /*
+ * It is now ok to complete the request.
+ */
+ end_that_request_last( req );
+}
+
+
+/*
+ * OSM reply handler. This gets all the message replies
+ */
+
+static void i2o_block_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *msg)
+{
+ struct request *req;
+ u8 st;
+ u32 *m = (u32 *)msg;
+ u8 unit = (m[2]>>8)&0xF0; /* low 4 bits are partition */
+
+ if(m[0] & (1<<13))
+ {
+ printk("IOP fail.\n");
+ printk("From %d To %d Cmd %d.\n",
+ (m[1]>>12)&0xFFF,
+ m[1]&0xFFF,
+ m[1]>>24);
+ printk("Failure Code %d.\n", m[4]>>24);
+ if(m[4]&(1<<16))
+ printk("Format error.\n");
+ if(m[4]&(1<<17))
+ printk("Path error.\n");
+ if(m[4]&(1<<18))
+ printk("Path State.\n");
+ if(m[4]&(1<<18))
+ printk("Congestion.\n");
+
+ m=(u32 *)bus_to_virt(m[7]);
+ printk("Failing message is %p.\n", m);
+
+ /* We need to up the request failure count here and maybe
+ abort it */
+ req=(struct request *)m[3];
+ /* Now flush the message by making it a NOP */
+ m[0]&=0x00FFFFFF;
+ m[0]|=(I2O_CMD_UTIL_NOP)<<24;
+ i2o_post_message(c,virt_to_bus(m));
+
+ }
+ else
+ {
+ if(m[2]&0x80000000)
+ {
+ int * ptr = (int *)m[3];
+ if(m[4]>>24)
+ *ptr = -1;
+ else
+ *ptr = 1;
+ return;
+ }
+ /*
+ * Lets see what is cooking. We stuffed the
+ * request in the context.
+ */
+
+ req=(struct request *)m[3];
+ st=m[4]>>24;
+
+ if(st!=0)
+ {
+ printk(KERN_ERR "i2ob: error %08X\n", m[4]);
+ /*
+ * Now error out the request block
+ */
+ req->errors++;
+ }
+ }
+ /*
+ * Dequeue the request.
+ */
+
+ spin_lock(&io_request_lock);
+ spin_lock(&i2ob_lock);
+ i2ob_unhook_request(&i2ob_dev[unit], req);
+ i2ob_end_request(req);
+
+ /*
+ * We may be able to do more I/O
+ */
+
+ atomic_dec(&queue_depth);
+ do_i2ob_request();
+ spin_unlock(&i2ob_lock);
+ spin_unlock(&io_request_lock);
+}
+
+static struct i2o_handler i2o_block_handler =
+{
+ i2o_block_reply,
+ "I2O Block OSM",
+ 0
+};
+
+
+/*
+ * Flush all pending requests as errors. Must call with the queue
+ * locked.
+ */
+
+#if 0
+static void i2ob_clear_queue(struct i2ob_device *dev)
+{
+ struct request *req;
+
+ while (1) {
+ req = dev->tail;
+ if (!req)
+ return;
+ req->errors++;
+ i2ob_end_request(req);
+
+ if (dev->tail == dev->head)
+ dev->head = NULL;
+ dev->tail = dev->tail->next;
+ }
+}
+#endif
+
+/*
+ * The I2O block driver is listed as one of those that pulls the
+ * front entry off the queue before processing it. This is important
+ * to remember here. If we drop the io lock then CURRENT will change
+ * on us. We must unlink CURRENT in this routine before we return, if
+ * we use it.
+ */
+
+static void do_i2ob_request(void)
+{
+ struct request *req;
+ int unit;
+ struct i2ob_device *dev;
+ u32 m;
+
+ while (CURRENT) {
+ /*
+ * On an IRQ completion if there is an inactive
+ * request on the queue head it means it isnt yet
+ * ready to dispatch.
+ */
+ if(CURRENT->rq_status == RQ_INACTIVE)
+ return;
+
+ /*
+ * Queue depths probably belong with some kind of
+ * generic IOP commit control. Certainly its not right
+ * its global!
+ */
+ if(atomic_read(&queue_depth)>=MAX_I2OB_DEPTH)
+ break;
+
+ req = CURRENT;
+ unit = MINOR(req->rq_dev);
+ dev = &i2ob_dev[(unit&0xF0)];
+ /* Get a message */
+ m = i2ob_get(dev);
+ /* No messages -> punt
+ FIXME: if we have no messages, and there are no messages
+ we deadlock now. Need a timer/callback ?? */
+ if(m==0xFFFFFFFF)
+ {
+ printk("i2ob: no messages!\n");
+ break;
+ }
+ req->errors = 0;
+ CURRENT = CURRENT->next;
+ req->next = NULL;
+
+ if (dev->head == NULL) {
+ dev->head = req;
+ dev->tail = req;
+ } else {
+ dev->tail->next = req;
+ dev->tail = req;
+ }
+ i2ob_send(m, dev, req, i2ob[unit].start_sect, (unit&0xF0));
+ }
+}
+
+static void i2ob_request(void)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&i2ob_lock, flags);
+ do_i2ob_request();
+ spin_unlock_irqrestore(&i2ob_lock, flags);
+}
+
+/*
+ * SCSI-CAM for ioctl geometry mapping
+ * Duplicated with SCSI - this should be moved into somewhere common
+ * perhaps genhd ?
+ */
+
+static void i2o_block_biosparam(
+ unsigned long capacity,
+ unsigned short *cyls,
+ unsigned char *hds,
+ unsigned char *secs)
+{
+ unsigned long heads, sectors, cylinders, temp;
+
+ cylinders = 1024L; /* Set number of cylinders to max */
+ sectors = 62L; /* Maximize sectors per track */
+
+ temp = cylinders * sectors; /* Compute divisor for heads */
+ heads = capacity / temp; /* Compute value for number of heads */
+ if (capacity % temp) { /* If no remainder, done! */
+ heads++; /* Else, increment number of heads */
+ temp = cylinders * heads; /* Compute divisor for sectors */
+ sectors = capacity / temp; /* Compute value for sectors per
+ track */
+ if (capacity % temp) { /* If no remainder, done! */
+ sectors++; /* Else, increment number of sectors */
+ temp = heads * sectors; /* Compute divisor for cylinders */
+ cylinders = capacity / temp;/* Compute number of cylinders */
+ }
+ }
+ /* if something went wrong, then apparently we have to return
+ a geometry with more than 1024 cylinders */
+ if (cylinders == 0 || heads > 255 || sectors > 63 || cylinders >1023)
+ {
+ unsigned long temp_cyl;
+
+ heads = 64;
+ sectors = 32;
+ temp_cyl = capacity / (heads * sectors);
+ if (temp_cyl > 1024)
+ {
+ heads = 255;
+ sectors = 63;
+ }
+ cylinders = capacity / (heads * sectors);
+ }
+ *cyls = (unsigned int) cylinders; /* Stuff return values */
+ *secs = (unsigned int) sectors;
+ *hds = (unsigned int) heads;
+}
+
+/*
+ * Rescan the partition tables
+ */
+
+static int do_i2ob_revalidate(kdev_t dev, int maxu)
+{
+ int minor=MINOR(dev);
+ int i;
+
+ minor&=0xF0;
+
+ i2ob_dev[minor].refcnt++;
+ if(i2ob_dev[minor].refcnt>maxu+1)
+ {
+ i2ob_dev[minor].refcnt--;
+ return -EBUSY;
+ }
+
+ for( i = 15; i>=0 ; i--)
+ {
+ int m = minor+i;
+ kdev_t d = MKDEV(MAJOR_NR, m);
+ struct super_block *sb = get_super(d);
+
+ sync_dev(d);
+ if(sb)
+ invalidate_inodes(sb);
+ invalidate_buffers(d);
+ i2ob_gendisk.part[m].start_sect = 0;
+ i2ob_gendisk.part[m].nr_sects = 0;
+ }
+
+ /*
+ * Do a physical check and then reconfigure
+ */
+
+ i2ob_install_device(i2ob_dev[minor].controller, i2ob_dev[minor].i2odev,
+ minor);
+ i2ob_dev[minor].refcnt--;
+ return 0;
+}
+
+/*
+ * Issue device specific ioctl calls.
+ */
+
+static int i2ob_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct i2ob_device *dev;
+ int minor;
+
+ /* Anyone capable of this syscall can do *real bad* things */
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (!inode)
+ return -EINVAL;
+ minor = MINOR(inode->i_rdev);
+ if (minor >= (MAX_I2OB<<4))
+ return -ENODEV;
+
+ dev = &i2ob_dev[minor];
+ switch (cmd) {
+ case BLKRASET:
+ if(!capable(CAP_SYS_ADMIN)) return -EACCES;
+ if(arg > 0xff) return -EINVAL;
+ read_ahead[MAJOR(inode->i_rdev)] = arg;
+ return 0;
+
+ case BLKRAGET:
+ if (!arg) return -EINVAL;
+ return put_user(read_ahead[MAJOR(inode->i_rdev)],
+ (long *) arg);
+ case BLKGETSIZE:
+ return put_user(i2ob[minor].nr_sects, (long *) arg);
+
+ case BLKFLSBUF:
+ if(!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ fsync_dev(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ return 0;
+
+ case HDIO_GETGEO:
+ {
+ struct hd_geometry g;
+ int u=minor&0xF0;
+ i2o_block_biosparam(i2ob_sizes[u]<<1,
+ &g.cylinders, &g.heads, &g.sectors);
+ g.start = i2ob[minor].start_sect;
+ return copy_to_user((void *)arg,&g, sizeof(g))?-EFAULT:0;
+ }
+
+ case BLKRRPART:
+ if(!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ return do_i2ob_revalidate(inode->i_rdev,1);
+
+ default:
+ return blk_ioctl(inode->i_rdev, cmd, arg);
+ }
+}
+
+/*
+ * Issue UTIL_CLAIM messages
+ */
+
+static int i2ob_claim_device(struct i2ob_device *dev, int onoff)
+{
+ return i2o_issue_claim(dev->controller, dev->tid, i2ob_context, onoff, &dev->done_flag);
+}
+
+/*
+ * Close the block device down
+ */
+
+static int i2ob_release(struct inode *inode, struct file *file)
+{
+ struct i2ob_device *dev;
+ int minor;
+
+ minor = MINOR(inode->i_rdev);
+ if (minor >= (MAX_I2OB<<4))
+ return -ENODEV;
+ sync_dev(inode->i_rdev);
+ dev = &i2ob_dev[(minor&0xF0)];
+ if (dev->refcnt <= 0)
+ printk(KERN_ALERT "i2ob_release: refcount(%d) <= 0\n", dev->refcnt);
+ dev->refcnt--;
+ if(dev->refcnt==0)
+ {
+ /*
+ * Flush the onboard cache on unmount
+ */
+ u32 msg[5];
+ int *query_done = &dev->done_flag;
+ msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|dev->tid;
+ msg[2] = i2ob_context|0x80000000;
+ msg[3] = (u32)query_done;
+ msg[4] = 60<<16;
+ i2o_post_wait(dev->controller, dev->tid, msg, 20, query_done,2);
+ /*
+ * Unlock the media
+ */
+ msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = I2O_CMD_BLOCK_MUNLOCK<<24|HOST_TID<<12|dev->tid;
+ msg[2] = i2ob_context|0x80000000;
+ msg[3] = (u32)query_done;
+ msg[4] = -1;
+ i2o_post_wait(dev->controller, dev->tid, msg, 20, query_done,2);
+
+ /*
+ * Now unclaim the device.
+ */
+ if (i2ob_claim_device(dev, 0)<0)
+ printk(KERN_ERR "i2ob_release: controller rejected unclaim.\n");
+
+ }
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * Open the block device.
+ */
+
+static int i2ob_open(struct inode *inode, struct file *file)
+{
+ int minor;
+ struct i2ob_device *dev;
+
+ if (!inode)
+ return -EINVAL;
+ minor = MINOR(inode->i_rdev);
+ if (minor >= MAX_I2OB<<4)
+ return -ENODEV;
+ dev=&i2ob_dev[(minor&0xF0)];
+
+ if(dev->refcnt++==0)
+ {
+ u32 msg[6];
+ int *query_done;
+
+
+ if(i2ob_claim_device(dev, 1)<0)
+ {
+ dev->refcnt--;
+ return -EBUSY;
+ }
+
+ query_done = &dev->done_flag;
+ /*
+ * Mount the media if needed. Note that we don't use
+ * the lock bit. Since we have to issue a lock if it
+ * refuses a mount (quite possible) then we might as
+ * well just send two messages out.
+ */
+ msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = I2O_CMD_BLOCK_MMOUNT<<24|HOST_TID<<12|dev->tid;
+ msg[2] = i2ob_context|0x80000000;
+ msg[3] = (u32)query_done;
+ msg[4] = -1;
+ msg[5] = 0;
+ i2o_post_wait(dev->controller, dev->tid, msg, 24, query_done,2);
+ /*
+ * Lock the media
+ */
+ msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = I2O_CMD_BLOCK_MLOCK<<24|HOST_TID<<12|dev->tid;
+ msg[2] = i2ob_context|0x80000000;
+ msg[3] = (u32)query_done;
+ msg[4] = -1;
+ i2o_post_wait(dev->controller, dev->tid, msg, 20, query_done,2);
+ }
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * Issue a device query
+ */
+
+static int i2ob_query_device(struct i2ob_device *dev, int table,
+ int field, void *buf, int buflen)
+{
+ return i2o_query_scalar(dev->controller, dev->tid, i2ob_context,
+ table, field, buf, buflen, &dev->done_flag);
+}
+
+
+/*
+ * Install the I2O block device we found.
+ */
+
+static int i2ob_install_device(struct i2o_controller *c, struct i2o_device *d, int unit)
+{
+ u64 size;
+ u32 blocksize;
+ u32 limit;
+ u8 type;
+ u32 flags, status;
+ struct i2ob_device *dev=&i2ob_dev[unit];
+ int i;
+
+ /*
+ * Ask for the current media data. If that isn't supported
+ * then we ask for the device capacity data
+ */
+
+ if(i2ob_query_device(dev, 0x0004, 1, &blocksize, 4) != 0
+ || i2ob_query_device(dev, 0x0004, 0, &size, 8) !=0 )
+ {
+ i2ob_query_device(dev, 0x0000, 3, &blocksize, 4);
+ i2ob_query_device(dev, 0x0000, 4, &size, 8);
+ }
+
+ i2ob_query_device(dev, 0x0000, 5, &flags, 4);
+ i2ob_query_device(dev, 0x0000, 6, &status, 4);
+ i2ob_sizes[unit] = (int)(size>>10);
+ i2ob_hardsizes[unit] = blocksize;
+ i2ob_gendisk.part[unit].nr_sects = i2ob_sizes[unit];
+
+ /* Setting this higher than 1024 breaks the symbios for some reason */
+
+ limit=4096; /* 8 deep scatter gather */
+
+ printk("Byte limit is %d.\n", limit);
+
+ for(i=unit;i<=unit+15;i++)
+ i2ob_max_sectors[i]=(limit>>9);
+
+ i2ob[unit].nr_sects = (int)(size>>9);
+
+ i2ob_query_device(dev, 0x0000, 0, &type, 1);
+
+ sprintf(d->dev_name, "%s%c", i2ob_gendisk.major_name, 'a' + (unit>>4));
+
+ printk("%s: ", d->dev_name);
+ if(status&(1<<10))
+ printk("RAID ");
+ switch(type)
+ {
+ case 0: printk("Disk Storage");break;
+ case 4: printk("WORM");break;
+ case 5: printk("CD-ROM");break;
+ case 7: printk("Optical device");break;
+ default:
+ printk("Type %d", type);
+ }
+ if(((flags & (1<<3)) && !(status & (1<<3))) ||
+ ((flags & (1<<4)) && !(status & (1<<4))))
+ {
+ printk(" Not loaded.\n");
+ return 0;
+ }
+ printk(" %dMb, %d byte sectors",
+ (int)(size>>20), blocksize);
+ if(status&(1<<0))
+ {
+ u32 cachesize;
+ i2ob_query_device(dev, 0x0003, 0, &cachesize, 4);
+ cachesize>>=10;
+ if(cachesize>4095)
+ printk(", %dMb cache", cachesize>>10);
+ else
+ printk(", %dKb cache", cachesize);
+ }
+ printk(".\n");
+ printk("%s: Maximum sectors/read set to %d.\n",
+ d->dev_name, i2ob_max_sectors[unit]);
+ resetup_one_dev(&i2ob_gendisk, unit>>4);
+ return 0;
+}
+
+static void i2ob_probe(void)
+{
+ int i;
+ int unit = 0;
+ int warned = 0;
+
+ for(i=0; i< MAX_I2O_CONTROLLERS; i++)
+ {
+ struct i2o_controller *c=i2o_find_controller(i);
+ struct i2o_device *d;
+
+ if(c==NULL)
+ continue;
+
+ for(d=c->devices;d!=NULL;d=d->next)
+ {
+ if(d->class!=I2O_CLASS_RANDOM_BLOCK_STORAGE)
+ continue;
+
+ if(unit<MAX_I2OB<<4)
+ {
+ /*
+ * Get the device and fill in the
+ * Tid and controller.
+ */
+ struct i2ob_device *dev=&i2ob_dev[unit];
+ dev->i2odev = d;
+ dev->controller = c;
+ dev->tid = d->id;
+
+ /*
+ * Insure the device can be claimed
+ * before installing it.
+ */
+ if(i2ob_claim_device(dev, 1)==0)
+ {
+ printk(KERN_INFO "Claimed Dev %x Tid %d Unit %d\n",dev,dev->tid,unit);
+ i2ob_install_device(c,d,unit);
+ unit+=16;
+
+ /*
+ * Now that the device has been
+ * installed, unclaim it so that
+ * it can be claimed by either
+ * the block or scsi driver.
+ */
+ if (i2ob_claim_device(dev, 0)<0)
+ printk(KERN_INFO "Could not unclaim Dev %x Tid %d\n",dev,dev->tid);
+
+ }
+ else
+ printk(KERN_INFO "TID %d not claimed\n",dev->tid);
+ }
+ else
+ {
+ if(!warned++)
+ printk("i2o_block: too many controllers, registering only %d.\n", unit>>4);
+ }
+ }
+ }
+ i2ob_devices = unit;
+}
+
+/*
+ * Have we seen a media change ?
+ */
+
+static int i2ob_media_change(kdev_t dev)
+{
+ int i=MINOR(dev);
+ i>>=4;
+ if(i2ob_media_change_flag[i])
+ {
+ i2ob_media_change_flag[i]=0;
+ return 1;
+ }
+ return 0;
+}
+
+static int i2ob_revalidate(kdev_t dev)
+{
+ return do_i2ob_revalidate(dev, 0);
+}
+
+static int i2ob_reboot_event(struct notifier_block *n, unsigned long code, void *p)
+{
+ int i;
+
+ if(code != SYS_RESTART && code != SYS_HALT && code != SYS_POWER_OFF)
+ return NOTIFY_DONE;
+ for(i=0;i<MAX_I2OB;i++)
+ {
+ struct i2ob_device *dev=&i2ob_dev[(i<<4)];
+
+ if(dev->refcnt!=0)
+ {
+ /*
+ * Flush the onboard cache on power down
+ * also unlock the media
+ */
+ u32 msg[5];
+ int *query_done = &dev->done_flag;
+ msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = I2O_CMD_BLOCK_CFLUSH<<24|HOST_TID<<12|dev->tid;
+ msg[2] = i2ob_context|0x80000000;
+ msg[3] = (u32)query_done;
+ msg[4] = 60<<16;
+ i2o_post_wait(dev->controller, dev->tid, msg, 20, query_done,2);
+ /*
+ * Unlock the media
+ */
+ msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = I2O_CMD_BLOCK_MUNLOCK<<24|HOST_TID<<12|dev->tid;
+ msg[2] = i2ob_context|0x80000000;
+ msg[3] = (u32)query_done;
+ msg[4] = -1;
+ i2o_post_wait(dev->controller, dev->tid, msg, 20, query_done,2);
+ }
+ }
+ return NOTIFY_DONE;
+}
+
+struct notifier_block i2ob_reboot_notifier =
+{
+ i2ob_reboot_event,
+ NULL,
+ 0
+};
+
+static struct file_operations i2ob_fops =
+{
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ i2ob_ioctl, /* ioctl */
+ NULL, /* mmap */
+ i2ob_open, /* open */
+ NULL, /* flush */
+ i2ob_release, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ i2ob_media_change, /* Media Change */
+ i2ob_revalidate, /* Revalidate */
+ NULL /* File locks */
+};
+
+/*
+ * Partitioning
+ */
+
+static void i2ob_geninit(struct gendisk *gd)
+{
+}
+
+static struct gendisk i2ob_gendisk =
+{
+ MAJOR_NR,
+ "i2ohd",
+ 4,
+ 1<<4,
+ MAX_I2OB,
+ i2ob_geninit,
+ i2ob,
+ i2ob_sizes,
+ 0,
+ NULL,
+ NULL
+};
+
+/*
+ * And here should be modules and kernel interface
+ * (Just smiley confuses emacs :-)
+ */
+
+#ifdef MODULE
+#define i2ob_init init_module
+#endif
+
+int i2ob_init(void)
+{
+ int i;
+
+ printk("I2O block device OSM v0.06. (C) 1999 Red Hat Software.\n");
+
+ /*
+ * Register the block device interfaces
+ */
+
+ if (register_blkdev(MAJOR_NR, "i2o_block", &i2ob_fops)) {
+ printk("Unable to get major number %d for i2o_block\n",
+ MAJOR_NR);
+ return -EIO;
+ }
+#ifdef MODULE
+ printk("i2o_block: registered device at major %d\n", MAJOR_NR);
+#endif
+
+ /*
+ * Now fill in the boiler plate
+ */
+
+ blksize_size[MAJOR_NR] = i2ob_blksizes;
+ hardsect_size[MAJOR_NR] = i2ob_hardsizes;
+ blk_size[MAJOR_NR] = i2ob_sizes;
+ max_sectors[MAJOR_NR] = i2ob_max_sectors;
+
+ blk_dev[MAJOR_NR].request_fn = i2ob_request;
+ for (i = 0; i < MAX_I2OB << 4; i++) {
+ i2ob_dev[i].refcnt = 0;
+ i2ob_dev[i].flags = 0;
+ i2ob_dev[i].controller = NULL;
+ i2ob_dev[i].i2odev = NULL;
+ i2ob_dev[i].tid = 0;
+ i2ob_dev[i].head = NULL;
+ i2ob_dev[i].tail = NULL;
+ i2ob_blksizes[i] = 1024;
+ i2ob_max_sectors[i] = 2;
+ }
+
+ /*
+ * Register the OSM handler as we will need this to probe for
+ * drives, geometry and other goodies.
+ */
+
+ if(i2o_install_handler(&i2o_block_handler)<0)
+ {
+ unregister_blkdev(MAJOR_NR, "i2o_block");
+ printk(KERN_ERR "i2o_block: unable to register OSM.\n");
+ return -EINVAL;
+ }
+ i2ob_context = i2o_block_handler.context;
+
+ /*
+ * Finally see what is actually plugged in to our controllers
+ */
+
+ i2ob_probe();
+
+ register_reboot_notifier(&i2ob_reboot_notifier);
+ return 0;
+}
+
+#ifdef MODULE
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Red Hat Software");
+MODULE_DESCRIPTION("I2O Block Device OSM");
+
+void cleanup_module(void)
+{
+ struct gendisk **gdp;
+
+ unregister_reboot_notifier(&i2ob_reboot_notifier);
+
+ /*
+ * Flush the OSM
+ */
+
+ i2o_remove_handler(&i2o_block_handler);
+
+ /*
+ * Return the block device
+ */
+ if (unregister_blkdev(MAJOR_NR, "i2o_block") != 0)
+ printk("i2o_block: cleanup_module failed\n");
+ else
+ printk("i2o_block: module cleaned up.\n");
+
+ /*
+ * Why isnt register/unregister gendisk in the kernel ???
+ */
+
+ for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+ if (*gdp == &i2ob_gendisk)
+ break;
+
+}
+#endif
diff --git a/drivers/i2o/i2o_config.c b/drivers/i2o/i2o_config.c
new file mode 100644
index 000000000..c3c644883
--- /dev/null
+++ b/drivers/i2o/i2o_config.c
@@ -0,0 +1,613 @@
+/*
+ * I2O Configuration Interface Driver
+ *
+ * (C) Copyright 1999 Red Hat Software
+ *
+ * Written by Alan Cox, Building Number Three Ltd
+ *
+ * Modified 04/20/199 by Deepak Saxena
+ * - Added basic ioctl() support
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/i2o.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/miscdevice.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/spinlock.h>
+
+#include "i2o_proc.h"
+
+static int i2o_cfg_token = 0;
+static int i2o_cfg_context = -1;
+static void *page_buf;
+static void *i2o_buffer;
+static int i2o_ready;
+static int i2o_pagelen;
+static int i2o_error;
+static int cfg_inuse;
+static int i2o_eof;
+static spinlock_t i2o_config_lock = SPIN_LOCK_UNLOCKED;
+struct wait_queue *i2o_wait_queue;
+
+static int ioctl_getiops(unsigned long);
+static int ioctl_gethrt(unsigned long);
+static int ioctl_getlct(unsigned long);
+static int ioctl_parms(unsigned long, unsigned int);
+static int ioctl_html(unsigned long);
+static int ioctl_swdl(unsigned long);
+static int ioctl_swul(unsigned long);
+static int ioctl_swdel(unsigned long);
+
+/*
+ * This is the callback for any message we have posted. The message itself
+ * will be returned to the message pool when we return from the IRQ
+ *
+ * This runs in irq context so be short and sweet.
+ */
+static void i2o_cfg_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *m)
+{
+ i2o_cfg_token = I2O_POST_WAIT_OK;
+
+ return;
+}
+
+/*
+ * Each of these describes an i2o message handler. They are
+ * multiplexed by the i2o_core code
+ */
+
+struct i2o_handler cfg_handler=
+{
+ i2o_cfg_reply,
+ "Configuration",
+ 0
+};
+
+static long long cfg_llseek(struct file *file, long long offset, int origin)
+{
+ return -ESPIPE;
+}
+
+/* i2ocontroller/i2odevice/page/?data */
+
+static ssize_t cfg_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+ printk(KERN_INFO "i2o_config write not yet supported\n");
+
+ return 0;
+}
+
+/* To be written for event management support */
+static ssize_t cfg_read(struct file *file, char *buf, size_t count, loff_t *ptr)
+{
+ return 0;
+}
+
+static int cfg_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret;
+
+ /* Only 1 token, so lock... */
+ spin_lock(&i2o_config_lock);
+
+ switch(cmd)
+ {
+ case I2OGETIOPS:
+ ret = ioctl_getiops(arg);
+ break;
+
+ case I2OHRTGET:
+ ret = ioctl_gethrt(arg);
+ break;
+
+ case I2OLCTGET:
+ ret = ioctl_getlct(arg);
+ break;
+
+ case I2OPARMSET:
+ ret = ioctl_parms(arg, I2OPARMSET);
+ break;
+
+ case I2OPARMGET:
+ ret = ioctl_parms(arg, I2OPARMGET);
+ break;
+
+ case I2OSWDL:
+ ret = ioctl_swdl(arg);
+ break;
+
+ case I2OSWUL:
+ ret = ioctl_swul(arg);
+ break;
+
+ case I2OSWDEL:
+ ret = ioctl_swdel(arg);
+ break;
+
+ case I2OHTML:
+ ret = ioctl_html(arg);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ spin_unlock(&i2o_config_lock);
+ return ret;
+}
+
+int ioctl_getiops(unsigned long arg)
+{
+ u8 *user_iop_table = (u8*)arg;
+ struct i2o_controller *c = NULL;
+ int i;
+ u8 foo[MAX_I2O_CONTROLLERS];
+
+ if(!access_ok(VERIFY_WRITE, user_iop_table, MAX_I2O_CONTROLLERS))
+ return -EFAULT;
+
+ for(i = 0; i < MAX_I2O_CONTROLLERS; i++)
+ {
+ c = i2o_find_controller(i);
+ if(c)
+ {
+ printk(KERN_INFO "ioctl: iop%d found\n", i);
+ foo[i] = 1;
+ i2o_unlock_controller(c);
+ }
+ else
+ {
+ printk(KERN_INFO "ioctl: iop%d not found\n", i);
+ foo[i] = 0;
+ }
+ }
+
+ __copy_to_user(user_iop_table, foo, MAX_I2O_CONTROLLERS);
+ return 0;
+}
+
+int ioctl_gethrt(unsigned long arg)
+{
+ struct i2o_controller *c;
+ struct i2o_cmd_hrtlct *cmd = (struct i2o_cmd_hrtlct*)arg;
+ struct i2o_cmd_hrtlct kcmd;
+ pi2o_hrt hrt;
+ u32 msg[6];
+ u32 *workspace;
+ int len;
+ int token;
+ u32 reslen;
+ int ret = 0;
+
+ if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct)))
+ return -EFAULT;
+
+ if(get_user(reslen, kcmd.reslen) < 0)
+ return -EFAULT;
+
+ if(kcmd.resbuf == NULL)
+ return -EFAULT;
+
+ c = i2o_find_controller(kcmd.iop);
+ if(!c)
+ return -ENXIO;
+
+ workspace = kmalloc(8192, GFP_KERNEL);
+ hrt = (pi2o_hrt)workspace;
+ if(workspace==NULL)
+ return -ENOMEM;
+
+ memset(workspace, 0, 8192);
+
+ msg[0]= SIX_WORD_MSG_SIZE| SGL_OFFSET_4;
+ msg[1]= I2O_CMD_HRT_GET<<24 | HOST_TID<<12 | ADAPTER_TID;
+ msg[2]= (u32)cfg_handler.context;
+ msg[3]= 0;
+ msg[4]= (0xD0000000 | 8192);
+ msg[5]= virt_to_phys(workspace);
+
+ token = i2o_post_wait(c, ADAPTER_TID, msg, 6*4, &i2o_cfg_token,2);
+ if(token == I2O_POST_WAIT_TIMEOUT)
+ {
+ kfree(workspace);
+ i2o_unlock_controller(c);
+ return -ETIMEDOUT;
+ }
+ i2o_unlock_controller(c);
+
+ len = 8 + ((hrt->entry_len * hrt->num_entries) << 2);
+ /* We did a get user...so assuming mem is ok...is this bad? */
+ put_user(len, kcmd.reslen);
+ if(len > reslen)
+ ret = -ENOBUFS;
+ if(copy_to_user(kcmd.resbuf, (void*)hrt, len))
+ ret = -EINVAL;
+
+ kfree(workspace);
+ return ret;
+}
+
+int ioctl_getlct(unsigned long arg)
+{
+ struct i2o_controller *c;
+ struct i2o_cmd_hrtlct *cmd = (struct i2o_cmd_hrtlct*)arg;
+ struct i2o_cmd_hrtlct kcmd;
+ pi2o_lct lct;
+ u32 msg[9];
+ u32 *workspace;
+ int len;
+ int token;
+ int ret = 0;
+ u32 reslen;
+
+ if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_hrtlct)))
+ return -EFAULT;
+
+ if(get_user(reslen, kcmd.reslen) < 0)
+ return -EFAULT;
+
+ if(kcmd.resbuf == NULL)
+ return -EFAULT;
+
+ c = i2o_find_controller(kcmd.iop);
+ if(!c)
+ return -ENXIO;
+
+ workspace = kmalloc(8192, GFP_KERNEL);
+ lct = (pi2o_lct)workspace;
+ if(workspace==NULL)
+ return -ENOMEM;
+
+ memset(workspace, 0, 8192);
+
+ msg[0]= EIGHT_WORD_MSG_SIZE | SGL_OFFSET_6;
+ msg[1]= I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID;
+ msg[2]= (u32)cfg_handler.context;
+ msg[3]= 0;
+ msg[4]= 0xFFFFFFFF;
+ msg[5]= 0;
+ msg[6]= (0xD0000000 | 8192);
+ msg[7]= virt_to_phys(workspace);
+
+ token = i2o_post_wait(c, ADAPTER_TID, msg, 8*4, &i2o_cfg_token,2);
+ if(token == I2O_POST_WAIT_TIMEOUT)
+ {
+ kfree(workspace);
+ i2o_unlock_controller(c);
+ return -ETIMEDOUT;
+ }
+ i2o_unlock_controller(c);
+
+ len = (unsigned int)lct->table_size << 2;
+ put_user(len, kcmd.reslen);
+ if(len > reslen)
+ ret = -ENOBUFS;
+ else if(copy_to_user(kcmd.resbuf, (void*)lct, len))
+ ret = -EINVAL;
+
+ kfree(workspace);
+ return ret;
+}
+
+static int ioctl_parms(unsigned long arg, unsigned int type)
+{
+ int ret = 0;
+ struct i2o_controller *c;
+ struct i2o_cmd_psetget *cmd = (struct i2o_cmd_psetget*)arg;
+ struct i2o_cmd_psetget kcmd;
+ u32 msg[9];
+ u32 reslen;
+ int token;
+ u8 *ops;
+ u8 *res;
+ u16 *res16;
+ u32 *res32;
+ u16 count;
+ int len;
+ int i,j;
+
+ u32 i2o_cmd = (type == I2OPARMGET ?
+ I2O_CMD_UTIL_PARAMS_GET :
+ I2O_CMD_UTIL_PARAMS_SET);
+
+ if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_cmd_psetget)))
+ return -EFAULT;
+
+ if(get_user(reslen, kcmd.reslen))
+ return -EFAULT;
+
+ c = i2o_find_controller(kcmd.iop);
+ if(!c)
+ return -ENXIO;
+
+ ops = (u8*)kmalloc(kcmd.oplen, GFP_KERNEL);
+ if(!ops)
+ return -ENOMEM;
+
+ if(copy_from_user(ops, kcmd.opbuf, kcmd.oplen))
+ {
+ kfree(ops);
+ return -EFAULT;
+ }
+
+ /*
+ * It's possible to have a _very_ large table
+ * and that the user asks for all of it at once...
+ */
+ res = (u8*)kmalloc(65536, GFP_KERNEL);
+ if(!res)
+ {
+ kfree(ops);
+ return -ENOMEM;
+ }
+
+ res16 = (u16*)res;
+
+ msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_5;
+ msg[1]=i2o_cmd<<24|HOST_TID<<12|cmd->tid;
+ msg[2]=(u32)cfg_handler.context;
+ msg[3]=0;
+ msg[4]=0;
+ msg[5]=0x54000000|kcmd.oplen;
+ msg[6]=virt_to_bus(ops);
+ msg[7]=0xD0000000|(65536);
+ msg[8]=virt_to_bus(res);
+
+ /*
+ * Parm set sometimes takes a little while for some reason
+ */
+ token = i2o_post_wait(c, kcmd.tid, msg, 9*4, &i2o_cfg_token,10);
+ if(token == I2O_POST_WAIT_TIMEOUT)
+ {
+ kfree(ops);
+ kfree(res);
+ return -ETIMEDOUT;
+ }
+
+ kfree(ops);
+
+ /*
+ * Determine required size...there's got to be a quicker way?
+ * Dump data to syslog for debugging failures
+ */
+ count = res16[0];
+ printk(KERN_INFO "%0#6x\n%0#6x\n", res16[0], res16[1]);
+ len = 4;
+ res16 += 2;
+ for(i = 0; i < count; i++ )
+ {
+ len += res16[0] << 2; /* BlockSize field in ResultBlock */
+ res32 = (u32*)res16;
+ for(j = 0; j < res16[0]; j++)
+ printk(KERN_INFO "%0#10x\n", res32[j]);
+ res16 += res16[0] << 1; /* Shift to next block */
+ }
+
+ put_user(len, kcmd.reslen);
+ if(len > reslen)
+ ret = -ENOBUFS;
+ else if(copy_to_user(cmd->resbuf, res, len))
+ ret = -EFAULT;
+
+ kfree(res);
+
+ return ret;
+}
+
+int ioctl_html(unsigned long arg)
+{
+ struct i2o_html *cmd = (struct i2o_html*)arg;
+ struct i2o_html kcmd;
+ struct i2o_controller *c;
+ u8 *res = NULL;
+ void *query = NULL;
+ int ret = 0;
+ int token;
+ u32 len;
+ u32 reslen;
+ u32 msg[MSG_FRAME_SIZE/4];
+
+ if(copy_from_user(&kcmd, cmd, sizeof(struct i2o_html)))
+ {
+ printk(KERN_INFO "i2o_config: can't copy html cmd\n");
+ return -EFAULT;
+ }
+
+ if(get_user(reslen, kcmd.reslen) < 0)
+ {
+ printk(KERN_INFO "i2o_config: can't copy html reslen\n");
+ return -EFAULT;
+ }
+
+ if(!kcmd.resbuf)
+ {
+ printk(KERN_INFO "i2o_config: NULL html buffer\n");
+ return -EFAULT;
+ }
+
+ c = i2o_find_controller(kcmd.iop);
+ if(!c)
+ return -ENXIO;
+
+ if(kcmd.qlen) /* Check for post data */
+ {
+ query = kmalloc(kcmd.qlen, GFP_KERNEL);
+ if(!query)
+ return -ENOMEM;
+ if(copy_from_user(query, kcmd.qbuf, kcmd.qlen))
+ {
+ printk(KERN_INFO "i2o_config: could not get query\n");
+ kfree(query);
+ return -EFAULT;
+ }
+ }
+
+ res = kmalloc(4096, GFP_KERNEL);
+ if(!res)
+ return -ENOMEM;
+
+ msg[1] = (I2O_CMD_UTIL_CONFIG_DIALOG << 24)|HOST_TID<<12|kcmd.tid;
+ msg[2] = i2o_cfg_context;
+ msg[3] = 0;
+ msg[4] = kcmd.page;
+ msg[5] = 0xD0000000|4096;
+ msg[6] = virt_to_bus(res);
+ if(!kcmd.qlen) /* Check for post data */
+ msg[0] = SEVEN_WORD_MSG_SIZE|SGL_OFFSET_5;
+ else
+ {
+ msg[0] = NINE_WORD_MSG_SIZE|SGL_OFFSET_5;
+ msg[5] = 0x50000000|4096;
+ msg[7] = 0xD4000000|(kcmd.qlen);
+ msg[8] = virt_to_phys(query);
+ }
+
+ token = i2o_post_wait(c, cmd->tid, msg, 9*4, &i2o_cfg_token, 10);
+ if(token == I2O_POST_WAIT_TIMEOUT)
+ {
+ kfree(res);
+ if(kcmd.qlen) kfree(query);
+
+ return -ETIMEDOUT;
+ }
+
+ len = strnlen(res, 8192);
+ put_user(len, kcmd.reslen);
+ if(len > reslen)
+ ret = -ENOMEM;
+ if(copy_to_user(kcmd.resbuf, res, len))
+ ret = -EFAULT;
+
+ kfree(res);
+ if(kcmd.qlen)
+ kfree(query);
+
+ return ret;
+}
+
+/* To be written */
+int ioctl_swdl(unsigned long arg)
+{
+ return -ENOSYS;
+}
+
+/* To be written */
+int ioctl_swul(unsigned long arg)
+{
+ return -EINVAL;
+}
+
+/* To be written */
+int ioctl_swdel(unsigned long arg)
+{
+ return 0;
+}
+
+static int cfg_open(struct inode *inode, struct file *file)
+{
+ /*
+ * Should support multiple management users
+ */
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int cfg_release(struct inode *inode, struct file *file)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+
+static struct file_operations config_fops =
+{
+ cfg_llseek,
+ cfg_read,
+ cfg_write,
+ NULL,
+ NULL /*cfg_poll*/,
+ cfg_ioctl,
+ NULL, /* No mmap */
+ cfg_open,
+ NULL, /* No flush */
+ cfg_release
+};
+
+static struct miscdevice i2o_miscdev = {
+ I2O_MINOR,
+ "i2octl",
+ &config_fops
+};
+
+#ifdef MODULE
+int init_module(void)
+#else
+int i2o_config_init(void)
+#endif
+{
+ printk(KERN_INFO "i2o configuration manager v 0.02\n");
+
+ if((page_buf = kmalloc(4096, GFP_KERNEL))==NULL)
+ {
+ printk(KERN_ERR "i2o_config: no memory for page buffer.\n");
+ return -ENOBUFS;
+ }
+ if(misc_register(&i2o_miscdev)==-1)
+ {
+ printk(KERN_ERR "i2o_config: can't register device.\n");
+ kfree(page_buf);
+ return -EBUSY;
+ }
+ /*
+ * Install our handler
+ */
+ if(i2o_install_handler(&cfg_handler)<0)
+ {
+ kfree(page_buf);
+ printk(KERN_ERR "i2o_config: handler register failed.\n");
+ misc_deregister(&i2o_miscdev);
+ return -EBUSY;
+ }
+ /*
+ * The low 16bits of the transaction context must match this
+ * for everything we post. Otherwise someone else gets our mail
+ */
+ i2o_cfg_context = cfg_handler.context;
+ return 0;
+}
+
+#ifdef MODULE
+
+void cleanup_module(void)
+{
+ misc_deregister(&i2o_miscdev);
+
+ if(page_buf)
+ kfree(page_buf);
+ if(i2o_cfg_context != -1)
+ i2o_remove_handler(&cfg_handler);
+ if(i2o_buffer)
+ kfree(i2o_buffer);
+}
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Red Hat Software");
+MODULE_DESCRIPTION("I2O Configuration");
+
+#endif
diff --git a/drivers/i2o/i2o_core.c b/drivers/i2o/i2o_core.c
new file mode 100644
index 000000000..3a3f1fe94
--- /dev/null
+++ b/drivers/i2o/i2o_core.c
@@ -0,0 +1,2053 @@
+/*
+ * Core I2O structure managment
+ *
+ * (C) Copyright 1999 Red Hat Software
+ *
+ * Written by Alan Cox, Building Number Three Ltd
+ *
+ * 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.
+ *
+ * A lot of the I2O message side code from this is taken from the
+ * Red Creek RCPCI45 adapter driver by Red Creek Communications
+ *
+ * Some fixes and cleanup by Philipp Rumpf
+ *
+ * Additional fixes by Juha Sievänen <Juha.Sievanen@cs.Helsinki.FI>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/i2o.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+
+#include <asm/io.h>
+#include <asm/spinlock.h>
+
+#include "i2o_lan.h"
+
+/*
+ * Size of the I2O module table
+ */
+
+
+static struct i2o_handler *i2o_handlers[MAX_I2O_MODULES];
+static struct i2o_controller *i2o_controllers[MAX_I2O_CONTROLLERS];
+int i2o_num_controllers = 0;
+
+
+extern int i2o_online_controller(struct i2o_controller *c);
+
+/*
+ * I2O configuration spinlock. This isnt a big deal for contention
+ * so we have one only
+ */
+
+#ifdef __SMP__
+static spinlock_t i2o_configuration_lock = SPIN_LOCK_UNLOCKED;
+#endif
+
+/*
+ * Install an I2O handler - these handle the asynchronous messaging
+ * from the card once it has initialised.
+ */
+
+int i2o_install_handler(struct i2o_handler *h)
+{
+ int i;
+ spin_lock(&i2o_configuration_lock);
+ for(i=0;i<MAX_I2O_MODULES;i++)
+ {
+ if(i2o_handlers[i]==NULL)
+ {
+ h->context = i;
+ i2o_handlers[i]=h;
+ spin_unlock(&i2o_configuration_lock);
+ return 0;
+ }
+ }
+ spin_unlock(&i2o_configuration_lock);
+ return -ENOSPC;
+}
+
+int i2o_remove_handler(struct i2o_handler *h)
+{
+ i2o_handlers[h->context]=NULL;
+ return 0;
+}
+
+
+/*
+ * Each I2O controller has a chain of devices on it - these match
+ * the useful parts of the LCT of the board.
+ */
+
+int i2o_install_device(struct i2o_controller *c, struct i2o_device *d)
+{
+ spin_lock(&i2o_configuration_lock);
+ d->controller=c;
+ d->owner=NULL;
+ d->next=c->devices;
+ c->devices=d;
+ *d->dev_name = 0;
+ spin_unlock(&i2o_configuration_lock);
+ return 0;
+}
+
+/* we need this version to call out of i2o_delete_controller */
+
+int __i2o_delete_device(struct i2o_device *d)
+{
+ struct i2o_device **p;
+
+ p=&(d->controller->devices);
+
+ /*
+ * Hey we have a driver!
+ */
+
+ if(d->owner)
+ return -EBUSY;
+
+ /*
+ * Seek, locate
+ */
+
+ while(*p!=NULL)
+ {
+ if(*p==d)
+ {
+ /*
+ * Destroy
+ */
+ *p=d->next;
+ kfree(d);
+ return 0;
+ }
+ p=&((*p)->next);
+ }
+ printk(KERN_ERR "i2o_delete_device: passed invalid device.\n");
+ return -EINVAL;
+}
+
+int i2o_delete_device(struct i2o_device *d)
+{
+ int ret;
+
+ spin_lock(&i2o_configuration_lock);
+
+ ret = __i2o_delete_device(d);
+
+ spin_unlock(&i2o_configuration_lock);
+
+ return ret;
+}
+
+/*
+ * Add and remove controllers from the I2O controller list
+ */
+
+int i2o_install_controller(struct i2o_controller *c)
+{
+ int i;
+ spin_lock(&i2o_configuration_lock);
+ for(i=0;i<MAX_I2O_CONTROLLERS;i++)
+ {
+ if(i2o_controllers[i]==NULL)
+ {
+ i2o_controllers[i]=c;
+ c->next=i2o_controller_chain;
+ i2o_controller_chain=c;
+ c->unit = i;
+ sprintf(c->name, "i2o/iop%d", i);
+ i2o_num_controllers++;
+ spin_unlock(&i2o_configuration_lock);
+ return 0;
+ }
+ }
+ printk(KERN_ERR "No free i2o controller slots.\n");
+ spin_unlock(&i2o_configuration_lock);
+ return -EBUSY;
+}
+
+int i2o_delete_controller(struct i2o_controller *c)
+{
+ struct i2o_controller **p;
+
+ spin_lock(&i2o_configuration_lock);
+ if(atomic_read(&c->users))
+ {
+ spin_unlock(&i2o_configuration_lock);
+ return -EBUSY;
+ }
+ while(c->devices)
+ {
+ if(__i2o_delete_device(c->devices)<0)
+ {
+ /* Shouldnt happen */
+ spin_unlock(&i2o_configuration_lock);
+ return -EBUSY;
+ }
+ }
+ c->destructor(c);
+
+ p=&i2o_controller_chain;
+
+ while(*p)
+ {
+ if(*p==c)
+ {
+ /* Prepare for restart */
+// i2o_clear_controller(c);
+
+ *p=c->next;
+ spin_unlock(&i2o_configuration_lock);
+ if(c->page_frame);
+ kfree(c->page_frame);
+ i2o_controllers[c->unit]=NULL;
+ kfree(c);
+ i2o_num_controllers--;
+ return 0;
+ }
+ p=&((*p)->next);
+ }
+ spin_unlock(&i2o_configuration_lock);
+ printk(KERN_ERR "i2o_delete_controller: bad pointer!\n");
+ return -ENOENT;
+}
+
+void i2o_unlock_controller(struct i2o_controller *c)
+{
+ atomic_dec(&c->users);
+}
+
+struct i2o_controller *i2o_find_controller(int n)
+{
+ struct i2o_controller *c;
+
+ if(n<0 || n>=MAX_I2O_CONTROLLERS)
+ return NULL;
+
+ spin_lock(&i2o_configuration_lock);
+ c=i2o_controllers[n];
+ if(c!=NULL)
+ atomic_inc(&c->users);
+ spin_unlock(&i2o_configuration_lock);
+ return c;
+}
+
+
+/*
+ * Track if a device is being used by a driver
+ */
+
+int i2o_claim_device(struct i2o_device *d, struct i2o_driver *r)
+{
+ spin_lock(&i2o_configuration_lock);
+ if(d->owner)
+ {
+ spin_unlock(&i2o_configuration_lock);
+ return -EBUSY;
+ }
+ atomic_inc(&d->controller->users);
+ d->owner=r;
+ spin_unlock(&i2o_configuration_lock);
+ return 0;
+}
+
+int i2o_release_device(struct i2o_device *d)
+{
+ spin_lock(&i2o_configuration_lock);
+ if(d->owner==NULL)
+ {
+ spin_unlock(&i2o_configuration_lock);
+ return -EINVAL;
+ }
+ atomic_dec(&d->controller->users);
+ d->owner=NULL;
+ spin_unlock(&i2o_configuration_lock);
+ return 0;
+}
+
+/*
+ * This is called by the bus specific driver layer when an interrupt
+ * or poll of this card interface is desired.
+ */
+
+void i2o_run_queue(struct i2o_controller *c)
+{
+ struct i2o_message *m;
+ u32 mv;
+
+ while((mv=I2O_REPLY_READ32(c))!=0xFFFFFFFF)
+ {
+ struct i2o_handler *i;
+ m=(struct i2o_message *)bus_to_virt(mv);
+ /*
+ * Temporary Debugging
+ */
+ if(((m->function_addr>>24)&0xFF)==0x15)
+ printk("UTFR!\n");
+// printk("dispatching.\n");
+ i=i2o_handlers[m->initiator_context&(MAX_I2O_MODULES-1)];
+ if(i)
+ i->reply(i,c,m);
+ else
+ printk("Spurious reply\n");
+ i2o_flush_reply(c,mv);
+ mb();
+ }
+}
+
+
+/*
+ * Do i2o class name lookup
+ */
+const char *i2o_get_class_name(int class)
+{
+ int idx = 16;
+ static char *i2o_class_name[] = {
+ "Executive",
+ "Device Driver Module",
+ "Block Device",
+ "Tape Device",
+ "LAN Inteface",
+ "WAN Interface",
+ "Fibre Channel Port",
+ "Fibre Channel Device",
+ "SCSI Device",
+ "ATE Port",
+ "ATE Device",
+ "Floppy Controller",
+ "Floppy Device",
+ "Secondary Bus Port",
+ "Peer Transport Agent",
+ "Peer Transport",
+ "Unknown"
+ };
+
+ switch(class&0xFFF)
+ {
+ case I2O_CLASS_EXECUTIVE:
+ idx = 0; break;
+ case I2O_CLASS_DDM:
+ idx = 1; break;
+ case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+ idx = 2; break;
+ case I2O_CLASS_SEQUENTIAL_STORAGE:
+ idx = 3; break;
+ case I2O_CLASS_LAN:
+ idx = 4; break;
+ case I2O_CLASS_WAN:
+ idx = 5; break;
+ case I2O_CLASS_FIBRE_CHANNEL_PORT:
+ idx = 6; break;
+ case I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL:
+ idx = 7; break;
+ case I2O_CLASS_SCSI_PERIPHERAL:
+ idx = 8; break;
+ case I2O_CLASS_ATE_PORT:
+ idx = 9; break;
+ case I2O_CLASS_ATE_PERIPHERAL:
+ idx = 10; break;
+ case I2O_CLASS_FLOPPY_CONTROLLER:
+ idx = 11; break;
+ case I2O_CLASS_FLOPPY_DEVICE:
+ idx = 12; break;
+ case I2O_CLASS_BUS_ADAPTER_PORT:
+ idx = 13; break;
+ case I2O_CLASS_PEER_TRANSPORT_AGENT:
+ idx = 14; break;
+ case I2O_CLASS_PEER_TRANSPORT:
+ idx = 15; break;
+ }
+
+ return i2o_class_name[idx];
+}
+
+
+/*
+ * Wait up to 5 seconds for a message slot to be available.
+ */
+
+u32 i2o_wait_message(struct i2o_controller *c, char *why)
+{
+ long time=jiffies;
+ u32 m;
+ while((m=I2O_POST_READ32(c))==0xFFFFFFFF)
+ {
+ if((jiffies-time)>=5*HZ)
+ {
+ printk(KERN_ERR "%s: Timeout waiting for message to send %s.\n",
+ c->name, why);
+ return 0xFFFFFFFF;
+ }
+ schedule();
+ barrier();
+ }
+ return m;
+}
+
+
+/*
+ * Wait up to 5 seconds for a reply to be available.
+ */
+
+u32 i2o_wait_reply(struct i2o_controller *c, char *why, int timeout)
+{
+ u32 m;
+ long time=jiffies;
+
+ while((m=I2O_REPLY_READ32(c))==0xFFFFFFFF)
+ {
+ if(jiffies-time >= timeout*HZ )
+ {
+ printk(KERN_ERR "%s: timeout waiting for %s reply.\n",
+ c->name, why);
+ return 0xFFFFFFFF;
+ }
+ schedule();
+ }
+ return m;
+}
+
+
+
+/* Quiesce and clear IOP */
+int i2o_quiesce_controller(struct i2o_controller *c)
+{
+ u32 m;
+ u32 *msg;
+
+ /* now we stop receiving messages to this IOP */
+ m=i2o_wait_message(c, "Quiesce IOP");
+ if(m==0xFFFFFFFF)
+ return -ETIMEDOUT;
+
+ msg=(u32 *)(c->mem_offset+m);
+
+ msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1]=I2O_CMD_SYS_QUIESCE<<24|HOST_TID<<12|ADAPTER_TID;
+ msg[2]=0;
+ msg[3]=0;
+
+ printk(KERN_DEBUG "Sending SysQuiesce to %s\n", c->name);
+ i2o_post_message(c,m);
+
+ m=i2o_wait_reply(c, "System Quiesce", 20);
+
+ if (m==0xFFFFFFFF)
+ return -ETIMEDOUT;
+ /* Someday we should check return status... */
+
+ return 0;
+}
+
+int i2o_clear_controller(struct i2o_controller *c)
+{
+ u32 m;
+ u32 *msg;
+
+ m=i2o_wait_message(c, "IOP Clear");
+ if (m==0xFFFFFFFF)
+ return -ETIMEDOUT;
+
+ msg=(u32 *)(c->mem_offset+m);
+
+ msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1]=I2O_CMD_ADAPTER_CLEAR<<24|HOST_TID<<12|ADAPTER_TID;
+ msg[2]=0;
+ msg[3]=0;
+
+ printk(KERN_DEBUG "Sending IOPClear to %s\n", c->name);
+ i2o_post_message(c, m);
+
+ m=i2o_wait_reply(c, "IOP Clear timeout", 5);
+
+ if(m==0xFFFFFFFF)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+
+/*
+ * i2o table walking. We just provide a single element retrieve. You can
+ * all sorts of fancy lookups in I2O but we have no performance critical
+ * lookups so why write all the code for it.
+ */
+
+#if 0
+static int i2o_query_table_polled(struct i2o_controller *c, int tid, void *buf, int buflen,
+ int group, int field, u32 *key, int keylen)
+{
+ u32 m;
+ u32 *msg;
+ u16 op[64];
+ u32 *p;
+ int i;
+ u32 *rbuf;
+
+ op[0]=1; /* One Operation */
+ op[1]=0; /* PAD */
+ op[2]=2; /* LIST_GET */
+ op[3]=group; /* group number */
+ op[4]=1; /* 1 field */
+ op[5]=field; /* Field number */
+ op[6]=1; /* Key count */
+ memcpy(op+7, key, keylen); /* Key */
+
+ m=i2o_wait_message(c, "I2O query table.");
+ if(m==0xFFFFFFFF)
+ {
+ return -ETIMEDOUT;
+ }
+
+ msg=(u32 *)(c->mem_offset+m);
+
+ rbuf=kmalloc(buflen+32, GFP_KERNEL);
+ if(rbuf==NULL)
+ {
+ printk(KERN_ERR "No free memory for table read.\n");
+ return -ENOMEM;
+ }
+ msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_5;
+ msg[1]=I2O_CMD_UTIL_PARAMS_GET<<24|HOST_TID<<12|tid;
+ msg[2]=0; /* Context */
+ msg[3]=0;
+ msg[4]=0;
+ msg[5]=0x54000000|(14);
+ msg[6]=virt_to_bus(op);
+ msg[7]=0xD0000000|(32+buflen);
+ msg[8]=virt_to_bus(rbuf);
+
+ i2o_post_message(c,m);
+ barrier();
+
+ /*
+ * Now wait for a reply
+ */
+
+
+ m=i2o_wait_reply(c, "Table read timeout", 5);
+
+ if(m==0xFFFFFFFF)
+ {
+ kfree(rbuf);
+ return -ETIMEDOUT;
+ }
+
+ msg = (u32 *)bus_to_virt(m);
+
+ if(msg[4]>>24)
+ {
+ i2o_report_status(KERN_WARNING, "i2o_core",
+ (msg[1]>>24)&0xFF, (msg[4]>>24)&0xFF,
+ msg[4]&0xFFFF);
+ }
+
+ p=rbuf;
+
+ /* Ok 'p' is the reply block - lets see what happened */
+ /* p0->p2 are the header */
+
+ /* FIXME: endians - turn p3 to little endian */
+
+ i=(p[0]&0xFFFF)<<2; /* Message size */
+ if(i<buflen)
+ buflen=i;
+
+ /* Do we have an error block ? */
+ if(p[0]&0xFF000000)
+ {
+ printk(KERN_ERR "%s: error in field read.\n",
+ c->name);
+ kfree(rbuf);
+ return -EBADR;
+ }
+
+ /* p[1] holds the more flag and row count - we dont care */
+
+ /* Ok it worked p[2]-> hold the data */
+ memcpy(buf, p+2, buflen);
+
+ kfree(rbuf);
+
+ /* Finally return the message */
+ I2O_REPLY_WRITE32(c,m);
+ return buflen;
+}
+#endif
+
+static int i2o_query_scalar_polled(struct i2o_controller *c, int tid, void *buf, int buflen,
+ int group, int field)
+{
+ u32 m;
+ u32 *msg;
+ u16 op[8];
+ u32 *p;
+ int i;
+ u32 *rbuf;
+
+ op[0]=1; /* One Operation */
+ op[1]=0; /* PAD */
+ op[2]=1; /* FIELD_GET */
+ op[3]=group; /* group number */
+ op[4]=1; /* 1 field */
+ op[5]=field; /* Field number */
+
+ m=i2o_wait_message(c, "I2O query scalar.");
+ if(m==0xFFFFFFFF)
+ {
+ return -ETIMEDOUT;
+ }
+
+ msg=(u32 *)(c->mem_offset+m);
+
+ rbuf=kmalloc(buflen+32, GFP_KERNEL);
+ if(rbuf==NULL)
+ {
+ printk(KERN_ERR "No free memory for scalar read.\n");
+ return -ENOMEM;
+ }
+
+ msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_5;
+ msg[1]=I2O_CMD_UTIL_PARAMS_GET<<24|HOST_TID<<12|tid;
+ msg[2]=0; /* Context */
+ msg[3]=0;
+ msg[4]=0;
+ msg[5]=0x54000000|12;
+ msg[6]=virt_to_bus(op);
+ msg[7]=0xD0000000|(32+buflen);
+ msg[8]=virt_to_bus(rbuf);
+
+ i2o_post_message(c,m);
+ barrier();
+
+ /*
+ * Now wait for a reply
+ */
+
+
+ m=i2o_wait_reply(c, "Scalar read timeout", 5);
+
+ if(m==0xFFFFFFFF)
+ {
+ kfree(rbuf);
+ return -ETIMEDOUT;
+ }
+
+ msg = (u32 *)bus_to_virt(m);
+ if(msg[4]>>24)
+ {
+ i2o_report_status(KERN_WARNING, "i2o_core",
+ (msg[1]>>24)&0xFF, (msg[4]>>24)&0xFF,
+ msg[4]&0xFFFF);
+ }
+
+ p=rbuf;
+
+ /* Ok 'p' is the reply block - lets see what happened */
+ /* p0->p2 are the header */
+
+ /* FIXME: endians - turn p3 to little endian */
+
+ if((p[0]&0xFFFF)!=1)
+ printk(KERN_WARNING "Suspicious field read return 0x%08X\n", p[0]);
+
+ i=(p[1]&0xFFFF)<<2; /* Message size */
+ if(i<buflen)
+ buflen=i;
+
+ /* Do we have an error block ? */
+ if(p[1]&0xFF000000)
+ {
+ printk(KERN_ERR "%s: error in field read.\n",
+ c->name);
+ kfree(rbuf);
+ return -EBADR;
+ }
+
+ /* p[1] holds the more flag and row count - we dont care */
+
+ /* Ok it worked p[2]-> hold the data */
+ memcpy(buf, p+2, buflen);
+
+ kfree(rbuf);
+
+ /* Finally return the message */
+ I2O_REPLY_WRITE32(c,m);
+ return buflen;
+}
+
+/*
+ * Dump the information block associated with a given unit (TID)
+ */
+
+void i2o_report_controller_unit(struct i2o_controller *c, int unit)
+{
+ char buf[64];
+
+ if(i2o_query_scalar_polled(c, unit, buf, 16, 0xF100, 3)>=0)
+ {
+ buf[16]=0;
+ printk(KERN_INFO " Vendor: %s\n", buf);
+ }
+ if(i2o_query_scalar_polled(c, unit, buf, 16, 0xF100, 4)>=0)
+ {
+ buf[16]=0;
+ printk(KERN_INFO " Device: %s\n", buf);
+ }
+#if 0
+ if(i2o_query_scalar_polled(c, unit, buf, 16, 0xF100, 5)>=0)
+ {
+ buf[16]=0;
+ printk(KERN_INFO "Description: %s\n", buf);
+ }
+#endif
+ if(i2o_query_scalar_polled(c, unit, buf, 8, 0xF100, 6)>=0)
+ {
+ buf[8]=0;
+ printk(KERN_INFO " Rev: %s\n", buf);
+ }
+}
+
+
+/*
+ * Parse the hardware resource table. Right now we print it out
+ * and don't do a lot with it. We should collate these and then
+ * interact with the Linux resource allocation block.
+ *
+ * Lets prove we can read it first eh ?
+ *
+ * This is full of endianisms!
+ */
+
+static int i2o_parse_hrt(struct i2o_controller *c, u8 *p)
+{
+ u32 *rows=(u32 *)p;
+ u8 *d;
+ int count;
+ int length;
+ int i;
+ int state;
+
+ if(p[3]!=0)
+ {
+ printk(KERN_ERR "i2o: HRT table for controller is too new a version.\n");
+ return -1;
+ }
+
+ count=p[0]|(p[1]<<8);
+ length = p[2];
+
+ printk(KERN_INFO "HRT has %d entries of %d bytes each.\n",
+ count, length<<2);
+
+ rows+=2;
+
+ for(i=0;i<count;i++)
+ {
+ printk(KERN_INFO "Adapter %08X: ", rows[0]);
+ p=(u8 *)(rows+1);
+ d=(u8 *)(rows+2);
+ state=p[1]<<8|p[0];
+
+ printk("TID %04X:[", state&0xFFF);
+ state>>=12;
+ if(state&(1<<0))
+ printk("H"); /* Hidden */
+ if(state&(1<<2))
+ {
+ printk("P"); /* Present */
+ if(state&(1<<1))
+ printk("C"); /* Controlled */
+ }
+ if(state>9)
+ printk("*"); /* Hard */
+
+ printk("]:");
+
+ switch(p[3]&0xFFFF)
+ {
+ case 0:
+ /* Adapter private bus - easy */
+ printk("Local bus %d: I/O at 0x%04X Mem 0x%08X",
+ p[2], d[1]<<8|d[0], *(u32 *)(d+4));
+ break;
+ case 1:
+ /* ISA bus */
+ printk("ISA %d: CSN %d I/O at 0x%04X Mem 0x%08X",
+ p[2], d[2], d[1]<<8|d[0], *(u32 *)(d+4));
+ break;
+
+ case 2: /* EISA bus */
+ printk("EISA %d: Slot %d I/O at 0x%04X Mem 0x%08X",
+ p[2], d[3], d[1]<<8|d[0], *(u32 *)(d+4));
+ break;
+
+ case 3: /* MCA bus */
+ printk("MCA %d: Slot %d I/O at 0x%04X Mem 0x%08X",
+ p[2], d[3], d[1]<<8|d[0], *(u32 *)(d+4));
+ break;
+
+ case 4: /* PCI bus */
+ printk("PCI %d: Bus %d Device %d Function %d",
+ p[2], d[2], d[1], d[0]);
+ break;
+
+ case 0x80: /* Other */
+ default:
+ printk("Unsupported bus type.");
+ break;
+ }
+ printk("\n");
+ rows+=length;
+ }
+ return 0;
+}
+
+/*
+ * The logical configuration table tells us what we can talk to
+ * on the board. Most of the stuff isn't interesting to us.
+ */
+
+static int i2o_parse_lct(struct i2o_controller *c, u32 *lct)
+{
+ int i;
+ int max;
+ int tid;
+ u32 *p;
+ struct i2o_device *d;
+ char str[22];
+
+ max=lct[0]&0xFFFF;
+
+ max-=3;
+ max/=9;
+
+ printk(KERN_INFO "LCT has %d entries.\n", max);
+
+ if(max > 128)
+ {
+ printk(KERN_INFO "LCT was truncated.\n");
+ max=128;
+ }
+
+ if(lct[1]&(1<<0))
+ printk(KERN_WARNING "Configuration dialog desired.\n");
+
+ p=lct+3;
+
+ for(i=0;i<max;i++)
+ {
+ d = (struct i2o_device *)kmalloc(sizeof(struct i2o_device), GFP_KERNEL);
+ if(d==NULL)
+ {
+ printk("i2o_core: Out of memory for LCT data.\n");
+ return -ENOMEM;
+ }
+
+ d->controller = c;
+ d->next = NULL;
+
+ d->id = tid = (p[0]>>16)&0xFFF;
+ d->class = p[3]&0xFFF;
+ d->subclass = p[4]&0xFFF;
+ d->parent = (p[5]>>12)&0xFFF;
+ d->flags = 0;
+
+ printk(KERN_INFO "TID %d.\n", tid);
+
+ i2o_report_controller_unit(c, tid);
+
+ i2o_install_device(c, d);
+
+ printk(KERN_INFO " Class: ");
+
+ sprintf(str, "%-21s", i2o_get_class_name(d->class));
+ printk("%s", str);
+
+ printk(" Subclass: 0x%03X Flags: ",
+ d->subclass);
+
+ if(p[2]&(1<<0))
+ printk("C"); // ConfigDialog requested
+ if(p[2]&(1<<1))
+ printk("M"); // Multi-user capable
+ if(!(p[2]&(1<<4)))
+ printk("P"); // Peer service enabled!
+ if(!(p[2]&(1<<5)))
+ printk("m"); // Mgmt service enabled!
+ printk("\n");
+ p+=9;
+ }
+ return 0;
+}
+
+#if 0
+/* Reset the IOP to sane state */
+/* I think we need handler for core (or executive class in I2O terms) */
+static int i2o_reset_adapter(struct i2o_controller *c)
+{
+ u32 m;
+ u8 *work8;
+ u32 *msg;
+ long time;
+
+ /* First stop extral operations */
+ m=i2o_wait_message(c, "quiesce IOP");
+ if(m==0xFFFFFFFF)
+ return -ETIMEDOUT;
+
+ msg=(u32 *)(c->mem_offset+m);
+
+ msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1]=I2O_CMD_SYS_QUIESCE<<24|HOST_TID<<12|ADAPTER_TID;
+ msg[2]=0;
+ msg[3]=0;
+
+ i2o_post_message(c,m);
+
+ m=i2o_wait_reply(c, "System Quiesce timeout", 5);
+
+ if(m==0xFFFFFFFF)
+ return -ETIMEDOUT;
+
+ /* Then reset the IOP */
+ m=i2o_wait_message(c, "reset IOP");
+ if(m==0xFFFFFFFF)
+ return -ETIMEDOUT;
+
+ msg=(u32 *)(c->mem_offset+m);
+
+ work8=(void *)kmalloc(4, GFP_KERNEL);
+ if(work8==NULL) {
+ printk(KERN_ERR "IOP reset failed - no free memory.\n");
+ return -ENOMEM;
+ }
+
+ memset(work8, 0, 4);
+
+ msg[0]=EIGHT_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1]=I2O_CMD_ADAPTER_RESET<<24|HOST_TID<<12|ADAPTER_TID;
+ msg[2]=0;
+ msg[3]=0;
+ msg[4]=0;
+ msg[5]=0;
+ msg[6]=virt_to_phys(work8);
+ msg[7]=0; /* 64bit host FIXME */
+
+ i2o_post_message(c,m);
+
+ /* Wait for a reply */
+ time=jiffies;
+
+ while(work8[0]==0x01) {
+ if((jiffies-time)>=5*HZ) {
+ printk(KERN_ERR "IOP reset timeout.\n");
+ kfree(work8);
+ return -ETIMEDOUT;
+ }
+ schedule();
+ barrier();
+ }
+
+ if (work8[0]==0x02)
+ printk(KERN_WARNING "IOP Reset rejected\n");
+
+ return 0;
+}
+#endif
+
+/*
+ * Bring an I2O controller into HOLD state. See the 1.5
+ * spec. Basically we go
+ *
+ * Wait for the message queue to initialise.
+ * If it didnt -> controller is dead
+ *
+ * Send a get status using the message queue
+ * Poll for a reply block 88 bytes long
+ *
+ * Send an initialise outbound queue
+ * Poll for a reply
+ *
+ * Post our blank messages to the queue FIFO
+ *
+ * Send GetHRT, Parse it
+ */
+
+int i2o_activate_controller(struct i2o_controller *c)
+{
+ long time;
+ u32 m;
+ u8 *workspace;
+ u32 *msg;
+ int i;
+
+ printk(KERN_INFO "Configuring I2O controller at 0x%08X.\n", (u32)c->mem_phys);
+
+ /* First reset the IOP to sane state */
+// i2o_reset_adapter(c)
+
+ m=i2o_wait_message(c, "initialise");
+ if(m==0xFFFFFFFF)
+ return -ETIMEDOUT;
+
+ msg=(u32 *)(c->mem_offset+m);
+
+ workspace = (void *)kmalloc(88, GFP_KERNEL);
+ if(workspace==NULL)
+ {
+ printk(KERN_ERR "IOP initialisation failed - no free memory.\n");
+ return -ENOMEM;
+ }
+
+ memset(workspace, 0, 88);
+
+ msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1]=I2O_CMD_STATUS_GET<<24|HOST_TID<<12|ADAPTER_TID;
+ msg[2]=0;
+ msg[3]=0;
+ msg[4]=0;
+ msg[5]=0;
+ msg[6]=virt_to_phys(workspace);
+ msg[7]=0; /* 64bit host FIXME */
+ msg[8]=88;
+
+ i2o_post_message(c,m);
+
+ /*
+ * Wait for a reply
+ */
+
+ time=jiffies;
+
+ while(workspace[87]!=0xFF)
+ {
+ if((jiffies-time)>=5*HZ)
+ {
+ printk(KERN_ERR "IOP get status timeout.\n");
+ kfree(workspace);
+ return -ETIMEDOUT;
+ }
+ schedule();
+ barrier();
+ }
+
+ /*
+ * Ok the reply has arrived. Fill in the important stuff
+ */
+
+ c->status = workspace[10];
+ c->i2oversion = (workspace[9]>>4)&0xFF;
+ c->inbound_size = (workspace[12]|(workspace[13]<<8))*4; /* 32bit words */
+
+ /*
+ * If the board is running, reset it - we have no idea
+ * what kind of a mess the previous owner left it in.
+ */
+
+// if(c->status == ADAPTER_STATE_OPERATIONAL)
+// i2o_reset_device(c);
+
+
+ m=i2o_wait_message(c, "initqueue");
+ if(m==0xFFFFFFFF)
+ {
+ kfree(workspace);
+ return -ETIMEDOUT;
+ }
+
+ msg=(u32 *)(c->mem_offset+m);
+
+ msg[0]= EIGHT_WORD_MSG_SIZE| TRL_OFFSET_6;
+ msg[1]= I2O_CMD_OUTBOUND_INIT<<24 | HOST_TID<<12 | ADAPTER_TID;
+ msg[2]= 0;
+ msg[3]= 0x0106; /* Transaction context */
+ msg[4]= 4096; /* Host page frame size */
+ msg[5]= MSG_FRAME_SIZE<<16|0x80; /* Outbound msg frame size and Initcode */
+ msg[6]= 0xD0000004; /* Simple SG LE, EOB */
+ msg[7]= virt_to_phys(workspace);
+ *((u32 *)workspace)=0;
+
+ /*
+ * Post it
+ */
+
+ i2o_post_message(c,m);
+
+ barrier();
+
+ time=jiffies;
+
+ while(workspace[0]!=I2O_CMD_OUTBOUND_INIT_COMPLETE)
+ {
+ if((jiffies-time)>=5*HZ)
+ {
+ printk(KERN_ERR "IOP outbound initialise failed.\n");
+ kfree(workspace);
+ return -ETIMEDOUT;
+ }
+ schedule();
+ barrier();
+ }
+
+ kfree(workspace);
+
+ c->page_frame = kmalloc(MSG_POOL_SIZE, GFP_KERNEL);
+ if(c->page_frame==NULL)
+ {
+ printk(KERN_ERR "IOP init failed: no memory for message page.\n");
+ return -ENOMEM;
+ }
+
+ m=virt_to_phys(c->page_frame);
+
+ for(i=0; i< NMBR_MSG_FRAMES; i++)
+ {
+ I2O_REPLY_WRITE32(c,m);
+ mb();
+ m+=MSG_FRAME_SIZE;
+ }
+
+ /*
+ * The outbound queue is initialised and loaded,
+ *
+ * Now we need the Hardware Resource Table. We must ask for
+ * this next we can't issue random messages yet.
+ */
+
+
+ workspace=kmalloc(2048, GFP_KERNEL);
+ if(workspace==NULL)
+ {
+ printk(KERN_ERR "IOP init failed; no memory.\n");
+ return -ENOMEM;
+ }
+
+ m=i2o_wait_message(c, "I2O HRT timeout.");
+ if(m==0xFFFFFFFF)
+ {
+ kfree(workspace);
+ return -ETIMEDOUT;
+ }
+
+ msg=(u32 *)(c->mem_offset+m);
+
+ msg[0]= SIX_WORD_MSG_SIZE| SGL_OFFSET_4;
+ msg[1]= I2O_CMD_HRT_GET<<24 | HOST_TID<<12 | ADAPTER_TID;
+ msg[2]= 0x0;
+ msg[3]= 0x0; /* Transaction context */
+ msg[4]= (0xD0000000 | 2048); /* Simple transaction , 2K */
+ msg[5]= virt_to_phys(workspace); /* Dump it here */
+ *((u32 *)workspace)=0xFFFFFFFF;
+
+ i2o_post_message(c,m);
+
+ barrier();
+
+ /*
+ * Now wait for a reply
+ */
+
+ m=i2o_wait_reply(c, "HRT table", 5);
+
+ if(m==0xFFFFFFFF)
+ {
+ kfree(workspace);
+ return -ETIMEDOUT;
+ }
+
+ msg=(u32 *)bus_to_virt(m);
+
+ if(msg[4]>>24)
+ {
+ i2o_report_status(KERN_WARNING, "i2o_core",
+ (msg[1]>>24)&0xFF, (msg[4]>>24)&0xFF,
+ msg[4]&0xFFFF);
+ }
+ I2O_REPLY_WRITE32(c,m);
+
+ i2o_parse_hrt(c, workspace);
+
+ kfree(workspace);
+
+ return i2o_online_controller(c);
+// i2o_report_controller_unit(c, ADAPTER_TID);
+}
+
+
+/*
+ * Bring a controller online. Needs completing for multiple controllers
+ */
+
+int i2o_online_controller(struct i2o_controller *c)
+{
+ u32 m;
+ u32 *msg;
+ u32 systab[32];
+ u32 privmem[2];
+ u32 privio[2];
+ u32 *workspace;
+
+ systab[0]=1;
+ systab[1]=0;
+ systab[2]=0;
+ systab[3]=0;
+ systab[4]=0; /* Organisation ID */
+ systab[5]=2; /* Ident 2 for now */
+ systab[6]=0<<24|0<<16|I2OVERSION<<12|1; /* Memory mapped, IOPState, v1.5, segment 1 */
+ systab[7]=MSG_FRAME_SIZE>>2; /* Message size */
+ systab[8]=0; /* LastChanged */
+ systab[9]=0; /* Should be IOP capabilities */
+ systab[10]=virt_to_phys(c->post_port);
+ systab[11]=0;
+
+ privmem[0]=c->priv_mem; /* Private memory space base address */
+ privmem[1]=c->priv_mem_size;
+ privio[0]=c->priv_io; /* Private I/O address */
+ privio[1]=c->priv_io_size;
+
+ m=i2o_wait_message(c, "SetSysTab");
+ if(m==0xFFFFFFFF)
+ return -ETIMEDOUT;
+
+ /* Now we build the systab */
+ msg=(u32 *)(c->mem_offset+m);
+
+ msg[0] = NINE_WORD_MSG_SIZE|SGL_OFFSET_6;
+ msg[1] = I2O_CMD_SYS_TAB_SET<<24 | HOST_TID<<12 | ADAPTER_TID;
+ msg[2] = 0; /* Context not needed */
+ msg[3] = 0;
+ msg[4] = (1<<16)|(2<<12); /* Host 1 I2O 2 */
+ msg[5] = 1; /* Segment 1 */
+
+ /*
+ * Scatter Gather List
+ */
+
+ msg[6] = 0x54000000|48; /* One table for now */
+ msg[7] = virt_to_phys(systab);
+ msg[8] = 0xD4000000|48; /* One table for now */
+ msg[9] = virt_to_phys(privmem);
+/* msg[10] = virt_to_phys(privio); */
+
+ i2o_post_message(c,m);
+
+ barrier();
+
+ /*
+ * Now wait for a reply
+ */
+
+
+ m=i2o_wait_reply(c, "Systab read", 5);
+
+ if(m==0xFFFFFFFF)
+ return -ETIMEDOUT;
+
+ msg=(u32 *)bus_to_virt(m);
+
+ if(msg[4]>>24)
+ {
+ i2o_report_status(KERN_ERR, "i2o_core",
+ (msg[1]>>24)&0xFF, (msg[4]>>24)&0xFF,
+ msg[4]&0xFFFF);
+ }
+ I2O_REPLY_WRITE32(c,m);
+
+ /*
+ * Finally we go online
+ */
+
+ m=i2o_wait_message(c, "No message for SysEnable");
+
+ if(m==0xFFFFFFFF)
+ return -ETIMEDOUT;
+
+ msg=(u32 *)(c->mem_offset+m);
+
+ msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = I2O_CMD_SYS_ENABLE<<24 | HOST_TID<<12 | ADAPTER_TID;
+ msg[2] = 0; /* Context not needed */
+ msg[3] = 0;
+
+ i2o_post_message(c,m);
+
+ barrier();
+
+ /*
+ * Now wait for a reply
+ */
+
+
+ m=i2o_wait_reply(c, "Enable", 240);
+
+ if(m==0xFFFFFFFF)
+ return -ETIMEDOUT;
+
+ msg=(u32 *)bus_to_virt(m);
+
+ if(msg[4]>>24)
+ {
+ i2o_report_status(KERN_ERR, "i2o_core",
+ (msg[1]>>24)&0xFF, (msg[4]>>24)&0xFF,
+ msg[4]&0xFFFF);
+ }
+ I2O_REPLY_WRITE32(c,m);
+
+ /*
+ * Grab the LCT, see what is attached
+ */
+
+ m=i2o_wait_message(c, "No message for LCT");
+
+ if(m==0xFFFFFFFF)
+ return -ETIMEDOUT;
+
+ msg=(u32 *)(c->mem_offset+m);
+
+
+ workspace = kmalloc(8192, GFP_KERNEL);
+ if(workspace==NULL)
+ {
+ msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1]= HOST_TID<<12|ADAPTER_TID; /* NOP */
+ i2o_post_message(c,m);
+ printk(KERN_ERR "No free memory for i2o controller buffer.\n");
+ return -ENOMEM;
+ }
+
+ memset(workspace, 0, 8192);
+
+ msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_6;
+ msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID;
+ msg[2] = 0; /* Context not needed */
+ msg[3] = 0;
+ msg[4] = 0xFFFFFFFF; /* All devices */
+ msg[5] = 0x00000000; /* Report now */
+ msg[6] = 0xD0000000|8192;
+ msg[7] = virt_to_bus(workspace);
+
+ i2o_post_message(c,m);
+
+ barrier();
+
+ /*
+ * Now wait for a reply
+ */
+
+ m=i2o_wait_reply(c, "LCT", 5);
+
+ if(m==0xFFFFFFFF)
+ {
+ kfree(workspace);
+ return -ETIMEDOUT;
+ }
+
+ msg=(u32 *)bus_to_virt(m);
+
+ if(msg[4]>>24)
+ {
+ i2o_report_status(KERN_ERR, "i2o_core",
+ (msg[1]>>24)&0xFF, (msg[4]>>24)&0xFF,
+ msg[4]&0xFFFF);
+ }
+
+ i2o_parse_lct(c, workspace);
+ kfree(workspace);
+
+ I2O_REPLY_WRITE32(c,m);
+
+ return 0;
+}
+
+/*
+ * Run time support routines
+ */
+
+/*
+ * Generic "post and forget" helpers. This is less efficient - we do
+ * a memcpy for example that isnt strictly needed, but for most uses
+ * this is simply not worth optimising
+ */
+
+int i2o_post_this(struct i2o_controller *c, int tid, u32 *data, int len)
+{
+ u32 m;
+ u32 *msg;
+ unsigned long t=jiffies;
+
+ do
+ {
+ mb();
+ m = I2O_POST_READ32(c);
+ }
+ while(m==0xFFFFFFFF && (jiffies-t)<HZ);
+
+
+ if(m==0xFFFFFFFF)
+ {
+ printk(KERN_ERR "i2o: controller not responding.\n");
+ return -1;
+ }
+ msg = bus_to_virt(c->mem_offset + m);
+ memcpy(msg, data, len);
+ i2o_post_message(c,m);
+ return 0;
+}
+
+/*
+ * Post a message and wait for a response flag to be set. This API will
+ * change to use wait_queue's one day
+ */
+
+int i2o_post_wait(struct i2o_controller *c, int tid, u32 *data, int len, int *flag, int timeout)
+{
+ unsigned long t=jiffies;
+
+ *flag = 0;
+
+ if(i2o_post_this(c, tid, data, len))
+ return -1;
+
+ while(!*flag && (jiffies-t)<timeout*HZ)
+ {
+ schedule();
+ mb();
+ }
+ if(*flag <= 0)
+ return -1;
+ return 0;
+}
+
+/*
+ * Issue UTIL_CLAIM messages
+ */
+
+int i2o_issue_claim(struct i2o_controller *c, int tid, int context, int onoff, int *flag)
+{
+ u32 msg[6];
+
+ msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
+ if(onoff)
+ msg[1] = I2O_CMD_UTIL_CLAIM << 24 | HOST_TID<<12 | tid;
+ else
+ msg[1] = I2O_CMD_UTIL_RELEASE << 24 | HOST_TID << 12 | tid;
+
+ /* The 0x80000000 convention for flagging is assumed by this helper */
+
+ msg[2] = 0x80000000|context;
+ msg[3] = (u32)flag;
+ msg[4] = 0x01<<24; /* Primary user */
+
+ return i2o_post_wait(c, tid, msg, 20, flag,2);
+}
+
+/*
+ * Query a scalar value
+ */
+
+int i2o_query_scalar(struct i2o_controller *c, int tid, int context,
+ int group, int field, void *buf, int buflen, int *flag)
+{
+ u16 *op;
+ u32 *bl;
+ u32 msg[9];
+
+ bl=kmalloc(buflen+64, GFP_KERNEL); /* Enough space for error replys */
+ if(bl==NULL)
+ {
+ printk(KERN_ERR "i2o: no memory for query buffer.\n");
+ return -ENOMEM;
+ }
+
+ op = (u16*)bl;
+ op[0]=1; /* One Operation */
+ op[1]=0; /* PAD */
+ op[2]=1; /* FIELD_GET */
+ op[3]=group; /* group number */
+ op[4]=1; /* field count, default = 1 */
+ op[5]=field; /* field index */
+
+ if(field == -1)
+ /* Single value or the whole group? */
+ {
+ op[4]=-1;
+ op[5]=0;
+ }
+
+ msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_5;
+ msg[1]=I2O_CMD_UTIL_PARAMS_GET<<24|HOST_TID<<12|tid;
+ msg[2]=context|0x80000000; /* So we can pick it out */
+ msg[3]=(u32)flag;
+ msg[4]=0;
+ msg[5]=0x54000000|12;
+ msg[6]=virt_to_bus(bl);
+ /*
+ * There are 8 bytes of "overhead" required to pull in
+ * a Params ResultsList; 2 bytes for ResultCount
+ * (which should have value=1), plus 2 bytes for pad,
+ * plus 2 bytes for BlockSize, plus 1 byte BlockStatus,
+ * plus 1 byte ErrorInfoSize (8 bytes total overhead).
+ * This is followed finally by actual result value(s).
+ *
+ * Tell the IOP to return 8 + buflen bytes.
+ */
+ msg[7]=0xD0000000|(8+buflen);
+ msg[8]=virt_to_bus(bl+3);
+
+ bl[3]=0xFCFCFCFC; // Pad,ResultCount
+ bl[4]=0xFAFAFCFC; // ErrorInfoSize,BlockStatus,BlockSize
+
+ /*
+ * Post the message and await a reply
+ */
+
+ if (i2o_post_wait(c, tid, msg, sizeof(msg), flag,2) < 0)
+ {
+ kfree(bl);
+ return -1;
+ }
+
+ if(bl[4]&0x00FF00000) /* BlockStatus != SUCCESS */
+ {
+ printk(KERN_WARNING "i2o_query_scalar - Error\n"
+ "ErrorInfoSize = 0x%02x, BlockStatus = 0x%02x, "
+ "BlockSize = 0x%04x\n",
+ bl[4]>>24, (bl[4]>>16)&0xFF, bl[4]&0xFFFF);
+ kfree(bl);
+ return -1;
+ }
+ if((bl[3] & 0xFFFF) != 1)
+ {
+ printk(KERN_ERR "i2o: query ResultCount = 0x%04x\n", bl[3]&0xFFFF);
+ }
+
+ memcpy(buf, bl+5, buflen);
+ kfree(bl);
+ return 0;
+}
+
+
+#if 0
+/*
+ * Query a table field
+ * FIXME: NOT TESTED!
+ */
+int i2o_query_table(struct i2o_controller *c, int tid, int context,
+ void *buf, int buflen,
+ int table,
+ int *field, int fieldlen,
+ u32 *key, int keylen,
+ int *flag)
+{
+ static u16 op[32];
+ u32 *bl;
+ u32 msg[9];
+ int i;
+
+ bl=kmalloc(buflen+64, GFP_KERNEL);
+ if(bl==NULL)
+ {
+ printk(KERN_ERR "i2o: no memory for query buffer.\n");
+ return -ENOMEM;
+ }
+
+ op[0]=1; /* Operation count */
+ op[1]=0; /* Reserved */
+ op[2]=I2O_PARAMS_LIST_GET; /* Operation */
+ op[3]=table; /* Group */
+ /* Specific fields or the whole group? */
+ if(*field != -1)
+ { /* FIXME: Fields can be variable size */
+ op[4]=fieldlen;
+ for (i=0; i < fieldlen; i++)
+ op[4+i]=field[i];
+ }
+ else
+ {
+ op[4]=-1;
+ op[5]=0;
+ }
+
+ memcpy(bl, op, 12);
+
+ msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_5;
+ msg[1]=I2O_CMD_UTIL_PARAMS_GET<<24|HOST_TID<<12|tid;
+ msg[2]=context|0x80000000; /* So we can pick it out */
+ msg[3]=(u32)flag;
+ msg[4]=0;
+ msg[5]=0x54000000|12;
+ msg[6]=virt_to_bus(bl);
+
+ msg[7]=0xD0000000|(buflen+48);
+ msg[8]=virt_to_bus(bl+4);
+
+ /*
+ * Post the message and await a reply
+ */
+
+ if(i2o_post_wait(c, tid, msg, sizeof(msg), flag,2)<0)
+ return -1;
+
+ if(bl[5]&0x00FF00000) /* BlockStatus != SUCCESS */
+ {
+ printk(KERN_WARNING "i2o_query_table - Error\n"
+ "ErrorInfoSize = 0x%02x, BlockStatus = 0x%02x, "
+ "BlockSize = 0x%04x\n",
+ bl[5]>>24, (bl[5]>>16)&0xFF, bl[5]&0xFFFF);
+ kfree(bl);
+ return -1;
+ }
+
+ if((bl[4]&0xFFFF)!=1)
+ printk(KERN_ERR "i2o: query ResultCount = %0#4x\n",
+ bl[4]&0xFFFF);
+
+ memcpy(buf, bl+6, buflen);
+ kfree(bl);
+ return 0;
+}
+#endif
+
+/*
+ * Set (for now) scalar value
+ *
+ * TODO: Add support for table groups
+ */
+
+int i2o_params_set(struct i2o_controller *c, int tid, int context, int table,
+ int field, void *buf, int buflen, int *flag)
+{
+ static u16 opdata[]={1,0,6,0,1,4,0};
+ u32 *bl;
+ u32 msg[9];
+
+ bl=kmalloc(buflen+64, GFP_KERNEL);
+ if(bl==NULL)
+ {
+ printk(KERN_ERR "i2o: no memory for set buffer.\n");
+ return -ENOMEM;
+ }
+
+ opdata[3]=table;
+ /* Single value or the whole group? */
+ if(field != -1) {
+ opdata[4]=1;
+ opdata[5]=field;
+ opdata[6]=*(u16 *)buf;
+ }
+ else {
+ opdata[4]=-1;
+ opdata[5]=0;
+ }
+
+ memcpy(bl, opdata, 14);
+
+ msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_5;
+ msg[1]=I2O_CMD_UTIL_PARAMS_SET<<24|HOST_TID<<12|tid;
+ msg[2]=context|0x80000000; /* So we can pick it out */
+ msg[3]=(u32)flag;
+ msg[4]=0;
+ msg[5]=0x54000000|14;
+ msg[6]=virt_to_bus(bl);
+ msg[7]=0xD0000000|(buflen+48);
+ msg[8]=virt_to_bus(bl+4);
+
+ /* Post the message and wait for a reply */
+ if(i2o_post_wait(c, tid, msg, 36, flag, 5)<0)
+ {
+ kfree(bl);
+ return -1;
+ }
+
+ /* Perhaps we should check errors, eh? */
+ if(bl[5]&0x00FF00000) /* BlockStatus != SUCCESS */
+ {
+ printk(KERN_WARNING "i2o_params_set - Error\n"
+ "ErrorInfoSize = %0#2x, BlockStatus = %0#2x, "
+ "BlockSize = %0#4x\n",
+ bl[5]>>24, (bl[5]>>16)&0xFF, bl[5]&0xFFFF);
+ kfree(bl);
+ return -1;
+ }
+
+ if((bl[4] & 0xFFFF) != 1)
+ {
+ printk(KERN_ERR "i2o: params set ResultCount = %0#4x\n",
+ bl[4]&0xFFFF);
+ }
+
+ kfree(bl);
+ return 0;
+}
+
+
+void report_common_status(u8 req_status)
+{
+ /* the following reply status strings are common to all classes */
+
+ static char *REPLY_STATUS[] = {
+ "SUCCESS",
+ "ABORT_DIRTY",
+ "ABORT_NO_DATA_TRANSFER",
+ "ABORT_PARTIAL_TRANSFER",
+ "ERROR_DIRTY",
+ "ERROR_NO_DATA_TRANSFER",
+ "ERROR_PARTIAL_TRANSFER",
+ "PROCESS_ABORT_DIRTY",
+ "PROCESS_ABORT_NO_DATA_TRANSFER",
+ "PROCESS_ABORT_PARTIAL_TRANSFER",
+ "TRANSACTION_ERROR",
+ "PROGRESS_REPORT"
+ };
+
+ if (req_status > I2O_REPLY_STATUS_PROGRESS_REPORT)
+ printk("%0#4x / ", req_status);
+ else
+ printk("%s / ", REPLY_STATUS[req_status]);
+
+ return;
+}
+
+static void report_common_dsc(u16 detailed_status)
+{
+ /* The following detailed statuscodes are valid
+ - for executive class, utility class, DDM class and
+ - for transaction error replies
+ */
+
+ static char *COMMON_DSC[] = {
+ "SUCCESS",
+ "0x01", // not used
+ "BAD_KEY",
+ "TCL_ERROR",
+ "REPLY_BUFFER_FULL",
+ "NO_SUCH_PAGE",
+ "INSUFFICIENT_RESOURCE_SOFT",
+ "INSUFFICIENT_RESOURCE_HARD",
+ "0x08", // not used
+ "CHAIN_BUFFER_TOO_LARGE",
+ "UNSUPPORTED_FUNCTION",
+ "DEVICE_LOCKED",
+ "DEVICE_RESET",
+ "INAPPROPRIATE_FUNCTION",
+ "INVALID_INITIATOR_ADDRESS",
+ "INVALID_MESSAGE_FLAGS",
+ "INVALID_OFFSET",
+ "INVALID_PARAMETER",
+ "INVALID_REQUEST",
+ "INVALID_TARGET_ADDRESS",
+ "MESSAGE_TOO_LARGE",
+ "MESSAGE_TOO_SMALL",
+ "MISSING_PARAMETER",
+ "TIMEOUT",
+ "UNKNOWN_ERROR",
+ "UNKNOWN_FUNCTION",
+ "UNSUPPORTED_VERSION",
+ "DEVICE_BUSY",
+ "DEVICE_NOT_AVAILABLE"
+ };
+
+ if (detailed_status > I2O_DSC_DEVICE_NOT_AVAILABLE)
+ printk("%0#4x.\n", detailed_status);
+ else
+ printk("%s.\n", COMMON_DSC[detailed_status]);
+
+ return;
+}
+
+void report_lan_dsc(u16 detailed_status)
+{
+ static char *LAN_DSC[] = { // Lan detailed status code strings
+ "SUCCESS",
+ "DEVICE_FAILURE",
+ "DESTINATION_NOT_FOUND",
+ "TRANSMIT_ERROR",
+ "TRANSMIT_ABORTED",
+ "RECEIVE_ERROR",
+ "RECEIVE_ABORTED",
+ "DMA_ERROR",
+ "BAD_PACKET_DETECTED",
+ "OUT_OF_MEMORY",
+ "BUCKET_OVERRUN",
+ "IOP_INTERNAL_ERROR",
+ "CANCELED",
+ "INVALID_TRANSACTION_CONTEXT",
+ "DEST_ADDRESS_DETECTED",
+ "DEST_ADDRESS_OMITTED",
+ "PARTIAL_PACKET_RETURNED",
+ "TEMP_SUSPENDED_STATE"
+ };
+
+ if (detailed_status > I2O_LAN_DSC_TEMP_SUSPENDED_STATE)
+ printk("%0#4x.\n", detailed_status);
+ else
+ printk("%s.\n", LAN_DSC[detailed_status]);
+
+ return;
+}
+
+static void report_util_cmd(u8 cmd)
+{
+ switch (cmd) {
+ case I2O_CMD_UTIL_NOP:
+ printk("UTIL_NOP, ");
+ break;
+ case I2O_CMD_UTIL_ABORT:
+ printk("UTIL_ABORT, ");
+ break;
+ case I2O_CMD_UTIL_CLAIM:
+ printk("UTIL_CLAIM, ");
+ break;
+ case I2O_CMD_UTIL_RELEASE:
+ printk("UTIL_CLAIM_RELEASE, ");
+ break;
+ case I2O_CMD_UTIL_CONFIG_DIALOG:
+ printk("UTIL_CONFIG_DIALOG, ");
+ break;
+ case I2O_CMD_UTIL_DEVICE_RESERVE:
+ printk("UTIL_DEVICE_RESERVE, ");
+ break;
+ case I2O_CMD_UTIL_DEVICE_RELEASE:
+ printk("UTIL_DEVICE_RELEASE, ");
+ break;
+ case I2O_CMD_UTIL_ACK:
+ printk("UTIL_EVENT_ACKNOWLEDGE, ");
+ break;
+ case I2O_CMD_UTIL_EVT_REGISTER:
+ printk("UTIL_EVENT_REGISTER, ");
+ break;
+ case I2O_CMD_UTIL_LOCK:
+ printk("UTIL_LOCK, ");
+ break;
+ case I2O_CMD_UTIL_LOCK_RELEASE:
+ printk("UTIL_LOCK_RELEASE, ");
+ break;
+ case I2O_CMD_UTIL_PARAMS_GET:
+ printk("UTIL_PARAMS_GET, ");
+ break;
+ case I2O_CMD_UTIL_PARAMS_SET:
+ printk("UTIL_PARAMS_SET, ");
+ break;
+ case I2O_CMD_UTIL_REPLY_FAULT_NOTIFY:
+ printk("UTIL_REPLY_FAULT_NOTIFY, ");
+ break;
+ default:
+ printk("%0#2x, ",cmd);
+ }
+
+ return;
+}
+
+
+static void report_exec_cmd(u8 cmd)
+{
+ switch (cmd) {
+ case I2O_CMD_ADAPTER_ASSIGN:
+ printk("EXEC_ADAPTER_ASSIGN, ");
+ break;
+ case I2O_CMD_ADAPTER_READ:
+ printk("EXEC_ADAPTER_READ, ");
+ break;
+ case I2O_CMD_ADAPTER_RELEASE:
+ printk("EXEC_ADAPTER_RELEASE, ");
+ break;
+ case I2O_CMD_BIOS_INFO_SET:
+ printk("EXEC_BIOS_INFO_SET, ");
+ break;
+ case I2O_CMD_BOOT_DEVICE_SET:
+ printk("EXEC_BOOT_DEVICE_SET, ");
+ break;
+ case I2O_CMD_CONFIG_VALIDATE:
+ printk("EXEC_CONFIG_VALIDATE, ");
+ break;
+ case I2O_CMD_CONN_SETUP:
+ printk("EXEC_CONN_SETUP, ");
+ break;
+ case I2O_CMD_DDM_DESTROY:
+ printk("EXEC_DDM_DESTROY, ");
+ break;
+ case I2O_CMD_DDM_ENABLE:
+ printk("EXEC_DDM_ENABLE, ");
+ break;
+ case I2O_CMD_DDM_QUIESCE:
+ printk("EXEC_DDM_QUIESCE, ");
+ break;
+ case I2O_CMD_DDM_RESET:
+ printk("EXEC_DDM_RESET, ");
+ break;
+ case I2O_CMD_DDM_SUSPEND:
+ printk("EXEC_DDM_SUSPEND, ");
+ break;
+ case I2O_CMD_DEVICE_ASSIGN:
+ printk("EXEC_DEVICE_ASSIGN, ");
+ break;
+ case I2O_CMD_DEVICE_RELEASE:
+ printk("EXEC_DEVICE_RELEASE, ");
+ break;
+ case I2O_CMD_HRT_GET:
+ printk("EXEC_HRT_GET, ");
+ break;
+ case I2O_CMD_ADAPTER_CLEAR:
+ printk("EXEC_IOP_CLEAR, ");
+ break;
+ case I2O_CMD_ADAPTER_CONNECT:
+ printk("EXEC_IOP_CONNECT, ");
+ break;
+ case I2O_CMD_ADAPTER_RESET:
+ printk("EXEC_IOP_RESET, ");
+ break;
+ case I2O_CMD_LCT_NOTIFY:
+ printk("EXEC_LCT_NOTIFY, ");
+ break;
+ case I2O_CMD_OUTBOUND_INIT:
+ printk("EXEC_OUTBOUND_INIT, ");
+ break;
+ case I2O_CMD_PATH_ENABLE:
+ printk("EXEC_PATH_ENABLE, ");
+ break;
+ case I2O_CMD_PATH_QUIESCE:
+ printk("EXEC_PATH_QUIESCE, ");
+ break;
+ case I2O_CMD_PATH_RESET:
+ printk("EXEC_PATH_RESET, ");
+ break;
+ case I2O_CMD_STATIC_MF_CREATE:
+ printk("EXEC_STATIC_MF_CREATE, ");
+ break;
+ case I2O_CMD_STATIC_MF_RELEASE:
+ printk("EXEC_STATIC_MF_RELEASE, ");
+ break;
+ case I2O_CMD_STATUS_GET:
+ printk("EXEC_STATUS_GET, ");
+ break;
+ case I2O_CMD_SW_DOWNLOAD:
+ printk("EXEC_SW_DOWNLOAD, ");
+ break;
+ case I2O_CMD_SW_UPLOAD:
+ printk("EXEC_SW_UPLOAD, ");
+ break;
+ case I2O_CMD_SW_REMOVE:
+ printk("EXEC_SW_REMOVE, ");
+ break;
+ case I2O_CMD_SYS_ENABLE:
+ printk("EXEC_SYS_ENABLE, ");
+ break;
+ case I2O_CMD_SYS_MODIFY:
+ printk("EXEC_SYS_MODIFY, ");
+ break;
+ case I2O_CMD_SYS_QUIESCE:
+ printk("EXEC_SYS_QUIESCE, ");
+ break;
+ case I2O_CMD_SYS_TAB_SET:
+ printk("EXEC_SYS_TAB_SET, ");
+ break;
+ default:
+ printk("%02x, ",cmd);
+ }
+
+ return;
+}
+
+static void report_lan_cmd(u8 cmd)
+{
+ switch (cmd) {
+ case LAN_PACKET_SEND:
+ printk("LAN_PACKET_SEND, ");
+ break;
+ case LAN_SDU_SEND:
+ printk("LAN_SDU_SEND, ");
+ break;
+ case LAN_RECEIVE_POST:
+ printk("LAN_RECEIVE_POST, ");
+ break;
+ case LAN_RESET:
+ printk("LAN_RESET, ");
+ break;
+ case LAN_SUSPEND:
+ printk("LAN_SUSPEND, ");
+ break;
+ default:
+ printk("%02x, ",cmd);
+ }
+
+ return;
+}
+
+/* TODO: Add support for other classes */
+void i2o_report_status(const char *severity, const char *module, u8 cmd,
+ u8 req_status, u16 detailed_status)
+{
+ printk("%s", severity);
+ printk("%s: ", module);
+
+ if (cmd < 0x1F) { // Utility Class
+ report_util_cmd(cmd);
+ report_common_status(req_status);
+ report_common_dsc(detailed_status);
+ return;
+ }
+
+ if (cmd >= 0x30 && cmd <= 0x3F) { // LAN class
+ report_lan_cmd(cmd);
+ report_common_status(req_status);
+ report_lan_dsc(detailed_status);
+ return;
+ }
+
+ if (cmd >= 0xA0 && cmd <= 0xEF) { // Executive class
+ report_exec_cmd(cmd);
+ report_common_status(req_status);
+ report_common_dsc(detailed_status);
+ return;
+ }
+
+ printk("%02x, %02x / %04x.\n", cmd, req_status, detailed_status);
+ return;
+}
+
+
+EXPORT_SYMBOL(i2o_install_handler);
+EXPORT_SYMBOL(i2o_remove_handler);
+EXPORT_SYMBOL(i2o_install_device);
+EXPORT_SYMBOL(i2o_delete_device);
+EXPORT_SYMBOL(i2o_quiesce_controller);
+EXPORT_SYMBOL(i2o_clear_controller);
+EXPORT_SYMBOL(i2o_install_controller);
+EXPORT_SYMBOL(i2o_delete_controller);
+EXPORT_SYMBOL(i2o_unlock_controller);
+EXPORT_SYMBOL(i2o_find_controller);
+EXPORT_SYMBOL(i2o_num_controllers);
+EXPORT_SYMBOL(i2o_claim_device);
+EXPORT_SYMBOL(i2o_release_device);
+EXPORT_SYMBOL(i2o_run_queue);
+EXPORT_SYMBOL(i2o_report_controller_unit);
+EXPORT_SYMBOL(i2o_activate_controller);
+EXPORT_SYMBOL(i2o_online_controller);
+EXPORT_SYMBOL(i2o_get_class_name);
+
+EXPORT_SYMBOL(i2o_query_scalar);
+EXPORT_SYMBOL(i2o_params_set);
+EXPORT_SYMBOL(i2o_post_this);
+EXPORT_SYMBOL(i2o_post_wait);
+EXPORT_SYMBOL(i2o_issue_claim);
+
+EXPORT_SYMBOL(i2o_report_status);
+EXPORT_SYMBOL(report_common_status);
+EXPORT_SYMBOL(report_lan_dsc);
+
+EXPORT_SYMBOL(i2o_wait_message);
+
+MODULE_AUTHOR("Red Hat Software");
+MODULE_DESCRIPTION("I2O Core");
diff --git a/drivers/i2o/i2o_lan.c b/drivers/i2o/i2o_lan.c
new file mode 100644
index 000000000..1ebbe0b49
--- /dev/null
+++ b/drivers/i2o/i2o_lan.c
@@ -0,0 +1,853 @@
+/*
+ * linux/drivers/i2o/i2o_lan.c
+ *
+ * I2O LAN CLASS OSM Prototyping, May 7th 1999
+ *
+ * (C) Copyright 1999 University of Helsinki,
+ * Department of Computer Science
+ *
+ * This code is still under development / test.
+ *
+ * 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.
+ *
+ * Author: Auvo Häkkinen <Auvo.Hakkinen@cs.Helsinki.FI>
+ *
+ * Tested: in FDDI environment (using SysKonnect's DDM)
+ * in ETH environment (using Intel 82558 DDM proto)
+ *
+ * TODO: batch mode networking
+ * - this one assumes that we always get one packet in a bucket
+ * - we've not been able to test batch replies and batch receives
+ * error checking / timeouts
+ * - code/test for other LAN classes
+ */
+
+#include <linux/module.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/fddidevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/malloc.h>
+#include <linux/trdevice.h>
+#include <asm/io.h>
+
+#include <linux/errno.h>
+
+#include <linux/i2o.h>
+#include "i2o_lan.h"
+
+//#define DRIVERDEBUG
+#ifdef DRIVERDEBUG
+#define dprintk(s, args...) printk(s, ## args)
+#else
+#define dprintk(s, args...)
+#endif
+
+#define MAX_LAN_CARDS 4
+static struct device *i2o_landevs[MAX_LAN_CARDS+1];
+static int unit = -1; /* device unit number */
+
+struct i2o_lan_local {
+ u8 unit;
+ struct i2o_device *i2o_dev;
+ int reply_flag; // needed by scalar/table queries
+ struct fddi_statistics stats;
+/* first fields are same as in struct net_device_stats stats; */
+ unsigned short (*type_trans)(struct sk_buff *, struct device *);
+};
+
+/* function prototypes */
+static int i2o_lan_receive_post(struct device *dev);
+static int i2o_lan_receive_post_reply(struct device *dev, struct i2o_message *m);
+
+
+static void i2o_lan_reply(struct i2o_handler *h, struct i2o_controller *iop,
+ struct i2o_message *m)
+{
+ u32 *msg = (u32 *)m;
+ u8 unit = (u8)(msg[2]>>16); // InitiatorContext
+ struct device *dev = i2o_landevs[unit];
+
+#ifdef DRIVERDEBUG
+ i2o_report_status(KERN_INFO, "i2o_lan", msg[1]>>24, msg[4]>>24,
+ msg[4]&0xFFFF);
+#endif
+ if (msg[0] & (1<<13)) // Fail bit is set
+ {
+ printk(KERN_INFO "IOP failed to process the msg\n");
+ printk("From tid=%d to tid=%d",(msg[1]>>12)&0xFFF,msg[1]&0xFFF);
+ return;
+ }
+
+ switch (msg[1] >> 24) {
+ case LAN_RECEIVE_POST:
+ if (dev->start)
+ i2o_lan_receive_post_reply(dev,m);
+ else {
+ // we are getting unused buckets back
+ u8 trl_count = msg[3] & 0x000000FF;
+ struct i2o_bucket_descriptor *bucket =
+ (struct i2o_bucket_descriptor *)&msg[6];
+ struct sk_buff *skb;
+ do {
+ dprintk("Releasing unused bucket\n");
+ skb = (struct sk_buff *)bucket->context;
+ dev_kfree_skb(skb);
+ bucket++;
+ } while (--trl_count);
+ }
+ break;
+
+ case LAN_PACKET_SEND:
+ case LAN_SDU_SEND:
+ {
+ u8 trl_count = msg[3] & 0x000000FF;
+
+ if (msg[4] >> 24) // ReqStatus != SUCCESS
+ {
+ printk(KERN_WARNING "%s: ",dev->name);
+ report_common_status(msg[4]>>24);
+ report_lan_dsc(msg[4]&0xFFFF);
+ }
+
+ do { // The HDM has handled the outgoing packet
+ dev_kfree_skb((struct sk_buff *)msg[4 + trl_count]);
+ dprintk(KERN_INFO "%s: Request skb freed (trl_count=%d).\n",
+ dev->name,trl_count);
+ } while (--trl_count);
+
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* inform upper layers */
+ }
+ break;
+
+ default:
+ if (msg[2] & 0x80000000) // reply to a util get/set
+ { // flag for the i2o_post_wait
+ int *flag = (int *)msg[3];
+ // ReqStatus != I2O_REPLY_STATUS_SUCCESS
+ *flag = (msg[4] >> 24) ? I2O_POST_WAIT_TIMEOUT
+ : I2O_POST_WAIT_OK ;
+ }
+ }
+}
+
+static struct i2o_handler i2o_lan_handler =
+{
+ i2o_lan_reply,
+ "I2O Lan OSM",
+ 0 // context
+};
+static int lan_context;
+
+
+static int i2o_lan_receive_post_reply(struct device *dev, struct i2o_message *m)
+{
+ u32 *msg = (u32 *)m;
+ struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;
+ struct i2o_bucket_descriptor *bucket = (struct i2o_bucket_descriptor *)&msg[6];
+ struct i2o_packet_info *packet;
+
+ u8 trl_count = msg[3] & 0x000000FF;
+ struct sk_buff *skb;
+
+#ifdef 0
+ dprintk(KERN_INFO "TrlFlags = 0x%02X, TrlElementSize = %d, TrlCount = %d\n"
+ "msgsize = %d, buckets_remaining = %d\n",
+ msg[3]>>24, msg[3]&0x0000FF00, trl_count, msg[0]>>16, msg[5]);
+#endif
+
+/*
+ * NOTE: here we assume that also in batch mode we will get only
+ * one packet per bucket. This can be ensured by setting the
+ * PacketOrphanLimit to MaxPacketSize, as well as the bucket size.
+ */
+ do {
+ /* packet is not at all needed here */
+ packet = (struct i2o_packet_info *)bucket->packet_info;
+#ifdef 0
+ dprintk(KERN_INFO "flags = 0x%02X, offset = 0x%06X, status = 0x%02X, length = %d\n",
+ packet->flags, packet->offset, packet->status, packet->len);
+#endif
+ skb = (struct sk_buff *)(bucket->context);
+ skb_put(skb,packet->len);
+ skb->dev = dev;
+ skb->protocol = priv->type_trans(skb, dev);
+ netif_rx(skb);
+
+ dprintk(KERN_INFO "%s: Incoming packet (%d bytes) delivered "
+ "to upper level.\n",dev->name,packet->len);
+
+ bucket++; // to next Packet Descriptor Block
+
+ } while (--trl_count);
+
+ if (msg[5] <= I2O_BUCKET_THRESH) // BucketsRemaining
+ i2o_lan_receive_post(dev);
+
+ return 0;
+}
+
+/* ====================================================
+ * Interface to i2o: functions to send lan class request
+ */
+
+/*
+ * i2o_lan_receive_post(): Post buckets to receive packets.
+ */
+static int i2o_lan_receive_post(struct device *dev)
+{
+ struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;
+ struct i2o_device *i2o_dev = priv->i2o_dev;
+ struct i2o_controller *iop = i2o_dev->controller;
+ struct sk_buff *skb;
+ u32 m; u32 *msg;
+
+ u32 bucket_len = (dev->mtu + dev->hard_header_len);
+ u32 bucket_count;
+ int n_elems = (iop->inbound_size - 16 ) / 12; // msg header + SGLs
+ u32 total = 0;
+ int i;
+
+ dprintk(KERN_INFO "%s: Allocating %d buckets (size %d).\n",
+ dev->name, I2O_BUCKET_COUNT, bucket_len);
+
+ while (total < I2O_BUCKET_COUNT)
+ {
+ m = I2O_POST_READ32(iop);
+ if (m == 0xFFFFFFFF)
+ return -ETIMEDOUT;
+ msg = bus_to_virt(iop->mem_offset + m);
+
+ bucket_count = (total + n_elems < I2O_BUCKET_COUNT)
+ ? n_elems
+ : I2O_BUCKET_COUNT - total;
+
+ msg[0] = I2O_MESSAGE_SIZE(4 + 3 * bucket_count) | 1<<12 | SGL_OFFSET_4;
+ msg[1] = LAN_RECEIVE_POST<<24 | HOST_TID<<12 | i2o_dev->id;
+ msg[2] = priv->unit << 16 | lan_context; // InitiatorContext
+ msg[3] = bucket_count; // BucketCount
+
+ for (i = 0; i < bucket_count; i++)
+ {
+ skb = dev_alloc_skb(bucket_len + 2);
+ if (skb == NULL)
+ return -ENOMEM;
+ skb_reserve(skb, 2);
+ msg[4 + 3*i] = 0x51000000 | bucket_len;
+ msg[5 + 3*i] = (u32)skb;
+ msg[6 + 3*i] = virt_to_bus(skb->data);
+ }
+ msg[4 + 3*i - 3] |= 0x80000000; // set LE flag
+ i2o_post_message(iop,m);
+
+ dprintk(KERN_INFO "%s: Sending %d buckets (size %d) to LAN HDM.\n",
+ dev->name,bucket_count,bucket_len);
+
+ total += bucket_count;
+ }
+ return 0;
+}
+
+/*
+ * i2o_lan_reset(): Reset the LAN adapter into the operational state and
+ * restore it to full operation.
+ */
+static int i2o_lan_reset(struct device *dev)
+{
+ struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;
+ struct i2o_device *i2o_dev = priv->i2o_dev;
+ struct i2o_controller *iop = i2o_dev->controller;
+ u32 m; u32 *msg;
+
+ m = I2O_POST_READ32(iop);
+ if (m == 0xFFFFFFFF)
+ return -ETIMEDOUT;
+ msg = bus_to_virt(iop->mem_offset + m);
+
+ msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
+ msg[1] = LAN_RESET<<24 | HOST_TID<<12 | i2o_dev->id;
+ msg[2] = priv->unit << 16 | lan_context; // InitiatorContext
+ msg[3] = 0; // TransactionContext
+ msg[4] = 1 << 16; // return posted buckets
+
+ i2o_post_message(iop,m);
+
+ return 0;
+}
+
+/*
+ * i2o_lan_suspend(): Put LAN adapter into a safe, non-active state.
+ * Reply to any LAN class message with status error_no_data_transfer
+ * / suspended.
+ */
+static int i2o_lan_suspend(struct device *dev)
+{
+ struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;
+ struct i2o_device *i2o_dev = priv->i2o_dev;
+ struct i2o_controller *iop = i2o_dev->controller;
+ u32 m; u32 *msg;
+
+ m = I2O_POST_READ32(iop);
+ if (m == 0xFFFFFFFF)
+ return -ETIMEDOUT;
+ msg = bus_to_virt(iop->mem_offset + m);
+
+ msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
+ msg[1] = LAN_SUSPEND<<24 | HOST_TID<<12 | i2o_dev->id;
+ msg[2] = priv->unit << 16 | lan_context; // InitiatorContext
+ msg[3] = 0; // TransactionContext
+ msg[4] = 1 << 16; // return posted buckets
+
+ i2o_post_message(iop,m);
+
+ return 0;
+}
+
+/*
+ * Set DDM into batch mode.
+ */
+static void i2o_set_batch_mode(struct device *dev)
+{
+
+/*
+ * NOTE: we have not been able to test batch mode
+ * since HDMs we have, don't implement it
+ */
+
+ struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;
+ struct i2o_device *i2o_dev = priv->i2o_dev;
+ struct i2o_controller *iop = i2o_dev->controller;
+ u32 val;
+
+ /* set LAN_BATCH_CONTROL attributes */
+
+ // enable batch mode, toggle automatically
+ val = 0x00000000;
+ if (i2o_params_set(iop, i2o_dev->id, lan_context, 0x0003, 0,
+ &val, 4, &priv->reply_flag) <0)
+ printk(KERN_WARNING "Unable to enter I2O LAN batch mode.\n");
+ else
+ dprintk(KERN_INFO "%s: I2O LAN batch mode enabled.\n",dev->name);
+
+ /*
+ * When PacketOrphanlimit is same as the maximum packet length,
+ * the packets will never be split into two separate buckets
+ */
+
+ /* set LAN_OPERATION attributes */
+
+ val = dev->mtu + dev->hard_header_len; // PacketOrphanLimit
+ if (i2o_params_set(iop, i2o_dev->id, lan_context, 0x0004, 2,
+ &val, 4, &priv->reply_flag) < 0)
+ printk(KERN_WARNING "i2o_lan: Unable to set PacketOrphanLimit.\n");
+ else
+ dprintk(KERN_INFO "PacketOrphanLimit set to %d\n",val);
+
+#ifdef 0
+/*
+ * I2O spec 2.0: there should be proper default values for other attributes
+ * used in batch mode.
+ */
+
+ /* set LAN_RECEIVE_INFO attributes */
+
+ val = 10; // RxMaxBucketsReply
+ if (i2o_params_set(iop, i2o_dev->id, lan_context, 0x0008, 3,
+ &val, 4, &priv->reply_flag) < 0)
+ printk(KERN_WARNING "%s: Unable to set RxMaxBucketsReply.\n",
+ dev->name);
+
+ val = 10; // RxMaxPacketsBuckets
+ if (i2o_params_set(iop, i2o_dev->id, lan_context, 0x0008, 4,
+ &val, 4, &priv->reply_flag) < 0)
+ printk(KERN_WARNING "%s: Unable to set RxMaxPacketsBucket.\n",
+ dev->name);
+
+ /* set LAN_BATCH_CONTROL attributes */
+
+ val = 10; // MaxRxBatchCount
+ if (i2o_params_set(iop, i2o_dev->id, lan_context, 0x0003, 5,
+ &val, 4, &priv->reply_flag) < 0)
+ printk(KERN_WARNING "%s: Unable to set MaxRxBatchCount.\n",
+ dev->name);
+
+ val = 10; // MaxTxBatchCount
+ if (i2o_params_set(iop, i2o_dev->id, lan_context, 0x0003, 8,
+ &val, 4, &priv->reply_flag) < 0)
+ printk(KERN_WARNING "%s Unable to set MaxTxBatchCount.\n",
+ dev->name);
+#endif
+
+ return;
+}
+
+/*
+ * i2o_lan_open(): Open the device to send/receive packets via
+ * the network device.
+ */
+static int i2o_lan_open(struct device *dev)
+{
+ struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;
+ struct i2o_device *i2o_dev = priv->i2o_dev;
+ struct i2o_controller *iop = i2o_dev->controller;
+
+ i2o_lan_reset(dev);
+
+ if (i2o_issue_claim(iop, i2o_dev->id, lan_context, 1,
+ &priv->reply_flag) < 0)
+ {
+ printk(KERN_WARNING "%s: Unable to claim the I2O LAN device.\n", dev->name);
+ return -EAGAIN;
+ }
+ dprintk(KERN_INFO "%s: I2O LAN device claimed (tid=%d).\n", dev->name, i2o_dev->id);
+
+ dev->tbusy = 0;
+ dev->start = 1;
+
+ i2o_set_batch_mode(dev);
+ i2o_lan_receive_post(dev);
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+/*
+ * i2o_lan_close(): End the transfering.
+ */
+static int i2o_lan_close(struct device *dev)
+{
+ struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;
+ struct i2o_device *i2o_dev = priv->i2o_dev;
+ struct i2o_controller *iop = i2o_dev->controller;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ if (i2o_issue_claim(iop, i2o_dev->id, lan_context, 0,
+ &priv->reply_flag) < 0)
+ {
+ printk(KERN_WARNING "%s: Unable to unclaim I2O LAN device (tid=%d)\n",
+ dev->name, i2o_dev->id);
+ }
+
+ i2o_lan_suspend(dev);
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+/*
+ * i2o_lan_sdu_send(): Send a packet, MAC header added by the HDM.
+ * Must be supported by Fibre Channel, optional for Ethernet/802.3,
+ * Token Ring, FDDI
+ */
+static int i2o_lan_sdu_send(struct sk_buff *skb, struct device *dev)
+{
+#ifdef 0
+/* not yet tested */
+ struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;
+ struct i2o_device *i2o_dev = priv->i2o_dev;
+ struct i2o_controller *iop = i2o_dev->controller;
+ u32 m; u32 *msg;
+
+ dprintk(KERN_INFO "LanSDUSend called, skb->len = %d\n", skb->len);
+
+ m = *iop->post_port;
+ if (m == 0xFFFFFFFF)
+ {
+ dev_kfree_skb(skb);
+ return -1;
+ }
+ msg = bus_to_virt(iop->mem_offset + m);
+
+ msg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_4;
+ msg[1] = LAN_SDU_SEND<<24 | HOST_TID<<12 | i2o_dev->id;
+ msg[2] = priv->unit << 16 | lan_context; // IntiatorContext
+ msg[3] = 1<<4; // TransmitControlWord: suppress CRC generation
+
+ // create a simple SGL, see fig. 3-26
+ // D7 = 1101 0111 = LE eob 0 1 LA dir bc1 bc0
+
+ msg[4] = 0xD7000000 | (skb->len); // no MAC hdr included
+ msg[5] = (u32)skb; // TransactionContext
+ memcpy(&msg[6], skb->data, 8); // Destination MAC Addr ??
+ msg[7] &= 0x0000FFFF; // followed by two bytes zeros
+ msg[8] = virt_to_bus(skb->data);
+ dev->trans_start = jiffies;
+ i2o_post_message(iop,m);
+
+ dprintk(KERN_INFO "%s: Packet (%d bytes) sent to network.\n",
+ dev->name,skb->len);
+#endif
+ return 0;
+}
+
+/*
+ * i2o_lan_packet_send(): Send a packet as is, including the MAC header.
+ *
+ * Must be supported by Ethernet/802.3, Token Ring, FDDI, optional for
+ * Fibre Channel
+ */
+static int i2o_lan_packet_send(struct sk_buff *skb, struct device *dev)
+{
+ struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;
+ struct i2o_device *i2o_dev = priv->i2o_dev;
+ struct i2o_controller *iop = i2o_dev->controller;
+ u32 m; u32 *msg;
+
+ m = *iop->post_port;
+ if (m == 0xFFFFFFFF) {
+ dev_kfree_skb(skb);
+ return -1;
+ }
+
+ msg = bus_to_virt(iop->mem_offset + m);
+
+ msg[0] = SEVEN_WORD_MSG_SIZE | 1<<12 | SGL_OFFSET_4;
+ msg[1] = LAN_PACKET_SEND<<24 | HOST_TID<<12 | i2o_dev->id;
+ msg[2] = priv->unit << 16 | lan_context; // IntiatorContext
+ msg[3] = 1 << 4; // TransmitControlWord
+
+ // create a simple SGL, see fig. 3-26
+ // D5 = 1101 0101 = LE eob 0 1 LA dir bc1 bc0
+
+ msg[4] = 0xD5000000 | skb->len; // MAC hdr included
+ msg[5] = (u32)skb; // TransactionContext
+ msg[6] = virt_to_bus(skb->data);
+
+ i2o_post_message(iop,m);
+
+ dprintk(KERN_INFO "%s: Packet (%d bytes) sent to network.\n",
+ dev->name, skb->len);
+
+ return 0;
+}
+
+/*
+ * net_device_stats(): Return statistical information.
+ */
+static struct net_device_stats *i2o_lan_get_stats(struct device *dev)
+{
+ struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;
+ struct i2o_device *i2o_dev = priv->i2o_dev;
+ struct i2o_controller *iop = i2o_dev->controller;
+ u64 val[16];
+
+ /* query LAN_HISTORICAL_STATS scalar parameter group 0x0100 */
+
+ i2o_query_scalar(iop, i2o_dev->id, lan_context, 0x0100, -1,
+ &val, 16*8, &priv->reply_flag);
+ priv->stats.tx_packets = val[0];
+ priv->stats.tx_bytes = val[1];
+ priv->stats.rx_packets = val[2];
+ priv->stats.rx_bytes = val[3];
+ priv->stats.tx_errors = val[4];
+ priv->stats.rx_errors = val[5];
+ priv->stats.rx_dropped = val[6];
+
+ // other net_device_stats and FDDI class specific fields follow ...
+
+ return (struct net_device_stats *)&priv->stats;
+}
+
+/*
+ * i2o_lan_set_multicast_list(): Enable a network device to receive packets
+ * not send to the protocol address.
+ */
+static void i2o_lan_set_multicast_list(struct device *dev)
+{
+ struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;
+ struct i2o_device *i2o_dev = priv->i2o_dev;
+ struct i2o_controller *iop = i2o_dev->controller;
+ u32 filter_mask;
+
+ dprintk(KERN_INFO "Entered i2o_lan_set_multicast_list().\n");
+
+return;
+
+/*
+ * FIXME: For some reason this kills interrupt handler in i2o_post_wait :-(
+ *
+ */
+ dprintk(KERN_INFO "dev->flags = 0x%08X, dev->mc_count = 0x%08X\n",
+ dev->flags,dev->mc_count);
+
+ if (i2o_query_scalar(iop, i2o_dev->id, lan_context, 0x0001, 3,
+ &filter_mask, 4, &priv->reply_flag) < 0 )
+ printk(KERN_WARNING "i2o_lan: Unable to query filter mask.\n");
+
+ dprintk(KERN_INFO "filter_mask = 0x%08X\n",filter_mask);
+
+ if (dev->flags & IFF_PROMISC)
+ {
+ // Enable promiscuous mode
+
+ filter_mask |= 0x00000002;
+ if (i2o_params_set(iop, i2o_dev->id, lan_context, 0x0001, 3,
+ &filter_mask, 4, &priv->reply_flag) <0)
+ printk(KERN_WARNING "i2o_lan: Unable to enable promiscuous multicast mode.\n");
+ else
+ dprintk(KERN_INFO "i2o_lan: Promiscuous multicast mode enabled.\n");
+
+ return;
+ }
+
+// if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > HW_MAX_ADDRS)
+// {
+// // Disable promiscuous mode, use normal mode.
+// hardware_set_filter(NULL);
+//
+// dprintk(KERN_INFO "i2o_lan: Disabled promiscuous mode, uses normal mode\n");
+//
+// filter_mask = 0x00000000;
+// i2o_params_set(iop, i2o_dev->id, lan_context, 0x0001, 3,
+// &filter_mask, 4, &priv->reply_flag);
+//
+// return;
+// }
+
+ if (dev->mc_count)
+ {
+ // Walk the address list, and load the filter
+// hardware_set_filter(dev->mc_list);
+
+ filter_mask = 0x00000004;
+ if (i2o_params_set(iop, i2o_dev->id, lan_context, 0x0001, 3,
+ &filter_mask, 4, &priv->reply_flag) <0)
+ printk(KERN_WARNING "i2o_lan: Unable to enable Promiscuous multicast mode.\n");
+ else
+ dprintk(KERN_INFO "i2o_lan: Promiscuous multicast mode enabled.\n");
+
+ return;
+ }
+
+ // Unicast
+
+ filter_mask |= 0x00000300; // Broadcast, Multicast disabled
+ if (i2o_params_set(iop, i2o_dev->id, lan_context, 0x0001, 3,
+ &filter_mask, 4, &priv->reply_flag) <0)
+ printk(KERN_WARNING "i2o_lan: Unable to enable unicast mode.\n");
+ else
+ dprintk(KERN_INFO "i2o_lan: Unicast mode enabled.\n");
+
+ return;
+}
+
+struct device *i2o_lan_register_device(struct i2o_device *i2o_dev)
+{
+ struct device *dev = NULL;
+ struct i2o_lan_local *priv = NULL;
+ u8 hw_addr[8];
+ unsigned short (*type_trans)(struct sk_buff *, struct device *);
+
+ switch (i2o_dev->subclass)
+ {
+ case I2O_LAN_ETHERNET:
+ /* Note: init_etherdev calls
+ ether_setup() and register_netdevice()
+ and allocates the priv structure */
+
+ dev = init_etherdev(NULL, sizeof(struct i2o_lan_local));
+ if (dev == NULL)
+ return NULL;
+ type_trans = eth_type_trans;
+ break;
+
+/*
+#ifdef CONFIG_ANYLAN
+ case I2O_LAN_100VG:
+ printk(KERN_WARNING "i2o_lan: 100base VG not yet supported\n");
+ break;
+#endif
+*/
+
+#ifdef CONFIG_TR
+ case I2O_LAN_TR:
+ dev = init_trdev(NULL, sizeof(struct i2o_lan_local));
+ if(dev==NULL)
+ return NULL;
+ type_trans = tr_type_trans;
+ break;
+#endif
+
+#ifdef CONFIG_FDDI
+ case I2O_LAN_FDDI:
+ {
+ int size = sizeof(struct device) + sizeof(struct i2o_lan_local)
+ + sizeof("fddi%d ");
+
+ dev = (struct device *) kmalloc(size, GFP_KERNEL);
+ memset((char *)dev, 0, size);
+ dev->priv = (void *)(dev + 1);
+ dev->name = (char *)(dev + 1) + sizeof(struct i2o_lan_local);
+
+ if (dev_alloc_name(dev,"fddi%d") < 0)
+ {
+ printk(KERN_WARNING "i2o_lan: Too many FDDI devices.\n");
+ kfree(dev);
+ return NULL;
+ }
+ type_trans = fddi_type_trans;
+
+ fddi_setup(dev);
+ register_netdev(dev);
+ }
+ break;
+#endif
+
+/*
+#ifdef CONFIG_FIBRE_CHANNEL
+ case I2O_LAN_FIBRE_CHANNEL:
+ printk(KERN_WARNING "i2o_lan: Fibre Channel not yet supported\n");
+ break;
+#endif
+*/
+ case I2O_LAN_UNKNOWN:
+ default:
+ printk(KERN_WARNING "i2o_lan: LAN type 0x%08X not supported\n",
+ i2o_dev->subclass);
+ return NULL;
+ }
+
+ priv = (struct i2o_lan_local *)dev->priv;
+ priv->i2o_dev = i2o_dev;
+ priv->type_trans = type_trans;
+
+ if (i2o_query_scalar(i2o_dev->controller, i2o_dev->id, lan_context,
+ 0x0001, 0, &hw_addr, 8, &priv->reply_flag) < 0)
+ {
+ printk("%s: Unable to query hardware address.\n",
+ dev->name);
+ return NULL;
+ }
+
+ dprintk("%s hwaddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
+ dev->name,hw_addr[0], hw_addr[1], hw_addr[2], hw_addr[3],
+ hw_addr[4], hw_addr[5]);
+
+ dev->addr_len = 6;
+ memcpy(dev->dev_addr, hw_addr, 6);
+
+ dev->open = i2o_lan_open;
+ dev->stop = i2o_lan_close;
+ dev->hard_start_xmit = i2o_lan_packet_send;
+ dev->get_stats = i2o_lan_get_stats;
+ dev->set_multicast_list = i2o_lan_set_multicast_list;
+
+ return dev;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+ struct device *dev;
+ struct i2o_lan_local *priv;
+ int i;
+
+ if (i2o_install_handler(&i2o_lan_handler) < 0)
+ {
+ printk(KERN_ERR "Unable to register I2O LAN OSM.\n");
+ return -EINVAL;
+ }
+
+ lan_context = i2o_lan_handler.context;
+
+ for (i=0; i < MAX_I2O_CONTROLLERS; i++)
+ {
+ struct i2o_controller *iop = i2o_find_controller(i);
+ struct i2o_device *i2o_dev;
+
+ if (iop==NULL)
+ continue;
+
+ for (i2o_dev=iop->devices;i2o_dev != NULL;i2o_dev=i2o_dev->next)
+ {
+ int class = i2o_dev->class;
+
+ if (class != 0x020) /* not I2O_CLASS_LAN device*/
+ continue;
+
+ if (unit == MAX_LAN_CARDS)
+ {
+ printk(KERN_WARNING "Too many I2O LAN devices.\n");
+ return -EINVAL;
+ }
+
+ dev = i2o_lan_register_device(i2o_dev);
+ if (dev == NULL)
+ {
+ printk(KERN_WARNING "Unable to register I2O LAN device\n");
+ continue; // try next one
+ }
+ priv = (struct i2o_lan_local *)dev->priv;
+
+ unit++;
+ i2o_landevs[unit] = dev;
+ priv->unit = unit;
+
+ printk(KERN_INFO "%s: I2O LAN device registered, tid = %d,"
+ " subclass = 0x%08X, unit = %d.\n",
+ dev->name, i2o_dev->id, i2o_dev->subclass,
+ priv->unit);
+ }
+ }
+
+ dprintk(KERN_INFO "%d I2O LAN devices found and registered.\n", unit+1);
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ int i;
+
+ for (i = 0; i <= unit; i++)
+ {
+ struct device *dev = i2o_landevs[i];
+ struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv;
+ struct i2o_device *i2o_dev = priv->i2o_dev;
+
+ switch (i2o_dev->subclass)
+ {
+ case I2O_LAN_ETHERNET:
+ unregister_netdev(dev);
+ kfree(dev);
+ break;
+#ifdef CONFIG_FDDI
+ case I2O_LAN_FDDI:
+ unregister_netdevice(dev);
+ kfree(dev);
+ break;
+#endif
+#ifdef CONFIG_TR
+ case I2O_LAN_TR:
+ unregister_netdev(dev);
+ kfree(dev);
+ break;
+#endif
+ default:
+ printk(KERN_WARNING "i2o_lan: Spurious I2O LAN subclass 0x%08X.\n",
+ i2o_dev->subclass);
+ }
+
+ dprintk(KERN_INFO "%s: I2O LAN device unregistered.\n",
+ dev->name);
+ }
+
+ i2o_remove_handler(&i2o_lan_handler);
+}
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Univ of Helsinki, CS Department");
+MODULE_DESCRIPTION("I2O Lan OSM");
+
+#endif
diff --git a/drivers/i2o/i2o_lan.h b/drivers/i2o/i2o_lan.h
new file mode 100644
index 000000000..c8e82bf41
--- /dev/null
+++ b/drivers/i2o/i2o_lan.h
@@ -0,0 +1,112 @@
+/*
+ * i2o_lan.h LAN Class specific definitions
+ *
+ * I2O LAN CLASS OSM Prototyping, May 7th 1999
+ *
+ * (C) Copyright 1999 University of Helsinki,
+ * Department of Computer Science
+ *
+ * This code is still under development / test.
+ *
+ * Author: Auvo Häkkinen <Auvo.Hakkinen@cs.Helsinki.FI>
+ *
+ */
+
+#ifndef I2O_LAN_H
+#define I2O_LAN_H
+
+/* Tunable parameters first */
+
+#define I2O_BUCKET_COUNT 64
+#define I2O_BUCKET_THRESH 5
+
+/* LAN types */
+#define I2O_LAN_ETHERNET 0x0030
+#define I2O_LAN_100VG 0x0040
+#define I2O_LAN_TR 0x0050
+#define I2O_LAN_FDDI 0x0060
+#define I2O_LAN_FIBRE_CHANNEL 0x0070
+#define I2O_LAN_UNKNOWN 0x00000000
+
+/* Connector types */
+
+/* Ethernet */
+#define I2O_LAN_AUI (I2O_LAN_ETHERNET << 4) + 0x00000001
+#define I2O_LAN_10BASE5 (I2O_LAN_ETHERNET << 4) + 0x00000002
+#define I2O_LAN_FIORL (I2O_LAN_ETHERNET << 4) + 0x00000003
+#define I2O_LAN_10BASE2 (I2O_LAN_ETHERNET << 4) + 0x00000004
+#define I2O_LAN_10BROAD36 (I2O_LAN_ETHERNET << 4) + 0x00000005
+#define I2O_LAN_10BASE_T (I2O_LAN_ETHERNET << 4) + 0x00000006
+#define I2O_LAN_10BASE_FP (I2O_LAN_ETHERNET << 4) + 0x00000007
+#define I2O_LAN_10BASE_FB (I2O_LAN_ETHERNET << 4) + 0x00000008
+#define I2O_LAN_10BASE_FL (I2O_LAN_ETHERNET << 4) + 0x00000009
+#define I2O_LAN_100BASE_TX (I2O_LAN_ETHERNET << 4) + 0x0000000A
+#define I2O_LAN_100BASE_FX (I2O_LAN_ETHERNET << 4) + 0x0000000B
+#define I2O_LAN_100BASE_T4 (I2O_LAN_ETHERNET << 4) + 0x0000000C
+#define I2O_LAN_1000BASE_SX (I2O_LAN_ETHERNET << 4) + 0x0000000D
+#define I2O_LAN_1000BASE_LX (I2O_LAN_ETHERNET << 4) + 0x0000000E
+#define I2O_LAN_1000BASE_CX (I2O_LAN_ETHERNET << 4) + 0x0000000F
+#define I2O_LAN_1000BASE_T (I2O_LAN_ETHERNET << 4) + 0x00000010
+
+/* AnyLAN */
+#define I2O_LAN_100VG_ETHERNET (I2O_LAN_100VG << 4) + 0x00000001
+#define I2O_LAN_100VG_TR (I2O_LAN_100VG << 4) + 0x00000002
+
+/* Token Ring */
+#define I2O_LAN_4MBIT (I2O_LAN_TR << 4) + 0x00000001
+#define I2O_LAN_16MBIT (I2O_LAN_TR << 4) + 0x00000002
+
+/* FDDI */
+#define I2O_LAN_125MBAUD (I2O_LAN_FDDI << 4) + 0x00000001
+
+/* Fibre Channel */
+#define I2O_LAN_POINT_POINT (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000001
+#define I2O_LAN_ARB_LOOP (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000002
+#define I2O_LAN_PUBLIC_LOOP (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000003
+#define I2O_LAN_FABRIC (I2O_LAN_FIBRE_CHANNEL << 4) + 0x00000004
+
+#define I2O_LAN_EMULATION 0x00000F00
+#define I2O_LAN_OTHER 0x00000F01
+#define I2O_LAN_DEFAULT 0xFFFFFFFF
+
+/* LAN class functions */
+
+#define LAN_PACKET_SEND 0x3B
+#define LAN_SDU_SEND 0x3D
+#define LAN_RECEIVE_POST 0x3E
+#define LAN_RESET 0x35
+#define LAN_SUSPEND 0x37
+
+/* LAN DetailedStatusCode defines */
+#define I2O_LAN_DSC_SUCCESS 0x00
+#define I2O_LAN_DSC_DEVICE_FAILURE 0x01
+#define I2O_LAN_DSC_DESTINATION_NOT_FOUND 0x02
+#define I2O_LAN_DSC_TRANSMIT_ERROR 0x03
+#define I2O_LAN_DSC_TRANSMIT_ABORTED 0x04
+#define I2O_LAN_DSC_RECEIVE_ERROR 0x05
+#define I2O_LAN_DSC_RECEIVE_ABORTED 0x06
+#define I2O_LAN_DSC_DMA_ERROR 0x07
+#define I2O_LAN_DSC_BAD_PACKET_DETECTED 0x08
+#define I2O_LAN_DSC_OUT_OF_MEMORY 0x09
+#define I2O_LAN_DSC_BUCKET_OVERRUN 0x0A
+#define I2O_LAN_DSC_IOP_INTERNAL_ERROR 0x0B
+#define I2O_LAN_DSC_CANCELED 0x0C
+#define I2O_LAN_DSC_INVALID_TRANSACTION_CONTEXT 0x0D
+#define I2O_LAN_DSC_DEST_ADDRESS_DETECTED 0x0E
+#define I2O_LAN_DSC_DEST_ADDRESS_OMITTED 0x0F
+#define I2O_LAN_DSC_PARTIAL_PACKET_RETURNED 0x10
+#define I2O_LAN_DSC_TEMP_SUSPENDED_STATE 0x11
+
+struct i2o_packet_info {
+ u32 offset : 24;
+ u32 flags : 8;
+ u32 len : 24;
+ u32 status : 8;
+};
+
+struct i2o_bucket_descriptor {
+ u32 context; /* FIXME: 64bit support */
+ struct i2o_packet_info packet_info[1];
+};
+
+#endif /* I2O_LAN_H */
diff --git a/drivers/i2o/i2o_pci.c b/drivers/i2o/i2o_pci.c
new file mode 100644
index 000000000..596d9f953
--- /dev/null
+++ b/drivers/i2o/i2o_pci.c
@@ -0,0 +1,243 @@
+/*
+ * Find I2O capable controllers on the PCI bus, and register/install
+ * them with the I2O layer
+ *
+ * (C) Copyright 1999 Red Hat Software
+ *
+ * Written by Alan Cox, Building Number Three Ltd
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/i2o.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <asm/io.h>
+
+/*
+ * Free bus specific resources
+ */
+
+static void i2o_pci_dispose(struct i2o_controller *c)
+{
+ I2O_IRQ_WRITE32(c,0xFFFFFFFF);
+ if(c->bus.pci.irq > 0)
+ free_irq(c->bus.pci.irq, c);
+ iounmap(((u8 *)c->post_port)-0x40);
+}
+
+/*
+ * No real bus specific handling yet (note that later we will
+ * need to 'steal' PCI devices on i960 mainboards)
+ */
+
+static int i2o_pci_bind(struct i2o_controller *c, struct i2o_device *dev)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int i2o_pci_unbind(struct i2o_controller *c, struct i2o_device *dev)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * Bus specific interrupt handler
+ */
+
+static void i2o_pci_interrupt(int irq, void *dev_id, struct pt_regs *r)
+{
+ struct i2o_controller *c = dev_id;
+ i2o_run_queue(c);
+}
+
+/*
+ * Install a PCI (or in theory AGP) i2o controller
+ */
+
+int __init i2o_pci_install(struct pci_dev *dev)
+{
+ struct i2o_controller *c=kmalloc(sizeof(struct i2o_controller),
+ GFP_KERNEL);
+ u8 *mem;
+ u32 memptr = 0;
+ u32 size;
+
+ int i;
+
+ if(c==NULL)
+ {
+ printk(KERN_ERR "i2o_pci: insufficient memory to add controller.\n");
+ return -ENOMEM;
+ }
+ memset(c, 0, sizeof(*c));
+
+ for(i=0; i<6; i++)
+ {
+ /* Skip I/O spaces */
+ if(!(dev->base_address[i]&PCI_BASE_ADDRESS_SPACE))
+ {
+ memptr=PCI_BASE_ADDRESS_MEM_MASK&dev->base_address[i];
+ break;
+ }
+ }
+
+ if(i==6)
+ {
+ printk(KERN_ERR "i2o_pci: I2O controller has no memory regions defined.\n");
+ return -ENOMEM;
+ }
+
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0+4*i, 0xFFFFFFFF);
+ pci_read_config_dword(dev, PCI_BASE_ADDRESS_0+4*i, &size);
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_0+4*i, dev->base_address[i]);
+
+ /* Map the I2O controller */
+
+ printk(KERN_INFO "PCI I2O controller at 0x%08X size=%d\n", memptr, -size);
+ mem = ioremap(memptr, -size);
+
+ c->bus.pci.irq = -1;
+
+ c->irq_mask = (volatile u32 *)(mem+0x34);
+ c->post_port = (volatile u32 *)(mem+0x40);
+ c->reply_port = (volatile u32 *)(mem+0x44);
+
+ c->mem_phys = memptr;
+ c->mem_offset = (u32)mem;
+ c->destructor = i2o_pci_dispose;
+
+ c->bind = i2o_pci_bind;
+ c->unbind = i2o_pci_unbind;
+
+ c->type = I2O_TYPE_PCI;
+
+ I2O_IRQ_WRITE32(c,0xFFFFFFFF);
+
+ i = i2o_install_controller(c);
+
+ if(i<0)
+ {
+ printk(KERN_ERR "i2o: unable to install controller.\n");
+ return i;
+ }
+
+ c->bus.pci.irq = dev->irq;
+ if(c->bus.pci.irq)
+ {
+ i=request_irq(dev->irq, i2o_pci_interrupt, SA_SHIRQ,
+ c->name, c);
+ if(i<0)
+ {
+ printk(KERN_ERR "%s: unable to allocate interrupt %d.\n",
+ c->name, dev->irq);
+ c->bus.pci.irq = -1;
+ i2o_delete_controller(c);
+ return -EBUSY;
+ }
+ }
+ return 0;
+}
+
+int __init i2o_pci_scan(void)
+{
+ struct pci_dev *dev;
+ int count=0;
+
+ printk(KERN_INFO "Checking for PCI I2O controllers...\n");
+
+ for(dev=pci_devices; dev!=NULL; dev=dev->next)
+ {
+ if((dev->class>>8)!=PCI_CLASS_INTELLIGENT_I2O)
+ continue;
+ if((dev->class&0xFF)>1)
+ {
+ printk(KERN_INFO "I2O controller found but does not support I2O 1.5 (skipping).\n");
+ continue;
+ }
+ printk(KERN_INFO "I2O controller on bus %d at %d.\n",
+ dev->bus->number, dev->devfn);
+ if(!dev->master)
+ printk(KERN_WARNING "Controller not master enabled.\n");
+ if(i2o_pci_install(dev)==0)
+ count++;
+ }
+ if(count)
+ printk(KERN_INFO "%d I2O controller%s found and installed.\n", count,
+ count==1?"":"s");
+ return count?count:-ENODEV;
+}
+
+static void i2o_pci_unload(void)
+{
+ int i=0;
+ struct i2o_controller *c;
+
+ for(i = 0; i < MAX_I2O_CONTROLLERS; i++)
+ {
+ c=i2o_find_controller(i);
+ if(c==NULL)
+ continue;
+ if(c->type == I2O_TYPE_PCI)
+ i2o_delete_controller(c);
+ i2o_unlock_controller(c);
+ }
+}
+
+static void i2o_pci_activate(void)
+{
+ int i=0;
+ struct i2o_controller *c;
+
+ for(i = 0; i < MAX_I2O_CONTROLLERS; i++)
+ {
+ c=i2o_find_controller(i);
+ if(c==NULL)
+ continue;
+ if(c->type == I2O_TYPE_PCI)
+ {
+ if(i2o_activate_controller(c))
+ {
+ printk("I2O: Failed to initialize iop%d\n", c->unit);
+ i2o_unlock_controller(c);
+ free_irq(c->bus.pci.irq, c);
+ i2o_delete_controller(c);
+ continue;
+ }
+
+ I2O_IRQ_WRITE32(c,0);
+ }
+ i2o_unlock_controller(c);
+ }
+}
+
+#ifdef MODULE
+
+EXPORT_NO_SYMBOLS;
+MODULE_AUTHOR("Red Hat Software");
+MODULE_DESCRIPTION("I2O PCI Interface");
+
+int init_module(void)
+{
+ if(i2o_pci_scan()<0)
+ return -ENODEV;
+ i2o_pci_activate();
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ i2o_pci_unload();
+}
+
+#endif
diff --git a/drivers/i2o/i2o_proc.c b/drivers/i2o/i2o_proc.c
new file mode 100644
index 000000000..ce38b3914
--- /dev/null
+++ b/drivers/i2o/i2o_proc.c
@@ -0,0 +1,2382 @@
+/*
+ * procfs handler for Linux I2O subsystem
+ *
+ * Copyright (c) 1999 Intel Corporation
+ *
+ * Originally written by Deepak Saxena(deepak.saxena@intel.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 is an initial test release. The code is based on the design
+ * of the ide procfs system (drivers/block/ide-proc.c). Some code
+ * taken from i2o-core module by Alan Cox.
+ *
+ * DISCLAIMER: This code is still under development/test and may cause
+ * your system to behave unpredictably. Use at your own discretion.
+ *
+ * LAN entries by Juha Sievänen(Juha.Sievanen@cs.Helsinki.FI),
+ * University of Helsinki, Department of Computer Science
+ *
+ */
+
+/*
+ * set tabstop=3
+ */
+
+/*
+ * TODO List
+ *
+ * - Add support for any version 2.0 spec changes once 2.0 IRTOS is
+ * is available to test with
+ * - Clean up code to use official structure definitions
+ */
+
+// FIXME!
+#define FMT_U64_HEX "0x%08x%08x"
+#define U64_VAL(pu64) *((u32*)(pu64)+1), *((u32*)(pu64))
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/i2o.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/byteorder.h>
+#include <asm/spinlock.h>
+
+
+#include "i2o_proc.h"
+
+#include "i2o_lan.h"
+
+/*
+ * Structure used to define /proc entries
+ */
+typedef struct _i2o_proc_entry_t
+{
+ char *name; /* entry name */
+ mode_t mode; /* mode */
+ read_proc_t *read_proc; /* read func */
+ write_proc_t *write_proc; /* write func */
+} i2o_proc_entry;
+
+static int proc_context = 0;
+
+
+static int i2o_proc_read_lct(char *, char **, off_t, int, int *, void *);
+static int i2o_proc_read_hrt(char *, char **, off_t, int, int *, void *);
+static int i2o_proc_read_stat(char *, char **, off_t, int, int *, void *);
+static int i2o_proc_read_hw(char *, char **, off_t, int, int *, void *);
+static int i2o_proc_read_dev(char *, char **, off_t, int, int *, void *);
+static int i2o_proc_read_dev_name(char *, char **, off_t, int, int *, void *);
+static int i2o_proc_read_ddm(char *, char **, off_t, int, int *, void *);
+static int i2o_proc_read_uinfo(char *, char **, off_t, int, int *, void *);
+static int print_serial_number(char *, int, u8 *, int);
+static int i2o_proc_create_entries(void *,
+ i2o_proc_entry *p, struct proc_dir_entry *);
+static void i2o_proc_remove_entries(i2o_proc_entry *p,
+ struct proc_dir_entry *);
+static int i2o_proc_add_controller(struct i2o_controller *,
+ struct proc_dir_entry * );
+static void i2o_proc_remove_controller(struct i2o_controller *,
+ struct proc_dir_entry * );
+static int create_i2o_procfs(void);
+static int destroy_i2o_procfs(void);
+static void i2o_proc_reply(struct i2o_handler *, struct i2o_controller *,
+ struct i2o_message *);
+
+static int i2o_proc_read_lan_dev_info(char *, char **, off_t, int, int *,
+ void *);
+static int i2o_proc_read_lan_mac_addr(char *, char **, off_t, int, int *,
+ void *);
+static int i2o_proc_read_lan_curr_addr(char *, char **, off_t, int, int *,
+ void *);
+#if 0
+static int i2o_proc_read_lan_mcast_addr(char *, char **, off_t, int, int *,
+ void *);
+#endif
+static int i2o_proc_read_lan_batch_control(char *, char **, off_t, int, int *,
+ void *);
+static int i2o_proc_read_lan_operation(char *, char **, off_t, int, int *,
+ void *);
+static int i2o_proc_read_lan_media_operation(char *, char **, off_t, int,
+ int *, void *);
+#if 0
+static int i2o_proc_read_lan_alt_addr(char *, char **, off_t, int, int *,
+ void *);
+#endif
+static int i2o_proc_read_lan_tx_info(char *, char **, off_t, int, int *,
+ void *);
+static int i2o_proc_read_lan_rx_info(char *, char **, off_t, int, int *,
+ void *);
+static int i2o_proc_read_lan_hist_stats(char *, char **, off_t, int, int *,
+ void *);
+static int i2o_proc_read_lan_opt_tx_hist_stats(char *, char **, off_t, int,
+ int *, void *);
+static int i2o_proc_read_lan_opt_rx_hist_stats(char *, char **, off_t, int,
+ int *, void *);
+static int i2o_proc_read_lan_fddi_stats(char *, char **, off_t, int, int *,
+ void *);
+
+#if 0
+/* Do we really need this??? */
+
+static loff_t i2o_proc_lseek(struct file *file, loff_t off, int whence)
+{
+ return 0;
+}
+#endif
+
+static struct proc_dir_entry *i2o_proc_dir_root;
+
+/*
+ * Message handler
+ */
+static struct i2o_handler i2o_proc_handler =
+{
+ (void *)i2o_proc_reply,
+ "I2O procfs Layer",
+ 0
+};
+
+/*
+ * IOP specific entries...write field just in case someone
+ * ever wants one.
+ */
+static i2o_proc_entry generic_iop_entries[] =
+{
+ {"hrt", S_IFREG|S_IRUGO, i2o_proc_read_hrt, NULL},
+ {"lct", S_IFREG|S_IRUGO, i2o_proc_read_lct, NULL},
+ {"stat", S_IFREG|S_IRUGO, i2o_proc_read_stat, NULL},
+ {"hw", S_IFREG|S_IRUGO, i2o_proc_read_hw, NULL},
+ {NULL, 0, NULL, NULL}
+};
+
+/*
+ * Device specific entries
+ */
+static i2o_proc_entry generic_dev_entries[] =
+{
+ {"dev_identity", S_IFREG|S_IRUGO, i2o_proc_read_dev, NULL},
+ {"ddm_identity", S_IFREG|S_IRUGO, i2o_proc_read_ddm, NULL},
+ {"user_info", S_IFREG|S_IRUGO, i2o_proc_read_uinfo, NULL},
+ {NULL, 0, NULL, NULL}
+};
+
+/*
+ * Storage unit specific entries (SCSI Periph, BS) with device names
+ */
+static i2o_proc_entry rbs_dev_entries[] =
+{
+ {"dev_name", S_IFREG|S_IRUGO, i2o_proc_read_dev_name, NULL},
+ {NULL, 0, NULL, NULL}
+};
+
+#define SCSI_TABLE_SIZE 13
+ static char *scsi_devices[] =
+ {
+ "Direct-Access Read/Write",
+ "Sequential-Access Storage",
+ "Printer",
+ "Processor",
+ "WORM Device",
+ "CD-ROM Device",
+ "Scanner Device",
+ "Optical Memory Device",
+ "Medium Changer Device",
+ "Communications Device",
+ "Graphics Art Pre-Press Device",
+ "Graphics Art Pre-Press Device",
+ "Array Controller Device"
+ };
+
+/* private */
+
+/*
+ * LAN specific entries
+ *
+ * Should groups with r/w entries have their own subdirectory?
+ *
+ */
+static i2o_proc_entry lan_entries[] =
+{
+ /* LAN param groups 0000h-0008h */
+ {"lan_dev_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_dev_info, NULL},
+ {"lan_mac_addr", S_IFREG|S_IRUGO, i2o_proc_read_lan_mac_addr, NULL},
+#if 0
+ {"lan_mcast_addr", S_IFREG|S_IRUGO|S_IWUSR,
+ i2o_proc_read_lan_mcast_addr, NULL},
+#endif
+ {"lan_batch_ctrl", S_IFREG|S_IRUGO|S_IWUSR,
+ i2o_proc_read_lan_batch_control, NULL},
+ {"lan_operation", S_IFREG|S_IRUGO, i2o_proc_read_lan_operation, NULL},
+ {"lan_media_operation", S_IFREG|S_IRUGO,
+ i2o_proc_read_lan_media_operation, NULL},
+#if 0
+ {"lan_alt_addr", S_IFREG|S_IRUGO, i2o_proc_read_lan_alt_addr, NULL},
+#endif
+ {"lan_tx_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_tx_info, NULL},
+ {"lan_rx_info", S_IFREG|S_IRUGO, i2o_proc_read_lan_rx_info, NULL},
+ {"lan_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_hist_stats, NULL},
+ {"lan_opt_tx_stats", S_IFREG|S_IRUGO,
+ i2o_proc_read_lan_opt_tx_hist_stats, NULL},
+ {"lan_opt_rx_stats", S_IFREG|S_IRUGO,
+ i2o_proc_read_lan_opt_rx_hist_stats, NULL},
+ {"lan_fddi_stats", S_IFREG|S_IRUGO, i2o_proc_read_lan_fddi_stats, NULL},
+ /* some useful r/w entries, no write yet */
+ {"lan_curr_addr", S_IFREG|S_IRUGO|S_IWUSR,
+ i2o_proc_read_lan_curr_addr, NULL},
+ {NULL, 0, NULL, NULL}
+};
+
+static u32 i2o_proc_token = 0;
+
+static char* bus_strings[] =
+{
+ "Local Bus",
+ "ISA",
+ "EISA",
+ "MCA",
+ "PCI",
+ "PCMCIA",
+ "NUBUS",
+ "CARDBUS"
+};
+
+static spinlock_t i2o_proc_lock = SPIN_LOCK_UNLOCKED;
+
+void i2o_proc_reply(struct i2o_handler *phdlr, struct i2o_controller *pctrl,
+ struct i2o_message *pmsg)
+{
+ i2o_proc_token = I2O_POST_WAIT_OK;
+}
+
+int i2o_proc_read_hrt(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ struct i2o_controller *c = (struct i2o_controller *)data;
+ pi2o_hrt hrt;
+ u32 msg[6];
+ u32 *workspace;
+ u32 bus;
+ int count;
+ int i;
+ int token;
+
+ spin_lock(&i2o_proc_lock);
+
+ len = 0;
+
+ workspace = kmalloc(2048, GFP_KERNEL);
+ hrt = (pi2o_hrt)workspace;
+ if(workspace==NULL)
+ {
+ len += sprintf(buf, "No free memory for HRT buffer\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ memset(workspace, 0, 2048);
+
+ msg[0]= SIX_WORD_MSG_SIZE| SGL_OFFSET_4;
+ msg[1]= I2O_CMD_HRT_GET<<24 | HOST_TID<<12 | ADAPTER_TID;
+ msg[2]= (u32)proc_context;
+ msg[3]= 0;
+ msg[4]= (0xD0000000 | 2048);
+ msg[5]= virt_to_phys(workspace);
+
+ token = i2o_post_wait(c, ADAPTER_TID, msg, 6*4, &i2o_proc_token,2);
+ if(token == I2O_POST_WAIT_TIMEOUT)
+ {
+ kfree(workspace);
+ len += sprintf(buf, "Timeout waiting for HRT\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ if(hrt->hrt_version)
+ {
+ len += sprintf(buf+len,
+ "HRT table for controller is too new a version.\n");
+ return len;
+ }
+
+ count = hrt->num_entries;
+
+ if((count * hrt->entry_len + 8) > 2048) {
+ printk(KERN_WARNING "i2o_proc: HRT does not fit into buffer\n");
+ len += sprintf(buf+len,
+ "HRT table too big to fit in buffer.\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ len += sprintf(buf+len, "HRT has %d entries of %d bytes each.\n",
+ count, hrt->entry_len);
+
+ for(i = 0; i < count; i++)
+ {
+ len += sprintf(buf+len, "Entry %d:\n", i);
+ len += sprintf(buf+len, " Adapter ID: %0#10x\n",
+ hrt->hrt_entry[i].adapter_id);
+ len += sprintf(buf+len, " Controlled by: %0#6x\n",
+ hrt->hrt_entry[i].parent_tid);
+ len += sprintf(buf+len, " Bus#%d\n",
+ hrt->hrt_entry[i].bus_num);
+
+ if(hrt->hrt_entry[i].bus_type != 0x80)
+ {
+ bus = hrt->hrt_entry[i].bus_type;
+ len += sprintf(buf+len, " %s Information\n", bus_strings[bus]);
+
+ switch(bus)
+ {
+ case I2O_BUS_LOCAL:
+ len += sprintf(buf+len, " IOBase: %0#6x,",
+ hrt->hrt_entry[i].bus.local_bus.LbBaseIOPort);
+ len += sprintf(buf+len, " MemoryBase: %0#10x\n",
+ hrt->hrt_entry[i].bus.local_bus.LbBaseMemoryAddress);
+ break;
+
+ case I2O_BUS_ISA:
+ len += sprintf(buf+len, " IOBase: %0#6x,",
+ hrt->hrt_entry[i].bus.isa_bus.IsaBaseIOPort);
+ len += sprintf(buf+len, " MemoryBase: %0#10x,",
+ hrt->hrt_entry[i].bus.isa_bus.IsaBaseMemoryAddress);
+ len += sprintf(buf+len, " CSN: %0#4x,",
+ hrt->hrt_entry[i].bus.isa_bus.CSN);
+ break;
+
+ case I2O_BUS_EISA:
+ len += sprintf(buf+len, " IOBase: %0#6x,",
+ hrt->hrt_entry[i].bus.eisa_bus.EisaBaseIOPort);
+ len += sprintf(buf+len, " MemoryBase: %0#10x,",
+ hrt->hrt_entry[i].bus.eisa_bus.EisaBaseMemoryAddress);
+ len += sprintf(buf+len, " Slot: %0#4x,",
+ hrt->hrt_entry[i].bus.eisa_bus.EisaSlotNumber);
+ break;
+
+ case I2O_BUS_MCA:
+ len += sprintf(buf+len, " IOBase: %0#6x,",
+ hrt->hrt_entry[i].bus.mca_bus.McaBaseIOPort);
+ len += sprintf(buf+len, " MemoryBase: %0#10x,",
+ hrt->hrt_entry[i].bus.mca_bus.McaBaseMemoryAddress);
+ len += sprintf(buf+len, " Slot: %0#4x,",
+ hrt->hrt_entry[i].bus.mca_bus.McaSlotNumber);
+ break;
+
+ case I2O_BUS_PCI:
+ len += sprintf(buf+len, " Bus: %0#4x",
+ hrt->hrt_entry[i].bus.pci_bus.PciBusNumber);
+ len += sprintf(buf+len, " Dev: %0#4x",
+ hrt->hrt_entry[i].bus.pci_bus.PciDeviceNumber);
+ len += sprintf(buf+len, " Func: %0#4x",
+ hrt->hrt_entry[i].bus.pci_bus.PciFunctionNumber);
+ len += sprintf(buf+len, " Vendor: %0#6x",
+ hrt->hrt_entry[i].bus.pci_bus.PciVendorID);
+ len += sprintf(buf+len, " Device: %0#6x\n",
+ hrt->hrt_entry[i].bus.pci_bus.PciDeviceID);
+ break;
+
+ default:
+ len += sprintf(buf+len, " Unsupported Bus Type\n");
+ }
+ }
+ else
+ len += sprintf(buf+len, " Unknown Bus Type\n");
+ }
+
+ kfree(workspace);
+
+ spin_unlock(&i2o_proc_lock);
+
+ return len;
+}
+
+int i2o_proc_read_lct(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ struct i2o_controller *c = (struct i2o_controller*)data;
+ u32 msg[8];
+ u32 *workspace;
+ pi2o_lct lct; /* = (pi2o_lct)c->lct; */
+ int entries;
+ int token;
+ int i;
+
+#define BUS_TABLE_SIZE 3
+ static char *bus_ports[] =
+ {
+ "Generic Bus",
+ "SCSI Bus",
+ "Fibre Channel Bus"
+ };
+
+ spin_lock(&i2o_proc_lock);
+
+ len = 0;
+
+ workspace = kmalloc(8192, GFP_KERNEL);
+ lct = (pi2o_lct)workspace;
+ if(workspace==NULL)
+ {
+ len += sprintf(buf, "No free memory for LCT buffer\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ memset(workspace, 0, 8192);
+
+ msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_6;
+ msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID;
+ msg[2] = (u32)proc_context;
+ msg[3] = 0;
+ msg[4] = 0xFFFFFFFF; /* All devices */
+ msg[5] = 0x00000000; /* Report now */
+ msg[6] = 0xD0000000|8192;
+ msg[7] = virt_to_bus(workspace);
+
+ token = i2o_post_wait(c, ADAPTER_TID, msg, 8*4, &i2o_proc_token,2);
+ if(token == I2O_POST_WAIT_TIMEOUT)
+ {
+ kfree(workspace);
+ len += sprintf(buf, "Timeout waiting for LCT\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ entries = (lct->table_size - 3)/9;
+
+ len += sprintf(buf, "LCT contains %d %s\n", entries,
+ entries == 1 ? "entry" : "entries");
+ if(lct->boot_tid)
+ len += sprintf(buf+len, "Boot Device @ ID %d\n", lct->boot_tid);
+
+ for(i = 0; i < entries; i++)
+ {
+ len += sprintf(buf+len, "Entry %d\n", i);
+
+ len += sprintf(buf+len, " %s", i2o_get_class_name(lct->lct_entry[i].class_id));
+
+ /*
+ * Classes which we'll print subclass info for
+ */
+ switch(lct->lct_entry[i].class_id & 0xFFF)
+ {
+ case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+ switch(lct->lct_entry[i].sub_class)
+ {
+ case 0x00:
+ len += sprintf(buf+len, ": Direct-Access Read/Write");
+ break;
+
+ case 0x04:
+ len += sprintf(buf+len, ": WORM Drive");
+ break;
+
+ case 0x05:
+ len += sprintf(buf+len, ": CD-ROM Drive");
+ break;
+
+ case 0x07:
+ len += sprintf(buf+len, ": Optical Memory Device");
+ break;
+
+ default:
+ len += sprintf(buf+len, ": Unknown");
+ break;
+ }
+ break;
+
+ case I2O_CLASS_LAN:
+ switch(lct->lct_entry[i].sub_class & 0xFF)
+ {
+ case 0x30:
+ len += sprintf(buf+len, ": Ethernet");
+ break;
+
+ case 0x40:
+ len += sprintf(buf+len, ": 100base VG");
+ break;
+
+ case 0x50:
+ len += sprintf(buf+len, ": IEEE 802.5/Token-Ring");
+ break;
+
+ case 0x60:
+ len += sprintf(buf+len, ": ANSI X3T9.5 FDDI");
+ break;
+
+ case 0x70:
+ len += sprintf(buf+len, ": Fibre Channel");
+ break;
+
+ default:
+ len += sprintf(buf+len, ": Unknown Sub-Class");
+ break;
+ }
+ break;
+
+ case I2O_CLASS_SCSI_PERIPHERAL:
+ if(lct->lct_entry[i].sub_class < SCSI_TABLE_SIZE)
+ len += sprintf(buf+len, ": %s",
+ scsi_devices[lct->lct_entry[i].sub_class]);
+ else
+ len += sprintf(buf+len, ": Unknown Device Type");
+ break;
+
+ case I2O_CLASS_BUS_ADAPTER_PORT:
+ if(lct->lct_entry[i].sub_class < BUS_TABLE_SIZE)
+ len += sprintf(buf+len, ": %s",
+ bus_ports[lct->lct_entry[i].sub_class]);
+ else
+ len += sprintf(buf+len, ": Unknown Bus Type");
+ break;
+ }
+ len += sprintf(buf+len, "\n");
+
+ len += sprintf(buf+len, " Local TID: 0x%03x\n", lct->lct_entry[i].tid);
+ len += sprintf(buf+len, " User TID: 0x%03x\n", lct->lct_entry[i].user_tid);
+ len += sprintf(buf+len, " Parent TID: 0x%03x\n",
+ lct->lct_entry[i].parent_tid);
+ len += sprintf(buf+len, " Identity Tag: 0x%x%x%x%x%x%x%x%x\n",
+ lct->lct_entry[i].identity_tag[0],
+ lct->lct_entry[i].identity_tag[1],
+ lct->lct_entry[i].identity_tag[2],
+ lct->lct_entry[i].identity_tag[3],
+ lct->lct_entry[i].identity_tag[4],
+ lct->lct_entry[i].identity_tag[5],
+ lct->lct_entry[i].identity_tag[6],
+ lct->lct_entry[i].identity_tag[7]);
+ len += sprintf(buf+len, " Change Indicator: %0#10x\n",
+ lct->lct_entry[i].change_ind);
+ len += sprintf(buf+len, " Device Flags: %0#10x\n",
+ lct->lct_entry[i].device_flags);
+ }
+
+ kfree(workspace);
+ spin_unlock(&i2o_proc_lock);
+
+ return len;
+}
+
+int i2o_proc_read_stat(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ struct i2o_controller *c = (struct i2o_controller*)data;
+ u32 *msg;
+ u32 m;
+ u8 *workspace;
+ u16 *work16;
+ u32 *work32;
+ long time;
+ char prodstr[25];
+ int version;
+
+ spin_lock(&i2o_proc_lock);
+
+ len = 0;
+
+ workspace = (u8*)kmalloc(88, GFP_KERNEL);
+ if(!workspace)
+ {
+ len += sprintf(buf, "No memory for status transfer\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ m = I2O_POST_READ32(c);
+ if(m == 0xFFFFFFFF)
+ {
+ len += sprintf(buf, "Could not get inbound message frame from IOP!\n");
+ kfree(workspace);
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ msg = (u32 *)(m+c->mem_offset);
+
+ memset(workspace, 0, 88);
+ work32 = (u32*)workspace;
+ work16 = (u16*)workspace;
+
+ msg[0] = NINE_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = I2O_CMD_STATUS_GET<<24|HOST_TID<<12|ADAPTER_TID;
+ msg[2] = msg[3] = msg[4] = msg[5] = 0;
+ msg[6] = virt_to_phys(workspace);
+ msg[7] = 0; /* FIXME: 64-bit */
+ msg[8] = 88;
+
+ /*
+ * hmm...i2o_post_message should just take ptr to message, and
+ * determine offset on it's own...less work for OSM developers
+ */
+ i2o_post_message(c, m);
+
+ time = jiffies;
+
+ while(workspace[87] != 0xFF)
+ {
+ if(jiffies-time >= 2*HZ)
+ {
+ len += sprintf(buf, "Timeout waiting for status reply\n");
+ kfree(workspace);
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+ schedule();
+ barrier();
+ }
+
+ len += sprintf(buf+len, "Organization ID: %0#6x\n", work16[0]);
+
+ version = workspace[9]&0xF0>>4;
+ if(version == 0x02) {
+ len += sprintf(buf+len, "Lowest I2O version supported: ");
+ switch(workspace[2]) {
+ case 0x00:
+ case 0x01:
+ len += sprintf(buf+len, "1.5\n");
+ break;
+ case 0x02:
+ len += sprintf(buf+len, "2.0\n");
+ break;
+ }
+
+ len += sprintf(buf+len, "Highest I2O version supported: ");
+ switch(workspace[3]) {
+ case 0x00:
+ case 0x01:
+ len += sprintf(buf+len, "1.5\n");
+ break;
+ case 0x02:
+ len += sprintf(buf+len, "2.0\n");
+ break;
+ }
+ }
+
+ len += sprintf(buf+len, "IOP ID: %0#5x\n", work16[2]&0xFFF);
+ len += sprintf(buf+len, "Host Unit ID: %0#6x\n", work16[3]);
+ len += sprintf(buf+len, "Segment Number: %0#5x\n", work16[4]&0XFFF);
+
+ len += sprintf(buf+len, "I2O Version: ");
+ switch(version)
+ {
+ case 0x00:
+ case 0x01:
+ len += sprintf(buf+len, "1.5\n");
+ break;
+ case 0x02:
+ len += sprintf(buf+len, "2.0\n");
+ break;
+ default:
+ len += sprintf(buf+len, "Unknown version\n");
+ }
+
+ len += sprintf(buf+len, "IOP State: ");
+ switch(workspace[10])
+ {
+ case 0x01:
+ len += sprintf(buf+len, "Init\n");
+ break;
+
+ case 0x02:
+ len += sprintf(buf+len, "Reset\n");
+ break;
+
+ case 0x04:
+ len += sprintf(buf+len, "Hold\n");
+ break;
+
+ case 0x05:
+ len += sprintf(buf+len, "Hold\n");
+ break;
+
+ case 0x08:
+ len += sprintf(buf+len, "Operational\n");
+ break;
+
+ case 0x10:
+ len += sprintf(buf+len, "FAILED\n");
+ break;
+
+ case 0x11:
+ len += sprintf(buf+len, "FAULTED\n");
+ break;
+
+ default:
+ len += sprintf(buf+len, "Unknown\n");
+ break;
+ }
+
+ /* 0x00 is the only type supported w/spec 1.5 */
+ /* Added 2.0 types */
+ len += sprintf(buf+len, "Messenger Type: ");
+ switch (workspace[11])
+ {
+ case 0x00:
+ len += sprintf(buf+len, "Memory Mapped\n");
+ break;
+ case 0x01:
+ len += sprintf(buf+len, "Memory mapped only\n");
+ break;
+ case 0x02:
+ len += sprintf(buf+len, "Remote only\n");
+ break;
+ case 0x03:
+ len += sprintf(buf+len, "Memory mapped and remote\n");
+ break;
+ default:
+ len += sprintf(buf+len, "Unknown\n");
+ break;
+ }
+ len += sprintf(buf+len, "Inbound Frame Size: %d bytes\n", work16[6]*4);
+ len += sprintf(buf+len, "Max Inbound Frames: %d\n", work32[4]);
+ len += sprintf(buf+len, "Current Inbound Frames: %d\n", work32[5]);
+ len += sprintf(buf+len, "Max Outbound Frames: %d\n", work32[6]);
+
+ /* Spec doesn't say if NULL terminated or not... */
+ memcpy(prodstr, work32+7, 24);
+ prodstr[24] = '\0';
+ len += sprintf(buf+len, "Product ID: %s\n", prodstr);
+
+ len += sprintf(buf+len, "LCT Size: %d\n", work32[13]);
+
+ len += sprintf(buf+len, "Desired Private Memory Space: %d kB\n",
+ work32[15]>>10);
+ len += sprintf(buf+len, "Allocated Private Memory Space: %d kB\n",
+ work32[16]>>10);
+ len += sprintf(buf+len, "Private Memory Base Address: %0#10x\n",
+ work32[17]);
+ len += sprintf(buf+len, "Desired Private I/O Space: %d kB\n",
+ work32[18]>>10);
+ len += sprintf(buf+len, "Allocated Private I/O Space: %d kB\n",
+ work32[19]>>10);
+ len += sprintf(buf+len, "Private I/O Base Address: %0#10x\n",
+ work32[20]);
+
+ spin_unlock(&i2o_proc_lock);
+
+ return len;
+}
+
+int i2o_proc_read_hw(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ struct i2o_controller *c = (struct i2o_controller*)data;
+ static u32 work32[5];
+ static u8 *work8 = (u8*)work32;
+ static u16 *work16 = (u16*)work32;
+ int token;
+ u32 hwcap;
+
+ static char *cpu_table[] =
+ {
+ "Intel 80960 Series",
+ "AMD2900 Series",
+ "Motorola 68000 Series",
+ "ARM Series",
+ "MIPS Series",
+ "Sparc Series",
+ "PowerPC Series",
+ "Intel x86 Series"
+ };
+
+ spin_lock(&i2o_proc_lock);
+
+ len = 0;
+
+ token = i2o_query_scalar(c, ADAPTER_TID, proc_context,
+ 0, // ParamGroup 0x0000h
+ -1, // all fields
+ &work32,
+ sizeof(work32),
+ &i2o_proc_token);
+
+ if(token < 0)
+ {
+ len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ len += sprintf(buf, "IOP Hardware Information Table\n");
+
+ len += sprintf(buf+len, "I2O Vendor ID: %0#6x\n", work16[0]);
+ len += sprintf(buf+len, "Product ID: %0#6x\n", work16[1]);
+ len += sprintf(buf+len, "RAM: %dkB\n", work32[1]>>10);
+ len += sprintf(buf+len, "Non-Volatile Storage: %dkB\n", work32[2]>>10);
+
+ hwcap = work32[3];
+ len += sprintf(buf+len, "Capabilities:\n");
+ if(hwcap&0x00000001)
+ len += sprintf(buf+len, " Self-booting\n");
+ if(hwcap&0x00000002)
+ len += sprintf(buf+len, " Upgradable IRTOS\n");
+ if(hwcap&0x00000004)
+ len += sprintf(buf+len, " Supports downloading DDMs\n");
+ if(hwcap&0x00000008)
+ len += sprintf(buf+len, " Supports installing DDMs\n");
+ if(hwcap&0x00000010)
+ len += sprintf(buf+len, " Battery-backed RAM\n");
+
+ len += sprintf(buf+len, "CPU: ");
+ if(work8[16] > 8)
+ len += sprintf(buf+len, "Unknown\n");
+ else
+ len += sprintf(buf+len, "%s\n", cpu_table[work8[16]]);
+ /* Anyone using ProcessorVersion? */
+
+ spin_unlock(&i2o_proc_lock);
+
+ return len;
+}
+
+int i2o_proc_read_dev(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ struct i2o_device *d = (struct i2o_device*)data;
+ static u32 work32[128]; // allow for "stuff" + up to 256 byte (max) serial number
+ // == (allow) 512d bytes (max)
+ static u16 *work16 = (u16*)work32;
+ char sz[17];
+ int token;
+
+ spin_lock(&i2o_proc_lock);
+
+ len = 0;
+
+ token = i2o_query_scalar(d->controller, d->id, proc_context,
+ 0xF100, // ParamGroup F100h (Device Identity)
+ -1, // all fields
+ &work32,
+ sizeof(work32),
+ &i2o_proc_token);
+
+ if(token < 0)
+ {
+ len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ len += sprintf(buf, "Device Class: %s\n", i2o_get_class_name(work16[0]));
+
+ len += sprintf(buf+len, "Owner TID: %0#5x\n", work16[2]);
+ len += sprintf(buf+len, "Parent TID: %0#5x\n", work16[3]);
+
+ memcpy(sz, work32+2, 16);
+ sz[16] = '\0';
+ len += sprintf(buf+len, "Vendor Info: %s\n", sz);
+
+ memcpy(sz, work32+6, 16);
+ sz[16] = '\0';
+ len += sprintf(buf+len, "Product Info: %s\n", sz);
+
+ memcpy(sz, work32+10, 16);
+ sz[16] = '\0';
+ len += sprintf(buf+len, "Description: %s\n", sz);
+
+ memcpy(sz, work32+14, 8);
+ sz[8] = '\0';
+ len += sprintf(buf+len, "Product Revision: %s\n", sz);
+
+ len += sprintf(buf+len, "Serial Number: ");
+ len = print_serial_number(buf, len,
+ (u8*)(work32+16),
+ /* allow for SNLen plus
+ * possible trailing '\0'
+ */
+ sizeof(work32)-(16*sizeof(u32))-2
+ );
+ len += sprintf(buf+len, "\n");
+
+ spin_unlock(&i2o_proc_lock);
+
+ return len;
+}
+
+
+int i2o_proc_read_dev_name(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ struct i2o_device *d = (struct i2o_device*)data;
+
+ if ( d->dev_name[0] == '\0' )
+ return 0;
+
+ len = sprintf(buf, "%s\n", d->dev_name);
+
+ return len;
+}
+
+
+
+int i2o_proc_read_ddm(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ struct i2o_device *d = (struct i2o_device*)data;
+ static u32 work32[128];
+ static u16 *work16 = (u16*)work32;
+ int token;
+ char mod[25];
+
+ spin_lock(&i2o_proc_lock);
+
+ len = 0;
+
+ token = i2o_query_scalar(d->controller, d->id, proc_context,
+ 0xF101, // ParamGroup F101h (DDM Identity)
+ -1, // all fields
+ &work32,
+ sizeof(work32),
+ &i2o_proc_token);
+
+ if(token < 0)
+ {
+ len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ len += sprintf(buf, "Registering DDM TID: 0x%03x\n", work16[0]&0xFFF);
+
+ memcpy(mod, (char*)(work16+1), 24);
+ mod[24] = '\0';
+ len += sprintf(buf+len, "Module Name: %s\n", mod);
+
+ memcpy(mod, (char*)(work16+13), 8);
+ mod[8] = '\0';
+ len += sprintf(buf+len, "Module Rev: %s\n", mod);
+
+ len += sprintf(buf+len, "Serial Number: ");
+ len = print_serial_number(buf, len,
+ (u8*)(work16+17),
+ /* allow for SNLen plus
+ * possible trailing '\0'
+ */
+ sizeof(work32)-(17*sizeof(u16))-2
+ );
+ len += sprintf(buf+len, "\n");
+
+ spin_unlock(&i2o_proc_lock);
+
+ return len;
+}
+
+int i2o_proc_read_uinfo(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ struct i2o_device *d = (struct i2o_device*)data;
+ static u32 work32[128];
+ int token;
+ char sz[65];
+
+ spin_lock(&i2o_proc_lock);
+
+ len = 0;
+
+ token = i2o_query_scalar(d->controller, d->id, proc_context,
+ 0xF102, // ParamGroup F102h (User Information)
+ -1, // all fields
+ &work32,
+ sizeof(work32),
+ &i2o_proc_token);
+
+ if(token < 0)
+ {
+ len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ memcpy(sz, (char*)work32, 64);
+ sz[64] = '\0';
+ len += sprintf(buf, "Device Name: %s\n", sz);
+
+ memcpy(sz, (char*)(work32+16), 64);
+ sz[64] = '\0';
+ len += sprintf(buf+len, "Service Name: %s\n", sz);
+
+ memcpy(sz, (char*)(work32+32), 64);
+ sz[64] = '\0';
+ len += sprintf(buf+len, "Physical Name: %s\n", sz);
+
+ memcpy(sz, (char*)(work32+48), 4);
+ sz[4] = '\0';
+ len += sprintf(buf+len, "Instance Number: %s\n", sz);
+
+ spin_unlock(&i2o_proc_lock);
+
+ return len;
+}
+
+static int print_serial_number(char *buff, int pos, u8 *serialno, int max_len)
+{
+ int i;
+
+ /* 19990419 -sralston
+ * The I2O v1.5 (and v2.0 so far) "official specification"
+ * got serial numbers WRONG!
+ * Apparently, and despite what Section 3.4.4 says and
+ * Figure 3-35 shows (pg 3-39 in the pdf doc),
+ * the convention / consensus seems to be:
+ * + First byte is SNFormat
+ * + Second byte is SNLen (but only if SNFormat==7 (?))
+ * + (v2.0) SCSI+BS may use IEEE Registered (64 or 128 bit) format
+ */
+ switch(serialno[0])
+ {
+ case I2O_SNFORMAT_BINARY: /* Binary */
+ pos += sprintf(buff+pos, "0x");
+ for(i = 0; i < serialno[1]; i++)
+ {
+ pos += sprintf(buff+pos, "%02X", serialno[2+i]);
+ }
+ break;
+
+ case I2O_SNFORMAT_ASCII: /* ASCII */
+ if ( serialno[1] < ' ' ) /* printable or SNLen? */
+ {
+ /* sanity */
+ max_len = (max_len < serialno[1]) ? max_len : serialno[1];
+ serialno[1+max_len] = '\0';
+
+ /* just print it */
+ pos += sprintf(buff+pos, "%s", &serialno[2]);
+ }
+ else
+ {
+ /* print chars for specified length */
+ for(i = 0; i < serialno[1]; i++)
+ {
+ pos += sprintf(buff+pos, "%c", serialno[2+i]);
+ }
+ }
+ break;
+
+ case I2O_SNFORMAT_UNICODE: /* UNICODE */
+ pos += sprintf(buff+pos, "UNICODE Format. Can't Display\n");
+ break;
+
+ case I2O_SNFORMAT_LAN48_MAC: /* LAN-48 MAC Address */
+ pos += sprintf(buff+pos,
+ "LAN-48 MAC Address @ %02X:%02X:%02X:%02X:%02X:%02X",
+ serialno[2], serialno[3],
+ serialno[4], serialno[5],
+ serialno[6], serialno[7]);
+
+ case I2O_SNFORMAT_WAN: /* WAN MAC Address */
+ /* FIXME: Figure out what a WAN access address looks like?? */
+ pos += sprintf(buff+pos, "WAN Access Address");
+ break;
+
+
+/* plus new in v2.0 */
+ case I2O_SNFORMAT_LAN64_MAC: /* LAN-64 MAC Address */
+ /* FIXME: Figure out what a LAN-64 address really looks like?? */
+ pos += sprintf(buff+pos,
+ "LAN-64 MAC Address @ [?:%02X:%02X:?] %02X:%02X:%02X:%02X:%02X:%02X",
+ serialno[8], serialno[9],
+ serialno[2], serialno[3],
+ serialno[4], serialno[5],
+ serialno[6], serialno[7]);
+ break;
+
+
+ case I2O_SNFORMAT_DDM: /* I2O DDM */
+ pos += sprintf(buff+pos,
+ "DDM: Tid=%03Xh, Rsvd=%04Xh, OrgId=%04Xh",
+ *(u16*)&serialno[2],
+ *(u16*)&serialno[4],
+ *(u16*)&serialno[6]);
+ break;
+
+ case I2O_SNFORMAT_IEEE_REG64: /* IEEE Registered (64-bit) */
+ case I2O_SNFORMAT_IEEE_REG128: /* IEEE Registered (128-bit) */
+ /* FIXME: Figure if this is even close?? */
+ pos += sprintf(buff+pos,
+ "IEEE NodeName(hi,lo)=(%08Xh:%08Xh), PortName(hi,lo)=(%08Xh:%08Xh)\n",
+ *(u32*)&serialno[2],
+ *(u32*)&serialno[6],
+ *(u32*)&serialno[10],
+ *(u32*)&serialno[14]);
+ break;
+
+
+ case I2O_SNFORMAT_UNKNOWN: /* Unknown 0 */
+ case I2O_SNFORMAT_UNKNOWN2: /* Unknown 0xff */
+ default:
+ pos += sprintf(buff+pos, "Unknown Data Format");
+ break;
+ }
+
+ return pos;
+}
+
+/* LAN group 0000h - Device info (scalar) */
+int i2o_proc_read_lan_dev_info(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ struct i2o_device *d = (struct i2o_device*)data;
+ static u32 work32[56];
+ static u8 *work8 = (u8*)work32;
+ static u16 *work16 = (u16*)work32;
+ static u64 *work64 = (u64*)work32;
+ int token;
+
+ spin_lock(&i2o_proc_lock);
+
+ len = 0;
+
+ token = i2o_query_scalar(d->controller, d->id, proc_context,
+ 0x0000, -1, &work32, 56*4, &i2o_proc_token);
+ if(token < 0)
+ {
+ len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ len += sprintf(buf, "LAN Type ........... ");
+ switch (work16[0])
+ {
+ case 0x0030:
+ len += sprintf(buf+len, "Ethernet, ");
+ break;
+ case 0x0040:
+ len += sprintf(buf+len, "100Base VG, ");
+ break;
+ case 0x0050:
+ len += sprintf(buf+len, "Token Ring, ");
+ break;
+ case 0x0060:
+ len += sprintf(buf+len, "FDDI, ");
+ break;
+ case 0x0070:
+ len += sprintf(buf+len, "Fibre Channel, ");
+ break;
+ default:
+ len += sprintf(buf+len, "Unknown type, ");
+ break;
+ }
+
+ if (work16[1]&0x00000001)
+ len += sprintf(buf+len, "emulated LAN, ");
+ else
+ len += sprintf(buf+len, "physical LAN port, ");
+
+ if (work16[1]&0x00000002)
+ len += sprintf(buf+len, "full duplex\n");
+ else
+ len += sprintf(buf+len, "simplex\n");
+
+ len += sprintf(buf+len, "Address format: ");
+ switch(work8[4]) {
+ case 0x00:
+ len += sprintf(buf+len, "IEEE 48bit\n");
+ break;
+ case 0x01:
+ len += sprintf(buf+len, "FC IEEE\n");
+ break;
+ default:
+ len += sprintf(buf+len, "Unknown\n");
+ break;
+ }
+
+ len += sprintf(buf+len, "State: ");
+ switch(work8[5])
+ {
+ case 0x00:
+ len += sprintf(buf+len, "Unknown\n");
+ break;
+ case 0x01:
+ len += sprintf(buf+len, "Unclaimed\n");
+ break;
+ case 0x02:
+ len += sprintf(buf+len, "Operational\n");
+ break;
+ case 0x03:
+ len += sprintf(buf+len, "Suspended\n");
+ break;
+ case 0x04:
+ len += sprintf(buf+len, "Resetting\n");
+ break;
+ case 0x05:
+ len += sprintf(buf+len, "Error\n");
+ break;
+ case 0x06:
+ len += sprintf(buf+len, "Operational no Rx\n");
+ break;
+ case 0x07:
+ len += sprintf(buf+len, "Suspended no Rx\n");
+ break;
+ default:
+ len += sprintf(buf+len, "Unspecified\n");
+ break;
+ }
+
+ len += sprintf(buf+len, "Error status: ");
+ if(work16[3]&0x0001)
+ len += sprintf(buf+len, "Transmit Control Unit Inoperative ");
+ if(work16[3]&0x0002)
+ len += sprintf(buf+len, "Receive Control Unit Inoperative\n");
+ if(work16[3]&0x0004)
+ len += sprintf(buf+len, "Local memory Allocation Error\n");
+ len += sprintf(buf+len, "\n");
+
+ len += sprintf(buf+len, "Min Packet size: %d\n", work32[2]);
+ len += sprintf(buf+len, "Max Packet size: %d\n", work32[3]);
+ len += sprintf(buf+len, "HW Address: "
+ "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+ work8[16],work8[17],work8[18],work8[19],
+ work8[20],work8[21],work8[22],work8[23]);
+
+ len += sprintf(buf+len, "Max Tx Wire Speed: " FMT_U64_HEX " bps\n", U64_VAL(&work64[3]));
+ len += sprintf(buf+len, "Max Rx Wire Speed: " FMT_U64_HEX " bps\n", U64_VAL(&work64[4]));
+
+ len += sprintf(buf+len, "Min SDU packet size: 0x%08x\n", work32[10]);
+ len += sprintf(buf+len, "Max SDU packet size: 0x%08x\n", work32[11]);
+
+ spin_unlock(&i2o_proc_lock);
+ return len;
+}
+
+/* LAN group 0001h - MAC address table (scalar) */
+int i2o_proc_read_lan_mac_addr(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ struct i2o_device *d = (struct i2o_device*)data;
+ static u32 work32[48];
+ static u8 *work8 = (u8*)work32;
+ int token;
+
+ spin_lock(&i2o_proc_lock);
+ len = 0;
+
+ token = i2o_query_scalar(d->controller, d->id, proc_context,
+ 0x0001, -1, &work32, 48*4, &i2o_proc_token);
+ if(token < 0)
+ {
+ len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ len += sprintf(buf, "Active address: "
+ "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+ work8[0],work8[1],work8[2],work8[3],
+ work8[4],work8[5],work8[6],work8[7]);
+ len += sprintf(buf+len, "Current address: "
+ "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+ work8[8],work8[9],work8[10],work8[11],
+ work8[12],work8[13],work8[14],work8[15]);
+ len += sprintf(buf+len, "Functional address mask: "
+ "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+ work8[16],work8[17],work8[18],work8[19],
+ work8[20],work8[21],work8[22],work8[23]);
+
+ len += sprintf(buf+len, "Filter mask: 0x%08x\n", work32[6]);
+ len += sprintf(buf+len, "HW/DDM capabilities: 0x%08x\n", work32[7]);
+ len += sprintf(buf+len, " Unicast packets %ssupported (%sabled)\n",
+ (work32[7]&0x00000001)?"":"not ",
+ (work32[6]&0x00000001)?"en":"dis");
+ len += sprintf(buf+len, " Promiscuous mode %ssupported (%sabled)\n",
+ (work32[7]&0x00000002)?"":"not",
+ (work32[6]&0x00000002)?"en":"dis");
+ len += sprintf(buf+len,
+ " Multicast promiscuous mode %ssupported (%sabled)\n",
+ (work32[7]&0x00000004)?"":"not ",
+ (work32[6]&0x00000004)?"en":"dis");
+ len += sprintf(buf+len,
+ " Broadcast Reception disabling %ssupported (%sabled)\n",
+ (work32[7]&0x00000100)?"":"not ",
+ (work32[6]&0x00000100)?"en":"dis");
+ len += sprintf(buf+len,
+ " Multicast Reception disabling %ssupported (%sabled)\n",
+ (work32[7]&0x00000200)?"":"not ",
+ (work32[6]&0x00000200)?"en":"dis");
+ len += sprintf(buf+len,
+ " Functional address disabling %ssupported (%sabled)\n",
+ (work32[7]&0x00000400)?"":"not ",
+ (work32[6]&0x00000400)?"en":"dis");
+ len += sprintf(buf+len, " MAC reporting %ssupported\n",
+ (work32[7]&0x00000800)?"":"not ");
+
+ len += sprintf(buf+len, " MAC Reporting mode: ");
+ if (work32[6]&0x00000800)
+ len += sprintf(buf+len, "Pass only priority MAC packets\n");
+ else if (work32[6]&0x00001000)
+ len += sprintf(buf+len, "Pass all MAC packets\n");
+ else if (work32[6]&0x00001800)
+ len += sprintf(buf+len, "Pass all MAC packets (promiscuous)\n");
+ else
+ len += sprintf(buf+len, "Do not pass MAC packets\n");
+
+ len += sprintf(buf+len, "Number of multicast addesses: %d\n", work32[8]);
+ len += sprintf(buf+len, "Perfect filtering for max %d multicast addesses\n",
+ work32[9]);
+ len += sprintf(buf+len, "Imperfect filtering for max %d multicast addesses\n",
+ work32[10]);
+
+ spin_unlock(&i2o_proc_lock);
+
+ return len;
+}
+
+/* LAN group 0001h, field 1 - Current MAC (scalar) */
+int i2o_proc_read_lan_curr_addr(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ struct i2o_device *d = (struct i2o_device*)data;
+ static u32 work32[2];
+ static u8 *work8 = (u8*)work32;
+ int token;
+
+ spin_lock(&i2o_proc_lock);
+ len = 0;
+
+ token = i2o_query_scalar(d->controller, d->id, proc_context,
+ 0x0001, 2, &work32, 8, &i2o_proc_token);
+ if(token < 0)
+ {
+ len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ len += sprintf(buf, "Current address: "
+ "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+ work8[0],work8[1],work8[2],work8[3],
+ work8[4],work8[5],work8[6],work8[7]);
+
+ spin_unlock(&i2o_proc_lock);
+ return len;
+}
+
+
+#if 0
+/* LAN group 0002h - Multicast MAC address table (table) */
+int i2o_proc_read_lan_mcast_addr(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ struct i2o_device *d = (struct i2o_device*)data;
+ static u8 work8[32];
+ static u32 field32[8];
+ static u8 *field8 = (u8 *)field32;
+ int token;
+
+ spin_lock(&i2o_proc_lock);
+ len = 0;
+
+ token = i2o_query_table_polled(d->controller, d->id, &work8, 32,
+ 0x0002, 0, field32, 8);
+
+ switch (token) {
+ case -ETIMEDOUT:
+ len += sprintf(buf, "Timeout reading table.\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ break;
+ case -ENOMEM:
+ len += sprintf(buf, "No free memory to read the table.\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ break;
+ case -EBADR:
+ len += sprintf(buf, "Error reading field.\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ break;
+ default:
+ break;
+ }
+
+ len += sprintf(buf, "Multicast MAC address: "
+ "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+ field8[0],field8[1],field8[2],field8[3],
+ field8[4],field8[5],field8[6],field8[7]);
+
+ spin_unlock(&i2o_proc_lock);
+ return len;
+}
+#endif
+
+/* LAN group 0003h - Batch Control (scalar) */
+int i2o_proc_read_lan_batch_control(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ struct i2o_device *d = (struct i2o_device*)data;
+ static u32 work32[18];
+ int token;
+
+ spin_lock(&i2o_proc_lock);
+ len = 0;
+
+ token = i2o_query_scalar(d->controller, d->id, proc_context,
+ 0x0003, -1, &work32, 72, &i2o_proc_token);
+ if(token < 0)
+ {
+ len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ len += sprintf(buf, "Batch mode ");
+ if (work32[0]&0x00000001)
+ len += sprintf(buf+len, "disabled");
+ else
+ len += sprintf(buf+len, "enabled");
+ if (work32[0]&0x00000002)
+ len += sprintf(buf+len, " (current setting)");
+ if (work32[0]&0x00000004)
+ len += sprintf(buf+len, ", forced");
+ else
+ len += sprintf(buf+len, ", toggle");
+ len += sprintf(buf+len, "\n");
+
+ if(d->i2oversion == 0x00) { /* Reserved in 1.53 and 2.0 */
+ len += sprintf(buf+len, "Rising Load Delay: %d ms\n",
+ work32[1]/10);
+ len += sprintf(buf+len, "Rising Load Threshold: %d ms\n",
+ work32[2]/10);
+ len += sprintf(buf+len, "Falling Load Delay: %d ms\n",
+ work32[3]/10);
+ len += sprintf(buf+len, "Falling Load Threshold: %d ms\n",
+ work32[4]/10);
+ }
+
+ len += sprintf(buf+len, "Max Rx Batch Count: %d\n", work32[5]);
+ len += sprintf(buf+len, "Max Rx Batch Delay: %d\n", work32[6]);
+
+ if(d->i2oversion == 0x00) {
+ len += sprintf(buf+len,
+ "Transmission Completion Reporting Delay: %d ms\n",
+ work32[7]);
+ } else {
+ len += sprintf(buf+len, "Max Tx Batch Delay: %d\n", work32[7]);
+ len += sprintf(buf+len, "Max Tx Batch Count: %d\n", work32[8]);
+ }
+
+ spin_unlock(&i2o_proc_lock);
+ return len;
+}
+
+/* LAN group 0004h - LAN Operation (scalar) */
+int i2o_proc_read_lan_operation(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ struct i2o_device *d = (struct i2o_device*)data;
+ static u32 work32[5];
+ int token;
+
+ spin_lock(&i2o_proc_lock);
+ len = 0;
+
+ token = i2o_query_scalar(d->controller, d->id, proc_context,
+ 0x0004, -1, &work32, 20, &i2o_proc_token);
+ if(token < 0)
+ {
+ len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ len += sprintf(buf, "Packet prepadding (32b words): %d\n", work32[0]);
+ len += sprintf(buf+len, "Transmission error reporting: %s\n",
+ (work32[1]&1)?"on":"off");
+ len += sprintf(buf+len, "Bad packet handling: %s\n",
+ (work32[1]&0x2)?"by host":"by DDM");
+ len += sprintf(buf+len, "Packet orphan limit: %d\n", work32[2]);
+
+ len += sprintf(buf+len, "Tx modes:\n");
+ if (work32[3]&0x00000004)
+ len += sprintf(buf+len, " HW CRC supressed\n");
+ else
+ len += sprintf(buf+len, " HW CRC\n");
+ if (work32[3]&0x00000100)
+ len += sprintf(buf+len, " HW IPv4 checksumming\n");
+ if (work32[3]&0x00000200)
+ len += sprintf(buf+len, " HW TCP checksumming\n");
+ if (work32[3]&0x00000400)
+ len += sprintf(buf+len, " HW UDP checksumming\n");
+ if (work32[3]&0x00000800)
+ len += sprintf(buf+len, " HW RSVP checksumming\n");
+ if (work32[3]&0x00001000)
+ len += sprintf(buf+len, " HW ICMP checksumming\n");
+ if (work32[3]&0x00002000)
+ len += sprintf(buf+len, " Loopback packet not delivered\n");
+
+ len += sprintf(buf+len, "Rx modes:\n");
+ if (work32[4]&0x00000004)
+ len += sprintf(buf+len, " FCS in payload\n");
+ if (work32[4]&0x00000100)
+ len += sprintf(buf+len, " HW IPv4 checksum validation\n");
+ if (work32[4]&0x00000200)
+ len += sprintf(buf+len, " HW TCP checksum validation\n");
+ if (work32[4]&0x00000400)
+ len += sprintf(buf+len, " HW UDP checksum validation\n");
+ if (work32[4]&0x00000800)
+ len += sprintf(buf+len, " HW RSVP checksum validation\n");
+ if (work32[4]&0x00001000)
+ len += sprintf(buf+len, " HW ICMP checksum validation\n");
+
+ spin_unlock(&i2o_proc_lock);
+ return len;
+}
+
+/* LAN group 0005h - Media operation (scalar) */
+int i2o_proc_read_lan_media_operation(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ struct i2o_device *d = (struct i2o_device*)data;
+ static u32 work32[9];
+ static u8 *work8 = (u8*)work32;
+ static u64 *work64 = (u64*)work32;
+ int token;
+
+ spin_lock(&i2o_proc_lock);
+ len = 0;
+
+ token = i2o_query_scalar(d->controller, d->id, proc_context,
+ 0x0005, -1, &work32, 36, &i2o_proc_token);
+ if(token < 0)
+ {
+ len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ len += sprintf(buf, "Connector type: ");
+ switch(work32[0])
+ {
+ case 0x00000000:
+ len += sprintf(buf+len, "OTHER\n");
+ break;
+ case 0x00000001:
+ len += sprintf(buf+len, "UNKNOWN\n");
+ break;
+ case 0x00000002:
+ len += sprintf(buf+len, "AUI\n");
+ break;
+ case 0x00000003:
+ len += sprintf(buf+len, "UTP\n");
+ break;
+ case 0x00000004:
+ len += sprintf(buf+len, "BNC\n");
+ break;
+ case 0x00000005:
+ len += sprintf(buf+len, "RJ45\n");
+ break;
+ case 0x00000006:
+ len += sprintf(buf+len, "STP DB9\n");
+ break;
+ case 0x00000007:
+ len += sprintf(buf+len, "FIBER MIC\n");
+ break;
+ case 0x00000008:
+ len += sprintf(buf+len, "APPLE AUI\n");
+ break;
+ case 0x00000009:
+ len += sprintf(buf+len, "MII\n");
+ break;
+ case 0x0000000A:
+ len += sprintf(buf+len, "DB9\n");
+ break;
+ case 0x0000000B:
+ len += sprintf(buf+len, "HSSDC\n");
+ break;
+ case 0x0000000C:
+ len += sprintf(buf+len, "DUPLEX SC FIBER\n");
+ break;
+ case 0x0000000D:
+ len += sprintf(buf+len, "DUPLEX ST FIBER\n");
+ break;
+ case 0x0000000E:
+ len += sprintf(buf+len, "TNC/BNC\n");
+ break;
+ case 0xFFFFFFFF:
+ len += sprintf(buf+len, "HW DEFAULT\n");
+ break;
+ }
+
+ len += sprintf(buf+len, "Connection type: ");
+ switch(work32[1])
+ {
+ case I2O_LAN_UNKNOWN:
+ len += sprintf(buf+len, "UNKNOWN\n");
+ break;
+ case I2O_LAN_AUI:
+ len += sprintf(buf+len, "AUI\n");
+ break;
+ case I2O_LAN_10BASE5:
+ len += sprintf(buf+len, "10BASE5\n");
+ break;
+ case I2O_LAN_FIORL:
+ len += sprintf(buf+len, "FIORL\n");
+ break;
+ case I2O_LAN_10BASE2:
+ len += sprintf(buf+len, "10BASE2\n");
+ break;
+ case I2O_LAN_10BROAD36:
+ len += sprintf(buf+len, "10BROAD36\n");
+ break;
+ case I2O_LAN_10BASE_T:
+ len += sprintf(buf+len, "10BASE-T\n");
+ break;
+ case I2O_LAN_10BASE_FP:
+ len += sprintf(buf+len, "10BASE-FP\n");
+ break;
+ case I2O_LAN_10BASE_FB:
+ len += sprintf(buf+len, "10BASE-FB\n");
+ break;
+ case I2O_LAN_10BASE_FL:
+ len += sprintf(buf+len, "10BASE-FL\n");
+ break;
+ case I2O_LAN_100BASE_TX:
+ len += sprintf(buf+len, "100BASE-TX\n");
+ break;
+ case I2O_LAN_100BASE_FX:
+ len += sprintf(buf+len, "100BASE-FX\n");
+ break;
+ case I2O_LAN_100BASE_T4:
+ len += sprintf(buf+len, "100BASE-T4\n");
+ break;
+ case I2O_LAN_1000BASE_SX:
+ len += sprintf(buf+len, "1000BASE-SX\n");
+ break;
+ case I2O_LAN_1000BASE_LX:
+ len += sprintf(buf+len, "1000BASE-LX\n");
+ break;
+ case I2O_LAN_1000BASE_CX:
+ len += sprintf(buf+len, "1000BASE-CX\n");
+ break;
+ case I2O_LAN_1000BASE_T:
+ len += sprintf(buf+len, "1000BASE-T\n");
+ break;
+ case I2O_LAN_100VG_ETHERNET:
+ len += sprintf(buf+len, "100VG-ETHERNET\n");
+ break;
+ case I2O_LAN_100VG_TR:
+ len += sprintf(buf+len, "100VG-TOKEN RING\n");
+ break;
+ case I2O_LAN_4MBIT:
+ len += sprintf(buf+len, "4MBIT TOKEN RING\n");
+ break;
+ case I2O_LAN_16MBIT:
+ len += sprintf(buf+len, "16 Mb Token Ring\n");
+ break;
+ case I2O_LAN_125MBAUD:
+ len += sprintf(buf+len, "125 MBAUD FDDI\n");
+ break;
+ case I2O_LAN_POINT_POINT:
+ len += sprintf(buf+len, "Point-to-point\n");
+ break;
+ case I2O_LAN_ARB_LOOP:
+ len += sprintf(buf+len, "Arbitrated loop\n");
+ break;
+ case I2O_LAN_PUBLIC_LOOP:
+ len += sprintf(buf+len, "Public loop\n");
+ break;
+ case I2O_LAN_FABRIC:
+ len += sprintf(buf+len, "Fabric\n");
+ break;
+ case I2O_LAN_EMULATION:
+ len += sprintf(buf+len, "Emulation\n");
+ break;
+ case I2O_LAN_OTHER:
+ len += sprintf(buf+len, "Other\n");
+ break;
+ case I2O_LAN_DEFAULT:
+ len += sprintf(buf+len, "HW default\n");
+ break;
+ }
+
+ len += sprintf(buf+len, "Current Tx Wire Speed: " FMT_U64_HEX " bps\n",
+ U64_VAL(&work64[1]));
+ len += sprintf(buf+len, "Current Rx Wire Speed: " FMT_U64_HEX " bps\n",
+ U64_VAL(&work64[2]));
+
+ len += sprintf(buf+len, "%s duplex\n", (work8[24]&1)?"Full":"Half");
+
+ len += sprintf(buf+len, "Link status: ");
+ if(work8[25] == 0x00)
+ len += sprintf(buf+len, "Unknown\n");
+ else if(work8[25] == 0x01)
+ len += sprintf(buf+len, "Normal\n");
+ else if(work8[25] == 0x02)
+ len += sprintf(buf+len, "Failure\n");
+ else if(work8[25] == 0x03)
+ len += sprintf(buf+len, "Reset\n");
+ else
+ len += sprintf(buf+len, "Unspecified\n");
+
+ if (d->i2oversion == 0x00) { /* Reserved in 1.53 and 2.0 */
+ len += sprintf(buf+len, "Bad packets handled by: %s\n",
+ (work8[26] == 0xFF)?"host":"DDM");
+ }
+ if (d->i2oversion != 0x00) {
+ len += sprintf(buf+len, "Duplex mode target: ");
+ switch (work8[27]) {
+ case 0:
+ len += sprintf(buf+len, "Half Duplex\n");
+ break;
+ case 1:
+ len += sprintf(buf+len, "Full Duplex\n");
+ break;
+ default:
+ len += sprintf(buf+len, "\n");
+ break;
+ }
+
+ len += sprintf(buf+len, "Connector type target: ");
+ switch(work32[7])
+ {
+ case 0x00000000:
+ len += sprintf(buf+len, "OTHER\n");
+ break;
+ case 0x00000001:
+ len += sprintf(buf+len, "UNKNOWN\n");
+ break;
+ case 0x00000002:
+ len += sprintf(buf+len, "AUI\n");
+ break;
+ case 0x00000003:
+ len += sprintf(buf+len, "UTP\n");
+ break;
+ case 0x00000004:
+ len += sprintf(buf+len, "BNC\n");
+ break;
+ case 0x00000005:
+ len += sprintf(buf+len, "RJ45\n");
+ break;
+ case 0x00000006:
+ len += sprintf(buf+len, "STP DB9\n");
+ break;
+ case 0x00000007:
+ len += sprintf(buf+len, "FIBER MIC\n");
+ break;
+ case 0x00000008:
+ len += sprintf(buf+len, "APPLE AUI\n");
+ break;
+ case 0x00000009:
+ len += sprintf(buf+len, "MII\n");
+ break;
+ case 0x0000000A:
+ len += sprintf(buf+len, "DB9\n");
+ break;
+ case 0x0000000B:
+ len += sprintf(buf+len, "HSSDC\n");
+ break;
+ case 0x0000000C:
+ len += sprintf(buf+len, "DUPLEX SC FIBER\n");
+ break;
+ case 0x0000000D:
+ len += sprintf(buf+len, "DUPLEX ST FIBER\n");
+ break;
+ case 0x0000000E:
+ len += sprintf(buf+len, "TNC/BNC\n");
+ break;
+ case 0xFFFFFFFF:
+ len += sprintf(buf+len, "HW DEFAULT\n");
+ break;
+ default:
+ len += sprintf(buf+len, "\n");
+ break;
+ }
+
+ len += sprintf(buf+len, "Connection type target: ");
+ switch(work32[8])
+ {
+ case I2O_LAN_UNKNOWN:
+ len += sprintf(buf+len, "UNKNOWN\n");
+ break;
+ case I2O_LAN_AUI:
+ len += sprintf(buf+len, "AUI\n");
+ break;
+ case I2O_LAN_10BASE5:
+ len += sprintf(buf+len, "10BASE5\n");
+ break;
+ case I2O_LAN_FIORL:
+ len += sprintf(buf+len, "FIORL\n");
+ break;
+ case I2O_LAN_10BASE2:
+ len += sprintf(buf+len, "10BASE2\n");
+ break;
+ case I2O_LAN_10BROAD36:
+ len += sprintf(buf+len, "10BROAD36\n");
+ break;
+ case I2O_LAN_10BASE_T:
+ len += sprintf(buf+len, "10BASE-T\n");
+ break;
+ case I2O_LAN_10BASE_FP:
+ len += sprintf(buf+len, "10BASE-FP\n");
+ break;
+ case I2O_LAN_10BASE_FB:
+ len += sprintf(buf+len, "10BASE-FB\n");
+ break;
+ case I2O_LAN_10BASE_FL:
+ len += sprintf(buf+len, "10BASE-FL\n");
+ break;
+ case I2O_LAN_100BASE_TX:
+ len += sprintf(buf+len, "100BASE-TX\n");
+ break;
+ case I2O_LAN_100BASE_FX:
+ len += sprintf(buf+len, "100BASE-FX\n");
+ break;
+ case I2O_LAN_100BASE_T4:
+ len += sprintf(buf+len, "100BASE-T4\n");
+ break;
+ case I2O_LAN_1000BASE_SX:
+ len += sprintf(buf+len, "1000BASE-SX\n");
+ break;
+ case I2O_LAN_1000BASE_LX:
+ len += sprintf(buf+len, "1000BASE-LX\n");
+ break;
+ case I2O_LAN_1000BASE_CX:
+ len += sprintf(buf+len, "1000BASE-CX\n");
+ break;
+ case I2O_LAN_1000BASE_T:
+ len += sprintf(buf+len, "1000BASE-T\n");
+ break;
+ case I2O_LAN_100VG_ETHERNET:
+ len += sprintf(buf+len, "100VG-ETHERNET\n");
+ break;
+ case I2O_LAN_100VG_TR:
+ len += sprintf(buf+len, "100VG-TOKEN RING\n");
+ break;
+ case I2O_LAN_4MBIT:
+ len += sprintf(buf+len, "4MBIT TOKEN RING\n");
+ break;
+ case I2O_LAN_16MBIT:
+ len += sprintf(buf+len, "16 Mb Token Ring\n");
+ break;
+ case I2O_LAN_125MBAUD:
+ len += sprintf(buf+len, "125 MBAUD FDDI\n");
+ break;
+ case I2O_LAN_POINT_POINT:
+ len += sprintf(buf+len, "Point-to-point\n");
+ break;
+ case I2O_LAN_ARB_LOOP:
+ len += sprintf(buf+len, "Arbitrated loop\n");
+ break;
+ case I2O_LAN_PUBLIC_LOOP:
+ len += sprintf(buf+len, "Public loop\n");
+ break;
+ case I2O_LAN_FABRIC:
+ len += sprintf(buf+len, "Fabric\n");
+ break;
+ case I2O_LAN_EMULATION:
+ len += sprintf(buf+len, "Emulation\n");
+ break;
+ case I2O_LAN_OTHER:
+ len += sprintf(buf+len, "Other\n");
+ break;
+ case I2O_LAN_DEFAULT:
+ len += sprintf(buf+len, "HW default\n");
+ break;
+ default:
+ len += sprintf(buf+len, "\n");
+ break;
+ }
+ }
+ spin_unlock(&i2o_proc_lock);
+ return len;
+}
+
+#if 0
+/* LAN group 0006h - Alternate address (table) */
+int i2o_proc_read_lan_alt_addr(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ struct i2o_device *d = (struct i2o_device*)data;
+ static u8 work8[32];
+ static u32 field32[2];
+ static u8 *field8 = (u8 *)field32;
+ int token;
+
+ spin_lock(&i2o_proc_lock);
+ len = 0;
+
+ token = i2o_query_table_polled(d->controller, d->id, &work8, 32,
+ 0x0006, 0, field32, 8);
+ switch (token) {
+ case -ETIMEDOUT:
+ len += sprintf(buf, "Timeout reading table.\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ break;
+ case -ENOMEM:
+ len += sprintf(buf, "No free memory to read the table.\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ break;
+ case -EBADR:
+ len += sprintf(buf, "Error reading field.\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ break;
+ default:
+ break;
+ }
+
+ len += sprintf(buf, "Alternate Address: "
+ "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+ field8[0],field8[1],field8[2],field8[3],
+ field8[4],field8[5],field8[6],field8[7]);
+
+ spin_unlock(&i2o_proc_lock);
+ return len;
+}
+#endif
+
+/* LAN group 0007h - Transmit info (scalar) */
+int i2o_proc_read_lan_tx_info(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ struct i2o_device *d = (struct i2o_device*)data;
+ static u32 work32[10];
+ int token;
+
+ spin_lock(&i2o_proc_lock);
+ len = 0;
+
+ token = i2o_query_scalar(d->controller, d->id, proc_context,
+ 0x0007, -1, &work32, 8, &i2o_proc_token);
+ if(token < 0)
+ {
+ len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ len += sprintf(buf, "Max SG Elements per packet: %d\n", work32[0]);
+ len += sprintf(buf+len, "Max SG Elements per chain: %d\n", work32[1]);
+ len += sprintf(buf+len, "Max outstanding packets: %d\n", work32[2]);
+ len += sprintf(buf+len, "Max packets per request: %d\n", work32[3]);
+
+ len += sprintf(buf+len, "Tx modes:\n");
+ if(work32[4]&0x00000002)
+ len += sprintf(buf+len, " No DA in SGL\n");
+ if(work32[4]&0x00000004)
+ len += sprintf(buf+len, " CRC suppression\n");
+ if(work32[4]&0x00000008)
+ len += sprintf(buf+len, " Loop suppression\n");
+ if(work32[4]&0x00000010)
+ len += sprintf(buf+len, " MAC insertion\n");
+ if(work32[4]&0x00000020)
+ len += sprintf(buf+len, " RIF insertion\n");
+ if(work32[4]&0x00000100)
+ len += sprintf(buf+len, " IPv4 Checksum\n");
+ if(work32[4]&0x00000200)
+ len += sprintf(buf+len, " TCP Checksum\n");
+ if(work32[4]&0x00000400)
+ len += sprintf(buf+len, " UDP Checksum\n");
+ if(work32[4]&0x00000800)
+ len += sprintf(buf+len, " RSVP Checksum\n");
+ if(work32[4]&0x00001000)
+ len += sprintf(buf+len, " ICMP Checksum\n");
+ if (d->i2oversion == 0x00) {
+ if(work32[4]&0x00008000)
+ len += sprintf(buf+len, " Loopback Enabled\n");
+ if(work32[4]&0x00010000)
+ len += sprintf(buf+len, " Loopback Suppression Enabled\n");
+ } else {
+ if(work32[4]&0x00010000)
+ len += sprintf(buf+len, " Loopback Enabled\n");
+ if(work32[4]&0x00020000)
+ len += sprintf(buf+len, " Loopback Suppression Enabled\n");
+ }
+
+ spin_unlock(&i2o_proc_lock);
+ return len;
+}
+
+/* LAN group 0008h - Receive info (scalar) */
+int i2o_proc_read_lan_rx_info(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ struct i2o_device *d = (struct i2o_device*)data;
+ static u32 work32[10];
+ int token;
+
+ spin_lock(&i2o_proc_lock);
+ len = 0;
+
+ token = i2o_query_scalar(d->controller, d->id, proc_context,
+ 0x0008, -1, &work32, 8, &i2o_proc_token);
+ if(token < 0)
+ {
+ len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ len += sprintf(buf, "Max size of chain element: %d\n", work32[0]);
+ len += sprintf(buf+len, "Max number of buckets: %d\n", work32[1]);
+
+ if (d->i2oversion > 0x00) { /* not in 1.5 */
+ len += sprintf(buf+len, "Rx modes: %d\n", work32[2]);
+ len += sprintf(buf+len, "RxMaxBucketsReply: %d\n", work32[3]);
+ len += sprintf(buf+len, "RxMaxPacketsPerBuckets: %d\n", work32[4]);
+ len += sprintf(buf+len, "RxMaxPostBuckets: %d\n", work32[5]);
+ }
+
+ spin_unlock(&i2o_proc_lock);
+ return len;
+}
+
+
+/* LAN group 0100h - LAN Historical statistics (scalar) */
+int i2o_proc_read_lan_hist_stats(char *buf, char **start, off_t offset, int len,
+ int *eof, void *data)
+{
+ struct i2o_device *d = (struct i2o_device*)data;
+ static u64 work64[9];
+ int token;
+
+ spin_lock(&i2o_proc_lock);
+ len = 0;
+
+ token = i2o_query_scalar(d->controller, d->id, proc_context,
+ 0x0100, -1, &work64, 9*8, &i2o_proc_token);
+ if(token < 0)
+ {
+ len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ len += sprintf(buf, "Tx packets: " FMT_U64_HEX "\n", U64_VAL(&work64[0]));
+ len += sprintf(buf+len, "Tx bytes: " FMT_U64_HEX "\n", U64_VAL(&work64[1]));
+ len += sprintf(buf+len, "Rx packets: " FMT_U64_HEX "\n", U64_VAL(&work64[2]));
+ len += sprintf(buf+len, "Rx bytes: " FMT_U64_HEX "\n", U64_VAL(&work64[3]));
+ len += sprintf(buf+len, "Tx errors: " FMT_U64_HEX "\n", U64_VAL(&work64[4]));
+ len += sprintf(buf+len, "Rx errors: " FMT_U64_HEX "\n", U64_VAL(&work64[5]));
+ len += sprintf(buf+len, "Rx dropped: " FMT_U64_HEX "\n", U64_VAL(&work64[6]));
+ len += sprintf(buf+len, "Adapter resets: " FMT_U64_HEX "\n", U64_VAL(&work64[7]));
+ len += sprintf(buf+len, "Adapter suspends: " FMT_U64_HEX "\n", U64_VAL(&work64[8]));
+
+ spin_unlock(&i2o_proc_lock);
+ return len;
+}
+
+
+/* LAN group 0182h - Optional Non Media Specific Transmit Historical Statistics
+ * (scalar) */
+int i2o_proc_read_lan_opt_tx_hist_stats(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ struct i2o_device *d = (struct i2o_device*)data;
+ static u64 work64[9];
+ int token;
+
+ spin_lock(&i2o_proc_lock);
+
+ len = 0;
+
+ token = i2o_query_scalar(d->controller, d->id, proc_context,
+ 0x0182, -1, &work64, 9*8, &i2o_proc_token);
+ if(token < 0)
+ {
+ len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ len += sprintf(buf, "TxRetryCount: " FMT_U64_HEX "\n", U64_VAL(&work64[0]));
+ len += sprintf(buf+len, "DirectedBytesTx: " FMT_U64_HEX "\n", U64_VAL(&work64[1]));
+ len += sprintf(buf+len, "DirectedPacketsTx: " FMT_U64_HEX "\n", U64_VAL(&work64[2]));
+ len += sprintf(buf+len, "MulticastBytesTx: " FMT_U64_HEX "\n", U64_VAL(&work64[3]));
+ len += sprintf(buf+len, "MulticastPacketsTx: " FMT_U64_HEX "\n", U64_VAL(&work64[4]));
+ len += sprintf(buf+len, "BroadcastBytesTx: " FMT_U64_HEX "\n", U64_VAL(&work64[5]));
+ len += sprintf(buf+len, "BroadcastPacketsTx: " FMT_U64_HEX "\n", U64_VAL(&work64[6]));
+ len += sprintf(buf+len, "TotalGroupAddrTxCount: " FMT_U64_HEX "\n", U64_VAL(&work64[7]));
+ len += sprintf(buf+len, "TotalTxPacketsTooShort: " FMT_U64_HEX "\n", U64_VAL(&work64[8]));
+
+ spin_unlock(&i2o_proc_lock);
+ return len;
+}
+
+/* LAN group 0183h - Optional Non Media Specific Receive Historical Statistics
+ * (scalar) */
+int i2o_proc_read_lan_opt_rx_hist_stats(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ struct i2o_device *d = (struct i2o_device*)data;
+ static u64 work64[11];
+ int token;
+
+ spin_lock(&i2o_proc_lock);
+
+ len = 0;
+
+ token = i2o_query_scalar(d->controller, d->id, proc_context,
+ 0x0183, -1, &work64, 11*8, &i2o_proc_token);
+ if(token < 0)
+ {
+ len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ len += sprintf(buf, "ReceiveCRCErrorCount: " FMT_U64_HEX "\n", U64_VAL(&work64[0]));
+ len += sprintf(buf+len, "DirectedBytesRx: " FMT_U64_HEX "\n", U64_VAL(&work64[1]));
+ len += sprintf(buf+len, "DirectedPacketsRx: " FMT_U64_HEX "\n", U64_VAL(&work64[2]));
+ len += sprintf(buf+len, "MulticastBytesRx: " FMT_U64_HEX "\n", U64_VAL(&work64[3]));
+ len += sprintf(buf+len, "MulticastPacketsRx: " FMT_U64_HEX "\n", U64_VAL(&work64[4]));
+ len += sprintf(buf+len, "BroadcastBytesRx: " FMT_U64_HEX "\n", U64_VAL(&work64[5]));
+ len += sprintf(buf+len, "BroadcastPacketsRx: " FMT_U64_HEX "\n", U64_VAL(&work64[6]));
+ len += sprintf(buf+len, "TotalGroupAddrRxCount: " FMT_U64_HEX "\n", U64_VAL(&work64[7]));
+ len += sprintf(buf+len, "TotalRxPacketsTooShort: " FMT_U64_HEX "\n", U64_VAL(&work64[8]));
+ len += sprintf(buf+len, "TotalRxPacketsTooLong: " FMT_U64_HEX "\n", U64_VAL(&work64[9]));
+ len += sprintf(buf+len, "TotalRuntPacketsReceived: " FMT_U64_HEX "\n", U64_VAL(&work64[10]));
+
+ spin_unlock(&i2o_proc_lock);
+ return len;
+}
+
+
+/* LAN group 0400h - Required FDDI Statistics (scalar) */
+int i2o_proc_read_lan_fddi_stats(char *buf, char **start, off_t offset,
+ int len, int *eof, void *data)
+{
+ struct i2o_device *d = (struct i2o_device*)data;
+ static u64 work64[11];
+ int token;
+
+ static char *conf_state[] =
+ {
+ "Isolated",
+ "Local a",
+ "Local b",
+ "Local ab",
+ "Local s",
+ "Wrap a",
+ "Wrap b",
+ "Wrap ab",
+ "Wrap s",
+ "C-Wrap a",
+ "C-Wrap b",
+ "C-Wrap s",
+ "Through",
+ };
+
+ static char *ring_state[] =
+ {
+ "Isolated",
+ "Non-op",
+ "Rind-op",
+ "Detect",
+ "Non-op-Dup",
+ "Ring-op-Dup",
+ "Directed",
+ "Trace"
+ };
+
+ static char *link_state[] =
+ {
+ "Off",
+ "Break",
+ "Trace",
+ "Connect",
+ "Next",
+ "Signal",
+ "Join",
+ "Verify",
+ "Active",
+ "Maintenance"
+ };
+
+ spin_lock(&i2o_proc_lock);
+
+ len = 0;
+
+ token = i2o_query_scalar(d->controller, d->id, proc_context,
+ 0x0400, -1, &work64, 11*8, &i2o_proc_token);
+ if(token < 0)
+ {
+ len += sprintf(buf, "Timeout waiting for reply from IOP\n");
+ spin_unlock(&i2o_proc_lock);
+ return len;
+ }
+
+ len += sprintf(buf, "ConfigurationState: %s\n", conf_state[work64[0]]);
+ len += sprintf(buf+len, "UpstreamNode: " FMT_U64_HEX "\n", U64_VAL(&work64[1]));
+ len += sprintf(buf+len, "DownStreamNode: " FMT_U64_HEX "\n", U64_VAL(&work64[2]));
+ len += sprintf(buf+len, "FrameErrors: " FMT_U64_HEX "\n", U64_VAL(&work64[3]));
+ len += sprintf(buf+len, "FramesLost: " FMT_U64_HEX "\n", U64_VAL(&work64[4]));
+ len += sprintf(buf+len, "RingMgmtState: %s\n", ring_state[work64[5]]);
+ len += sprintf(buf+len, "LCTFailures: " FMT_U64_HEX "\n", U64_VAL(&work64[6]));
+ len += sprintf(buf+len, "LEMRejects: " FMT_U64_HEX "\n", U64_VAL(&work64[7]));
+ len += sprintf(buf+len, "LEMCount: " FMT_U64_HEX "\n", U64_VAL(&work64[8]));
+ len += sprintf(buf+len, "LConnectionState: %s\n", link_state[work64[9]]);
+
+ spin_unlock(&i2o_proc_lock);
+ return len;
+}
+
+static int i2o_proc_create_entries(void *data,
+ i2o_proc_entry *pentry, struct proc_dir_entry *parent)
+{
+ struct proc_dir_entry *ent;
+
+ while(pentry->name != NULL)
+ {
+ ent = create_proc_entry(pentry->name, pentry->mode, parent);
+ if(!ent) return -1;
+
+ ent->data = data;
+ ent->read_proc = pentry->read_proc;
+ ent->write_proc = pentry->write_proc;
+ ent->nlink = 1;
+
+ pentry++;
+ }
+
+ return 0;
+}
+
+static void i2o_proc_remove_entries(i2o_proc_entry *pentry,
+ struct proc_dir_entry *parent)
+{
+ while(pentry->name != NULL)
+ {
+ remove_proc_entry(pentry->name, parent);
+ pentry++;
+ }
+}
+
+static int i2o_proc_add_controller(struct i2o_controller *pctrl,
+ struct proc_dir_entry *root )
+{
+ struct proc_dir_entry *dir, *dir1;
+ struct i2o_device *dev;
+ char buff[10];
+
+ sprintf(buff, "iop%d", pctrl->unit);
+
+ dir = create_proc_entry(buff, S_IFDIR, root);
+ if(!dir)
+ return -1;
+
+ pctrl->proc_entry = dir;
+
+ i2o_proc_create_entries(pctrl, generic_iop_entries, dir);
+
+ for(dev = pctrl->devices; dev; dev = dev->next)
+ {
+ sprintf(buff, "%0#5x", dev->id);
+
+ dir1 = create_proc_entry(buff, S_IFDIR, dir);
+ dev->proc_entry = dir1;
+
+ if(!dir1)
+ printk(KERN_INFO "i2o_proc: Could not allocate proc dir\n");
+
+ i2o_proc_create_entries(dev, generic_dev_entries, dir1);
+
+ switch(dev->class)
+ {
+ case I2O_CLASS_SCSI_PERIPHERAL:
+ case I2O_CLASS_RANDOM_BLOCK_STORAGE:
+ i2o_proc_create_entries(dev, rbs_dev_entries, dir1);
+ break;
+ case I2O_CLASS_LAN:
+ i2o_proc_create_entries(dev, lan_entries, dir1);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void i2o_proc_remove_controller(struct i2o_controller *pctrl,
+ struct proc_dir_entry *parent)
+{
+ char buff[10];
+
+ sprintf(buff, "iop%d", pctrl->unit);
+
+ i2o_proc_remove_entries(generic_iop_entries, pctrl->proc_entry);
+
+ remove_proc_entry(buff, parent);
+
+ pctrl->proc_entry = NULL;
+}
+
+static int create_i2o_procfs(void)
+{
+ struct i2o_controller *pctrl = NULL;
+ int i;
+
+ i2o_proc_dir_root = create_proc_entry("i2o", S_IFDIR, 0);
+ if(!i2o_proc_dir_root)
+ return -1;
+
+ for(i = 0; i < MAX_I2O_CONTROLLERS; i++)
+ {
+ pctrl = i2o_find_controller(i);
+ if(pctrl)
+ i2o_proc_add_controller(pctrl, i2o_proc_dir_root);
+ };
+
+ return 0;
+}
+
+static int destroy_i2o_procfs(void)
+{
+ struct i2o_controller *pctrl = NULL;
+ int i;
+
+ if(!i2o_find_controller(0))
+ return -1;
+
+ for(i = 0; i < MAX_I2O_CONTROLLERS; i++)
+ {
+ pctrl = i2o_find_controller(i);
+ if(pctrl)
+ i2o_proc_remove_controller(pctrl, i2o_proc_dir_root);
+ };
+
+ remove_proc_entry("i2o", 0);
+ return 0;
+}
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_DESCRIPTION("I2O procfs Handler");
+
+int init_module(void)
+{
+ if(create_i2o_procfs())
+ return -EBUSY;
+
+ if (i2o_install_handler(&i2o_proc_handler) < 0)
+ {
+ printk(KERN_ERR "i2o_proc: Unable to install PROC handler.\n");
+ return 0;
+ }
+
+ proc_context = i2o_proc_handler.context;
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ destroy_i2o_procfs();
+ i2o_remove_handler(&i2o_proc_handler);
+}
+#endif
diff --git a/drivers/i2o/i2o_proc.h b/drivers/i2o/i2o_proc.h
new file mode 100644
index 000000000..fd659c396
--- /dev/null
+++ b/drivers/i2o/i2o_proc.h
@@ -0,0 +1,141 @@
+#ifndef i2oproc_h
+#define i2oproc_h
+
+/*
+ * Fixme: make this dependent on architecture
+ * The official header files to this already...but we can't use them
+ */
+#define I2O_64BIT_CONTEXT 0
+
+typedef struct _i2o_msg {
+ u8 ver_offset;
+ u8 msg_flags;
+ u16 msg_size;
+ u32 target_addr:12;
+ u32 initiator_addr:12;
+ u32 function:8;
+ u32 init_context; /* FIXME: 64-bit support! */
+} i2o_msg, *pi2o_msg;
+
+typedef struct _i2o_reply_message {
+ i2o_msg msg_frame;
+ u32 tctx; /* FIXME: 64-bit */
+ u16 detailed_status_code;
+ u8 reserved;
+ u8 req_status;
+} i2o_reply_msg, *pi2o_reply_msg;
+
+typedef struct _i2o_mult_reply_message {
+ i2o_msg msg_frame;
+ u32 tctx; /* FIXME: 64-bit */
+ u16 detailed_status_code;
+ u8 reserved;
+ u8 req_status;
+} i2o_mult_reply_msg, *pi2o_mult_reply_msg;
+
+/**************************************************************************
+ * HRT related constants and structures
+ **************************************************************************/
+#define I2O_BUS_LOCAL 0
+#define I2O_BUS_ISA 1
+#define I2O_BUS_EISA 2
+#define I2O_BUS_MCA 3
+#define I2O_BUS_PCI 4
+#define I2O_BUS_PCMCIA 5
+#define I2O_BUS_NUBUS 6
+#define I2O_BUS_CARDBUS 7
+#define I2O_BUS_UNKNOWN 0x80
+
+typedef struct _i2o_pci_bus {
+ u8 PciFunctionNumber;
+ u8 PciDeviceNumber;
+ u8 PciBusNumber;
+ u8 reserved;
+ u16 PciVendorID;
+ u16 PciDeviceID;
+} i2o_pci_bus, *pi2o_pci_bus;
+
+typedef struct _i2o_local_bus {
+ u16 LbBaseIOPort;
+ u16 reserved;
+ u32 LbBaseMemoryAddress;
+} i2o_local_bus, *pi2o_local_bus;
+
+typedef struct _i2o_isa_bus {
+ u16 IsaBaseIOPort;
+ u8 CSN;
+ u8 reserved;
+ u32 IsaBaseMemoryAddress;
+} i2o_isa_bus, *pi2o_isa_bus;
+
+/* I2O_EISA_BUS_INFO */
+typedef struct _i2o_eisa_bus_info {
+ u16 EisaBaseIOPort;
+ u8 reserved;
+ u8 EisaSlotNumber;
+ u32 EisaBaseMemoryAddress;
+} i2o_eisa_bus, *pi2o_eisa_bus;
+
+typedef struct _i2o_mca_bus {
+ u16 McaBaseIOPort;
+ u8 reserved;
+ u8 McaSlotNumber;
+ u32 McaBaseMemoryAddress;
+} i2o_mca_bus, *pi2o_mca_bus;
+
+typedef struct _i2o_other_bus {
+ u16 BaseIOPort;
+ u16 reserved;
+ u32 BaseMemoryAddress;
+} i2o_other_bus, *pi2o_other_bus;
+
+
+typedef struct _i2o_hrt_entry {
+ u32 adapter_id;
+ u32 parent_tid:12;
+ u32 state:4;
+ u32 bus_num:8;
+ u32 bus_type:8;
+ union {
+ i2o_pci_bus pci_bus;
+ i2o_local_bus local_bus;
+ i2o_isa_bus isa_bus;
+ i2o_eisa_bus eisa_bus;
+ i2o_mca_bus mca_bus;
+ i2o_other_bus other_bus;
+ } bus;
+} i2o_hrt_entry, *pi2o_hrt_entry;
+
+typedef struct _i2o_hrt {
+ u16 num_entries;
+ u8 entry_len;
+ u8 hrt_version;
+ u32 change_ind;
+ i2o_hrt_entry hrt_entry[1];
+} i2o_hrt, *pi2o_hrt;
+
+typedef struct _i2o_lct_entry {
+ u32 entry_size:16;
+ u32 tid:12;
+ u32 reserved:4;
+ u32 change_ind;
+ u32 device_flags;
+ u32 class_id;
+ u32 sub_class;
+ u32 user_tid:12;
+ u32 parent_tid:12;
+ u32 bios_info:8;
+ u8 identity_tag[8];
+ u32 event_capabilities;
+} i2o_lct_entry, *pi2o_lct_entry;
+
+typedef struct _i2o_lct {
+ u32 table_size:16;
+ u32 boot_tid:12;
+ u32 lct_ver:4;
+ u32 iop_flags;
+ u32 current_change_ind;
+ i2o_lct_entry lct_entry[1];
+} i2o_lct, *pi2o_lct;
+
+#endif /* i2oproc_h */
diff --git a/drivers/i2o/i2o_scsi.c b/drivers/i2o/i2o_scsi.c
new file mode 100644
index 000000000..505e3c22d
--- /dev/null
+++ b/drivers/i2o/i2o_scsi.c
@@ -0,0 +1,871 @@
+/*
+ * 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.
+ *
+ * Complications for I2O scsi
+ *
+ * o Each (bus,lun) is a logical device in I2O. We keep a map
+ * table. We spoof failed selection for unmapped units
+ * o Request sense buffers can come back for free.
+ * o Scatter gather is a bit dynamic. We have to investigate at
+ * setup time.
+ * o Some of our resources are dynamically shared. The i2o core
+ * needs a message reservation protocol to avoid swap v net
+ * deadlocking. We need to back off queue requests.
+ *
+ * In general the firmware wants to help. Where its help isn't performance
+ * useful we just ignore the aid. Its not worth the code in truth.
+ *
+ * Fixes:
+ * Steve Ralston : Scatter gather now works
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include <linux/blk.h>
+#include <linux/version.h>
+#include <linux/i2o.h>
+#include "../scsi/scsi.h"
+#include "../scsi/hosts.h"
+#include "../scsi/sd.h"
+#include "i2o_scsi.h"
+
+#define VERSION_STRING "Version 0.0.1"
+
+#define dprintk(x)
+
+#define MAXHOSTS 32
+
+struct proc_dir_entry proc_scsi_i2o_scsi = {
+ PROC_SCSI_I2O, 8, "i2o_scsi", S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+struct i2o_scsi_host
+{
+ struct i2o_controller *controller;
+ s16 task[16][8]; /* Allow 16 devices for now */
+ unsigned long tagclock[16][8]; /* Tag clock for queueing */
+ s16 bus_task; /* The adapter TID */
+};
+
+static int scsi_context;
+static int lun_done;
+static int i2o_scsi_hosts;
+
+static u32 *retry[32];
+static struct i2o_controller *retry_ctrl[32];
+static struct timer_list retry_timer;
+static int retry_ct = 0;
+
+static atomic_t queue_depth;
+
+/*
+ * SG Chain buffer support...
+ */
+#define SG_MAX_FRAGS 64
+
+/*
+ * FIXME: we should allocate one of these per bus we find as we
+ * locate them not in a lump at boot.
+ */
+
+typedef struct _chain_buf
+{
+ u32 sg_flags_cnt[SG_MAX_FRAGS];
+ u32 sg_buf[SG_MAX_FRAGS];
+} chain_buf;
+
+#define SG_CHAIN_BUF_SZ sizeof(chain_buf)
+
+#define SG_MAX_BUFS (i2o_num_controllers * I2O_SCSI_CAN_QUEUE)
+#define SG_CHAIN_POOL_SZ (SG_MAX_BUFS * SG_CHAIN_BUF_SZ)
+
+static int max_sg_len = 0;
+static chain_buf *sg_chain_pool = NULL;
+static int sg_chain_tag = 0;
+static int sg_max_frags = SG_MAX_FRAGS;
+
+/*
+ * Retry congested frames. This actually needs pushing down into
+ * i2o core. We should only bother the OSM with this when we can't
+ * queue and retry the frame. Or perhaps we should call the OSM
+ * and its default handler should be this in the core, and this
+ * call a 2nd "I give up" handler in the OSM ?
+ */
+
+static void i2o_retry_run(unsigned long f)
+{
+ int i;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ for(i=0;i<retry_ct;i++)
+ i2o_post_message(retry_ctrl[i], virt_to_bus(retry[i]));
+ retry_ct=0;
+
+ restore_flags(flags);
+}
+
+static void flush_pending(void)
+{
+ int i;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ for(i=0;i<retry_ct;i++)
+ {
+ retry[i][0]&=~0xFFFFFF;
+ retry[i][0]|=I2O_CMD_UTIL_NOP<<24;
+ i2o_post_message(retry_ctrl[i],virt_to_bus(retry[i]));
+ }
+ retry_ct=0;
+
+ restore_flags(flags);
+}
+
+static void i2o_scsi_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *msg)
+{
+ Scsi_Cmnd *current_command;
+ u32 *m = (u32 *)msg;
+ u8 as,ds,st;
+
+ if(m[0] & (1<<13))
+ {
+ printk("IOP fail.\n");
+ printk("From %d To %d Cmd %d.\n",
+ (m[1]>>12)&0xFFF,
+ m[1]&0xFFF,
+ m[1]>>24);
+ printk("Failure Code %d.\n", m[4]>>24);
+ if(m[4]&(1<<16))
+ printk("Format error.\n");
+ if(m[4]&(1<<17))
+ printk("Path error.\n");
+ if(m[4]&(1<<18))
+ printk("Path State.\n");
+ if(m[4]&(1<<18))
+ printk("Congestion.\n");
+
+ m=(u32 *)bus_to_virt(m[7]);
+ printk("Failing message is %p.\n", m);
+
+ if((m[4]&(1<<18)) && retry_ct < 32)
+ {
+ retry_ctrl[retry_ct]=c;
+ retry[retry_ct]=m;
+ if(!retry_ct++)
+ {
+ retry_timer.expires=jiffies+1;
+ add_timer(&retry_timer);
+ }
+ }
+ else
+ {
+ /* Create a scsi error for this */
+ current_command = (Scsi_Cmnd *)m[3];
+ printk("Aborted %ld\n", current_command->serial_number);
+
+ spin_lock_irq(&io_request_lock);
+ current_command->result = DID_ERROR << 16;
+ current_command->scsi_done(current_command);
+ spin_unlock_irq(&io_request_lock);
+
+ /* Now flush the message by making it a NOP */
+ m[0]&=0x00FFFFFF;
+ m[0]|=(I2O_CMD_UTIL_NOP)<<24;
+ i2o_post_message(c,virt_to_bus(m));
+ }
+ return;
+ }
+
+
+ /* Low byte is the adapter status, next is the device */
+ as=(u8)m[4];
+ ds=(u8)(m[4]>>8);
+ st=(u8)(m[4]>>24);
+
+ dprintk(("i2o got a scsi reply %08X: ", m[0]));
+ dprintk(("m[2]=%08X: ", m[2]));
+ dprintk(("m[4]=%08X\n", m[4]));
+
+ if(m[2]&0x80000000)
+ {
+ if(m[2]&0x40000000)
+ {
+ dprintk(("Event.\n"));
+ lun_done=1;
+ return;
+ }
+ printk(KERN_ERR "i2o_scsi: bus reset reply.\n");
+ return;
+ }
+
+ current_command = (Scsi_Cmnd *)m[3];
+
+ /*
+ * Is this a control request coming back - eg an abort ?
+ */
+
+ if(current_command==NULL)
+ {
+ if(st)
+ dprintk(("SCSI abort: %08X", m[4]));
+ dprintk(("SCSI abort completed.\n"));
+ return;
+ }
+
+ dprintk(("Completed %ld\n", current_command->serial_number));
+
+ atomic_dec(&queue_depth);
+
+ if(st == 0x06)
+ {
+ if(m[5] < current_command->underflow)
+ {
+ int i;
+ printk(KERN_ERR "SCSI: underflow 0x%08X 0x%08X\n",
+ m[5], current_command->underflow);
+ printk("Cmd: ");
+ for(i=0;i<15;i++)
+ printk("%02X ", current_command->cmnd[i]);
+ printk(".\n");
+ }
+ else st=0;
+ }
+
+ if(st)
+ {
+ /* An error has occured */
+
+ dprintk((KERN_DEBUG "SCSI error %08X", m[4]));
+
+ if (ds == 0x0E)
+ /* SCSI Reset */
+ current_command->result = DID_RESET << 16;
+ else if (ds == 0x0F)
+ current_command->result = DID_PARITY << 16;
+ else
+ current_command->result = DID_ERROR << 16;
+ }
+ else
+ /*
+ * It worked maybe ?
+ */
+ current_command->result = DID_OK << 16 | ds;
+ spin_lock(&io_request_lock);
+ current_command->scsi_done(current_command);
+ spin_unlock(&io_request_lock);
+ return;
+}
+
+struct i2o_handler i2o_scsi_handler=
+{
+ i2o_scsi_reply,
+ "I2O SCSI OSM",
+ 0
+};
+
+static int i2o_find_lun(struct i2o_controller *c, struct i2o_device *d, int *target, int *lun)
+{
+ u8 reply[8];
+
+ if(i2o_query_scalar(c, d->id, scsi_context|0x40000000,
+ 0, 3, reply, 4, &lun_done)<0)
+ return -1;
+
+ *target=reply[0];
+
+ if(i2o_query_scalar(c, d->id, scsi_context|0x40000000,
+ 0, 4, reply, 8, &lun_done)<0)
+ return -1;
+
+ *lun=reply[1];
+
+ dprintk(("SCSI (%d,%d)\n", *target, *lun));
+ return 0;
+}
+
+static void i2o_scsi_init(struct i2o_controller *c, struct i2o_device *d, struct Scsi_Host *shpnt)
+{
+ struct i2o_device *unit;
+ struct i2o_scsi_host *h =(struct i2o_scsi_host *)shpnt->hostdata;
+ int lun;
+ int target;
+
+ h->controller=c;
+ h->bus_task=d->id;
+
+ for(target=0;target<16;target++)
+ for(lun=0;lun<8;lun++)
+ h->task[target][lun] = -1;
+
+ for(unit=c->devices;unit!=NULL;unit=unit->next)
+ {
+ dprintk(("Class %03X, parent %d, want %d.\n",
+ unit->class, unit->parent, d->id));
+
+ /* Only look at scsi and fc devices */
+ if ( (unit->class != I2O_CLASS_SCSI_PERIPHERAL)
+ && (unit->class != I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL)
+ )
+ continue;
+
+ /* On our bus ? */
+ dprintk(("Found a disk.\n"));
+ if ( (unit->parent == d->id)
+ || (unit->parent == d->parent)
+ )
+ {
+ u16 limit;
+ dprintk(("Its ours.\n"));
+ if(i2o_find_lun(c, unit, &target, &lun)==-1)
+ {
+ printk(KERN_ERR "i2o_scsi: Unable to get lun for tid %d.\n", d->id);
+ continue;
+ }
+ dprintk(("Found disk %d %d.\n", target, lun));
+ h->task[target][lun]=unit->id;
+ h->tagclock[target][lun]=jiffies;
+
+ /* Get the max fragments/request */
+ i2o_query_scalar(c, d->id, scsi_context|0x40000000,
+ 0xF103, 3, &limit, 2, &lun_done);
+
+ /* sanity */
+ if ( limit == 0 )
+ {
+ printk(KERN_WARNING "i2o_scsi: Ignoring unreasonable SG limit of 0 from IOP!\n");
+ limit = 1;
+ }
+
+ shpnt->sg_tablesize = limit;
+
+ dprintk(("i2o_scsi: set scatter-gather to %d.\n",
+ shpnt->sg_tablesize));
+ }
+ }
+}
+
+int i2o_scsi_detect(Scsi_Host_Template * tpnt)
+{
+ unsigned long flags;
+ struct Scsi_Host *shpnt = NULL;
+ int i;
+ int count;
+
+ printk("i2o_scsi.c: %s\n", VERSION_STRING);
+
+ if(i2o_install_handler(&i2o_scsi_handler)<0)
+ {
+ printk(KERN_ERR "i2o_scsi: Unable to install OSM handler.\n");
+ return 0;
+ }
+ scsi_context = i2o_scsi_handler.context;
+
+ if((sg_chain_pool = kmalloc(SG_CHAIN_POOL_SZ, GFP_KERNEL)) == NULL)
+ {
+ printk("i2o_scsi: Unable to alloc %d byte SG chain buffer pool.\n", SG_CHAIN_POOL_SZ);
+ printk("i2o_scsi: SG chaining DISABLED!\n");
+ sg_max_frags = 11;
+ }
+ else
+ {
+ printk(" chain_pool: %d bytes @ %p\n", SG_CHAIN_POOL_SZ, sg_chain_pool);
+ printk(" (%d byte buffers X %d can_queue X %d i2o controllers)\n",
+ SG_CHAIN_BUF_SZ, I2O_SCSI_CAN_QUEUE, i2o_num_controllers);
+ sg_max_frags = SG_MAX_FRAGS; // 64
+ }
+
+ init_timer(&retry_timer);
+ retry_timer.data = 0UL;
+ retry_timer.function = i2o_retry_run;
+
+// printk("SCSI OSM at %d.\n", scsi_context);
+
+ for (count = 0, i = 0; i < MAX_I2O_CONTROLLERS; i++)
+ {
+ struct i2o_controller *c=i2o_find_controller(i);
+ struct i2o_device *d;
+ /*
+ * This controller doesn't exist.
+ */
+
+ if(c==NULL)
+ continue;
+
+ /*
+ * Fixme - we need some altered device locking. This
+ * is racing with device addition in theory. Easy to fix.
+ */
+
+ for(d=c->devices;d!=NULL;d=d->next)
+ {
+ /*
+ * bus_adapter, SCSI (obsolete), or FibreChannel busses only
+ */
+ if( (d->class!=I2O_CLASS_BUS_ADAPTER_PORT) // bus_adapter
+ && (d->class!=I2O_CLASS_FIBRE_CHANNEL_PORT) // FC_PORT
+ )
+ continue;
+
+// printk("Found a controller.\n");
+ shpnt = scsi_register(tpnt, sizeof(struct i2o_scsi_host));
+ save_flags(flags);
+ cli();
+ shpnt->unique_id = (u32)d;
+ shpnt->io_port = 0;
+ shpnt->n_io_port = 0;
+ shpnt->irq = 0;
+ shpnt->this_id = /* Good question */15;
+ restore_flags(flags);
+// printk("Scanning I2O port %d.\n", d->id);
+ i2o_scsi_init(c, d, shpnt);
+ count++;
+ }
+ }
+ i2o_scsi_hosts = count;
+
+ if(count==0)
+ {
+ if(sg_chain_pool!=NULL)
+ {
+ kfree(sg_chain_pool);
+ sg_chain_pool = NULL;
+ }
+ flush_pending();
+ del_timer(&retry_timer);
+ i2o_remove_handler(&i2o_scsi_handler);
+ }
+
+ return count;
+}
+
+int i2o_scsi_release(struct Scsi_Host *host)
+{
+ if(--i2o_scsi_hosts==0)
+ {
+ if(sg_chain_pool!=NULL)
+ {
+ kfree(sg_chain_pool);
+ sg_chain_pool = NULL;
+ }
+ flush_pending();
+ del_timer(&retry_timer);
+ i2o_remove_handler(&i2o_scsi_handler);
+ }
+ return 0;
+}
+
+
+const char *i2o_scsi_info(struct Scsi_Host *SChost)
+{
+ struct i2o_scsi_host *hostdata;
+
+ hostdata = (struct i2o_scsi_host *)SChost->hostdata;
+
+ return(&hostdata->controller->name[0]);
+}
+
+
+/*
+ * From the wd93 driver:
+ * Returns true if there will be a DATA_OUT phase with this command,
+ * false otherwise.
+ * (Thanks to Joerg Dorchain for the research and suggestion.)
+ *
+ */
+static int is_dir_out(Scsi_Cmnd *cmd)
+{
+ switch (cmd->cmnd[0])
+ {
+ case WRITE_6: case WRITE_10: case WRITE_12:
+ case WRITE_LONG: case WRITE_SAME: case WRITE_BUFFER:
+ case WRITE_VERIFY: case WRITE_VERIFY_12:
+ case COMPARE: case COPY: case COPY_VERIFY:
+ case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
+ case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12:
+ case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE:
+ case MODE_SELECT: case MODE_SELECT_10: case LOG_SELECT:
+ case SEND_DIAGNOSTIC: case CHANGE_DEFINITION: case UPDATE_BLOCK:
+ case SET_WINDOW: case MEDIUM_SCAN: case SEND_VOLUME_TAG:
+ case 0xea:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int i2o_scsi_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
+{
+ int i;
+ int tid;
+ struct i2o_controller *c;
+ Scsi_Cmnd *current_command;
+ struct Scsi_Host *host;
+ struct i2o_scsi_host *hostdata;
+ u32 *msg, *mptr;
+ u32 m;
+ u32 *lenptr;
+ int direction;
+ int scsidir;
+ u32 len;
+
+ static int max_qd = 1;
+
+ /*
+ * The scsi layer should be handling this stuff
+ */
+
+ if(is_dir_out(SCpnt))
+ {
+ direction=0x04000000;
+ scsidir=0x80000000;
+ }
+ else
+ {
+ scsidir=0x40000000;
+ direction=0x00000000;
+ }
+
+ /*
+ * Do the incoming paperwork
+ */
+
+ host = SCpnt->host;
+ hostdata = (struct i2o_scsi_host *)host->hostdata;
+ SCpnt->scsi_done = done;
+
+ if(SCpnt->target > 15)
+ {
+ printk(KERN_ERR "i2o_scsi: Wild target %d.\n", SCpnt->target);
+ return -1;
+ }
+
+ tid = hostdata->task[SCpnt->target][SCpnt->lun];
+
+ dprintk(("qcmd: Tid = %d\n", tid));
+
+ current_command = SCpnt; /* set current command */
+ current_command->scsi_done = done; /* set ptr to done function */
+
+ /* We don't have such a device. Pretend we did the command
+ and that selection timed out */
+
+ if(tid == -1)
+ {
+ SCpnt->result = DID_NO_CONNECT << 16;
+ done(SCpnt);
+ return 0;
+ }
+
+ dprintk(("Real scsi messages.\n"));
+
+ c = hostdata->controller;
+
+ /*
+ * Obtain an I2O message. Right now we _have_ to obtain one
+ * until the scsi layer stuff is cleaned up.
+ */
+
+ do
+ {
+ mb();
+ m = I2O_POST_READ32(c);
+ }
+ while(m==0xFFFFFFFF);
+ msg = bus_to_virt(c->mem_offset + m);
+
+ /*
+ * Put together a scsi execscb message
+ */
+
+ msg[1] = I2O_CMD_SCSI_EXEC<<24|HOST_TID<<12|tid;
+ msg[2] = scsi_context; /* So the I2O layer passes to us */
+ /* Sorry 64bit folks. FIXME */
+ msg[3] = (u32)SCpnt; /* We want the SCSI control block back */
+ /* Direction, disconnect ok, no tagging (yet) */
+ msg[4] = scsidir|(1<<29)|SCpnt->cmd_len;
+
+ /*
+ * Attach tags to the devices
+ */
+ if(SCpnt->device->tagged_supported)
+ {
+ /*
+ * Some drives are too stupid to handle fairness issues
+ * with tagged queueing. We throw in the odd ordered
+ * tag to stop them starving themselves.
+ */
+ if((jiffies - hostdata->tagclock[SCpnt->target][SCpnt->lun]) > (5*HZ))
+ {
+ msg[4]|=(1<<23)|(1<<24);
+ hostdata->tagclock[SCpnt->target][SCpnt->lun]=jiffies;
+ }
+ else switch(SCpnt->tag)
+ {
+ case SIMPLE_QUEUE_TAG:
+ msg[4]|=(1<<23);
+ break;
+ case HEAD_OF_QUEUE_TAG:
+ msg[4]|=(1<<24);
+ break;
+ case ORDERED_QUEUE_TAG:
+ msg[4]|=(1<<23)|(1<<24);
+ break;
+ default:
+ msg[4]|=(1<<23);
+ }
+ }
+
+ mptr=msg+5;
+
+ /*
+ * Write SCSI command into the message - always 16 byte block
+ */
+
+ memcpy(mptr, SCpnt->cmnd, 16);
+ mptr+=4;
+ lenptr=mptr++; /* Remember me - fill in when we know */
+
+
+ /*
+ * Now fill in the SGList and command
+ *
+ * FIXME: we need to set the sglist limits according to the
+ * message size of the I2O controller. We might only have room
+ * for 6 or so worst case
+ */
+
+ if(SCpnt->use_sg)
+ {
+ struct scatterlist *sg = (struct scatterlist *)SCpnt->request_buffer;
+
+ if((sg_max_frags > 11) && (SCpnt->use_sg > 11))
+ {
+ /*
+ * Need to chain!
+ */
+ SCpnt->host_scribble = (void*)(sg_chain_pool + sg_chain_tag);
+ *mptr++=direction|0xB0000000|(SCpnt->use_sg*2*4);
+ *mptr=virt_to_bus(SCpnt->host_scribble);
+ mptr = (u32*)SCpnt->host_scribble;
+ if (SCpnt->use_sg > max_sg_len)
+ {
+ max_sg_len = SCpnt->use_sg;
+ printk("i2o_scsi: Chain SG! SCpnt=%p, SG_FragCnt=%d, SG_idx=%d\n",
+ SCpnt, SCpnt->use_sg, (chain_buf*)SCpnt->host_scribble-sg_chain_pool);
+ }
+ if ( ++sg_chain_tag == SG_MAX_BUFS )
+ sg_chain_tag = 0;
+ }
+
+ len = 0;
+
+ for(i = 0 ; i < SCpnt->use_sg; i++)
+ {
+ *mptr++=direction|0x10000000|sg->length;
+ len+=sg->length;
+ *mptr++=virt_to_bus(sg->address);
+ sg++;
+ }
+ mptr[-2]|=0xC0000000; /* End of List and block */
+ *lenptr=len;
+ if(len != SCpnt->underflow)
+ printk("Cmd len %08X Cmd underflow %08X\n",
+ len, SCpnt->underflow);
+ }
+ else
+ {
+ dprintk(("non sg for %p, %d\n", SCpnt->request_buffer,
+ SCpnt->request_bufflen));
+ *mptr++=0xD0000000|direction|SCpnt->request_bufflen;
+ *mptr++=virt_to_bus(SCpnt->request_buffer);
+ *lenptr = len = SCpnt->request_bufflen;
+ /* No transfer ? - fix up the request */
+ if(len == 0)
+ msg[4]&=~0xC0000000;
+ }
+
+ /*
+ * Stick the headers on
+ */
+
+ msg[0] = (mptr-msg)<<16 | SGL_OFFSET_10;
+
+ /* Queue the message */
+ i2o_post_message(c,m);
+
+ atomic_inc(&queue_depth);
+
+ if(atomic_read(&queue_depth)> max_qd)
+ {
+ max_qd=atomic_read(&queue_depth);
+ printk("Queue depth now %d.\n", max_qd);
+ }
+
+ mb();
+ dprintk(("Issued %ld\n", current_command->serial_number));
+
+ return 0;
+}
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+{
+ SCpnt->SCp.Status++;
+}
+
+int i2o_scsi_command(Scsi_Cmnd * SCpnt)
+{
+ i2o_scsi_queuecommand(SCpnt, internal_done);
+ SCpnt->SCp.Status = 0;
+ while (!SCpnt->SCp.Status)
+ barrier();
+ return SCpnt->result;
+}
+
+int i2o_scsi_abort(Scsi_Cmnd * SCpnt)
+{
+ struct i2o_controller *c;
+ struct Scsi_Host *host;
+ struct i2o_scsi_host *hostdata;
+ u32 *msg;
+ u32 m;
+ int tid;
+
+ printk("i2o_scsi_abort\n");
+
+ host = SCpnt->host;
+ hostdata = (struct i2o_scsi_host *)host->hostdata;
+ tid = hostdata->task[SCpnt->target][SCpnt->lun];
+ if(tid==-1)
+ {
+ printk(KERN_ERR "impossible command to abort.\n");
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+ c = hostdata->controller;
+
+ /*
+ * Obtain an I2O message. Right now we _have_ to obtain one
+ * until the scsi layer stuff is cleaned up.
+ */
+
+ do
+ {
+ mb();
+ m = I2O_POST_READ32(c);
+ }
+ while(m==0xFFFFFFFF);
+ msg = bus_to_virt(c->mem_offset + m);
+
+ msg[0] = FIVE_WORD_MSG_SIZE;
+ msg[1] = I2O_CMD_SCSI_ABORT<<24|HOST_TID<<12|tid;
+ msg[2] = scsi_context;
+ msg[3] = 0; /* Not needed for an abort */
+ msg[4] = (u32)SCpnt;
+ wmb();
+ i2o_post_message(c,m);
+ wmb();
+// SCpnt->result = DID_RESET << 16;
+// SCpnt->scsi_done(SCpnt);
+ return SCSI_ABORT_PENDING;
+}
+
+int i2o_scsi_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags)
+{
+ int tid;
+ struct i2o_controller *c;
+ struct Scsi_Host *host;
+ struct i2o_scsi_host *hostdata;
+ u32 m;
+ u32 *msg;
+
+ printk("i2o_scsi_reset\n");
+
+ /*
+ * Find the TID for the bus
+ */
+
+ host = SCpnt->host;
+ hostdata = (struct i2o_scsi_host *)host->hostdata;
+ tid = hostdata->bus_task;
+ c = hostdata->controller;
+
+ /*
+ * Now send a SCSI reset request. Any remaining commands
+ * will be aborted by the IOP. We need to catch the reply
+ * possibly ?
+ */
+
+ m = I2O_POST_READ32(c);
+
+ /*
+ * No free messages, try again next time - no big deal
+ */
+
+ if(m == 0xFFFFFFFF)
+ return SCSI_RESET_PUNT;
+
+ msg = bus_to_virt(c->mem_offset + m);
+ msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0;
+ msg[1] = I2O_CMD_SCSI_BUSRESET<<24|HOST_TID<<12|tid;
+ msg[2] = scsi_context|0x80000000;
+ /* We use the top bit to split controller and unit transactions */
+ /* Now store unit,tid so we can tie the completion back to a specific device */
+ msg[3] = c->unit << 16 | tid;
+ i2o_post_message(c,m);
+ return SCSI_RESET_PENDING;
+}
+
+/*
+ * This is anyones guess quite frankly.
+ */
+
+int i2o_scsi_bios_param(Disk * disk, kdev_t dev, int *ip)
+{
+ int size;
+
+ size = disk->capacity;
+ ip[0] = 64; /* heads */
+ ip[1] = 32; /* sectors */
+ if ((ip[2] = size >> 11) > 1024) { /* cylinders, test for big disk */
+ ip[0] = 255; /* heads */
+ ip[1] = 63; /* sectors */
+ ip[2] = size / (255 * 63); /* cylinders */
+ }
+ return 0;
+}
+
+/* Loadable module support */
+#ifdef MODULE
+
+MODULE_AUTHOR("Red Hat Software");
+
+Scsi_Host_Template driver_template = I2OSCSI;
+
+#include "../scsi/scsi_module.c"
+#endif
diff --git a/drivers/i2o/i2o_scsi.h b/drivers/i2o/i2o_scsi.h
new file mode 100644
index 000000000..c6c88bc5e
--- /dev/null
+++ b/drivers/i2o/i2o_scsi.h
@@ -0,0 +1,48 @@
+#ifndef _I2O_SCSI_H
+#define _I2O_SCSI_H
+
+#if !defined(LINUX_VERSION_CODE)
+#include <linux/version.h>
+#endif
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+#define I2O_SCSI_ID 15
+#define I2O_SCSI_CAN_QUEUE 8
+#define I2O_SCSI_CMD_PER_LUN 6
+
+extern struct proc_dir_entry proc_scsi_i2o_scsi;
+
+extern int i2o_scsi_detect(Scsi_Host_Template *);
+extern const char *i2o_scsi_info(struct Scsi_Host *);
+extern int i2o_scsi_command(Scsi_Cmnd *);
+extern int i2o_scsi_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+extern int i2o_scsi_abort(Scsi_Cmnd *);
+extern int i2o_scsi_reset(Scsi_Cmnd *, unsigned int);
+extern int i2o_scsi_bios_param(Disk *, kdev_t, int *);
+extern void i2o_scsi_setup(char *str, int *ints);
+
+#define I2OSCSI { \
+ next: NULL, \
+ proc_dir: &proc_scsi_i2o_scsi, \
+ name: "I2O SCSI Layer", \
+ detect: i2o_scsi_detect, \
+ release: i2o_scsi_release, \
+ info: i2o_scsi_info, \
+ command: i2o_scsi_command, \
+ queuecommand: i2o_scsi_queuecommand, \
+ abort: i2o_scsi_abort, \
+ reset: i2o_scsi_reset, \
+ bios_param: i2o_scsi_bios_param, \
+ can_queue: I2O_SCSI_CAN_QUEUE, \
+ this_id: I2O_SCSI_ID, \
+ sg_tablesize: 8, \
+ cmd_per_lun: I2O_SCSI_CMD_PER_LUN, \
+ unchecked_isa_dma: 0, \
+ use_clustering: ENABLE_CLUSTERING \
+ }
+
+#endif
diff --git a/drivers/isdn/eicon/Makefile b/drivers/isdn/eicon/Makefile
new file mode 100644
index 000000000..306aac0e8
--- /dev/null
+++ b/drivers/isdn/eicon/Makefile
@@ -0,0 +1,13 @@
+L_OBJS :=
+M_OBJS :=
+O_OBJS := eicon_mod.o eicon_isa.o eicon_pci.o eicon_idi.o eicon_io.o
+
+O_TARGET :=
+ifeq ($(CONFIG_ISDN_DRV_EICON),y)
+ O_TARGET += eicon.o
+else
+ O_TARGET += eicon.o
+ M_OBJS = eicon.o
+endif
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/isdn/eicon/eicon.h b/drivers/isdn/eicon/eicon.h
new file mode 100644
index 000000000..9552d2b54
--- /dev/null
+++ b/drivers/isdn/eicon/eicon.h
@@ -0,0 +1,528 @@
+/* $Id: eicon.h,v 1.5 1999/03/29 11:19:41 armin Exp $
+ *
+ * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ *
+ * Copyright 1998 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon.h,v $
+ * Revision 1.5 1999/03/29 11:19:41 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.4 1999/03/02 12:37:42 armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.3 1999/01/24 20:14:07 armin
+ * Changed and added debug stuff.
+ * Better data sending. (still problems with tty's flip buffer)
+ *
+ * Revision 1.2 1999/01/10 18:46:04 armin
+ * Bug with wrong values in HLC fixed.
+ * Bytes to send are counted and limited now.
+ *
+ * Revision 1.1 1999/01/01 18:09:41 armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+
+#ifndef eicon_h
+#define eicon_h
+
+#define EICON_IOCTL_SETMMIO 0
+#define EICON_IOCTL_GETMMIO 1
+#define EICON_IOCTL_SETIRQ 2
+#define EICON_IOCTL_GETIRQ 3
+#define EICON_IOCTL_LOADBOOT 4
+#define EICON_IOCTL_ADDCARD 5
+#define EICON_IOCTL_GETTYPE 6
+#define EICON_IOCTL_LOADPCI 7
+#define EICON_IOCTL_LOADISA 8
+#define EICON_IOCTL_GETVER 9
+
+#define EICON_IOCTL_MANIF 90
+
+#define EICON_IOCTL_FREEIT 97
+#define EICON_IOCTL_TEST 98
+#define EICON_IOCTL_DEBUGVAR 99
+
+/* Bus types */
+#define EICON_BUS_ISA 1
+#define EICON_BUS_MCA 2
+#define EICON_BUS_PCI 3
+
+/* Constants for describing Card-Type */
+#define EICON_CTYPE_S 0
+#define EICON_CTYPE_SX 1
+#define EICON_CTYPE_SCOM 2
+#define EICON_CTYPE_QUADRO 3
+#define EICON_CTYPE_S2M 4
+#define EICON_CTYPE_MAESTRA 5
+#define EICON_CTYPE_MAESTRAQ 6
+#define EICON_CTYPE_MAESTRAQ_U 7
+#define EICON_CTYPE_MAESTRAP 8
+#define EICON_CTYPE_ISABRI 0x10
+#define EICON_CTYPE_ISAPRI 0x20
+#define EICON_CTYPE_MASK 0x0f
+#define EICON_CTYPE_QUADRO_NR(n) (n<<4)
+
+#define MAX_HEADER_LEN 10
+
+/* Struct for adding new cards */
+typedef struct eicon_cdef {
+ int membase;
+ int irq;
+ char id[10];
+} eicon_cdef;
+
+#define EICON_ISA_BOOT_MEMCHK 1
+#define EICON_ISA_BOOT_NORMAL 2
+
+/* Struct for downloading protocol via ioctl for ISA cards */
+typedef struct {
+ /* start-up parameters */
+ unsigned char tei;
+ unsigned char nt2;
+ unsigned char skip1;
+ unsigned char WatchDog;
+ unsigned char Permanent;
+ unsigned char XInterface;
+ unsigned char StableL2;
+ unsigned char NoOrderCheck;
+ unsigned char HandsetType;
+ unsigned char skip2;
+ unsigned char LowChannel;
+ unsigned char ProtVersion;
+ unsigned char Crc4;
+ unsigned char Loopback;
+ unsigned char oad[32];
+ unsigned char osa[32];
+ unsigned char spid[32];
+ unsigned char boot_opt;
+ unsigned long bootstrap_len;
+ unsigned long firmware_len;
+ unsigned char code[1]; /* Rest (bootstrap- and firmware code) will be allocated */
+} eicon_isa_codebuf;
+
+/* Struct for downloading protocol via ioctl for PCI cards */
+typedef struct {
+ /* start-up parameters */
+ unsigned char tei;
+ unsigned char nt2;
+ unsigned char WatchDog;
+ unsigned char Permanent;
+ unsigned char XInterface;
+ unsigned char StableL2;
+ unsigned char NoOrderCheck;
+ unsigned char HandsetType;
+ unsigned char LowChannel;
+ unsigned char ProtVersion;
+ unsigned char Crc4;
+ unsigned char NoHscx30Mode; /* switch PRI into No HSCX30 test mode */
+ unsigned char Loopback; /* switch card into Loopback mode */
+ struct q931_link_s
+ {
+ unsigned char oad[32];
+ unsigned char osa[32];
+ unsigned char spid[32];
+ } l[2];
+ unsigned long protocol_len;
+ unsigned int dsp_code_num;
+ unsigned long dsp_code_len[9];
+ unsigned char code[1]; /* Rest (protocol- and dsp code) will be allocated */
+} eicon_pci_codebuf;
+
+/* Data for downloading protocol via ioctl */
+typedef union {
+ eicon_isa_codebuf isa;
+ eicon_pci_codebuf pci;
+} eicon_codebuf;
+
+/* Data for Management interface */
+typedef struct {
+ int count;
+ int pos;
+ int length[50];
+ unsigned char data[700];
+} eicon_manifbuf;
+
+
+#ifdef __KERNEL__
+
+/* Kernel includes */
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/tqueue.h>
+#include <linux/interrupt.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+
+#include <linux/isdnif.h>
+
+typedef struct {
+ __u16 length __attribute__ ((packed)); /* length of data/parameter field */
+ __u8 P[1]; /* data/parameter field */
+} eicon_PBUFFER;
+
+#include "eicon_isa.h"
+
+/* Macro for delay via schedule() */
+#define SLEEP(j) { \
+ current->state = TASK_INTERRUPTIBLE; \
+ schedule_timeout(j); \
+}
+
+#endif /* KERNEL */
+
+
+#define DSP_COMBIFILE_FORMAT_IDENTIFICATION_SIZE 48
+#define DSP_COMBIFILE_FORMAT_VERSION_BCD 0x0100
+
+#define DSP_FILE_FORMAT_IDENTIFICATION_SIZE 48
+#define DSP_FILE_FORMAT_VERSION_BCD 0x0100
+
+typedef struct tag_dsp_combifile_header
+{
+ char format_identification[DSP_COMBIFILE_FORMAT_IDENTIFICATION_SIZE] __attribute__ ((packed));
+ __u16 format_version_bcd __attribute__ ((packed));
+ __u16 header_size __attribute__ ((packed));
+ __u16 combifile_description_size __attribute__ ((packed));
+ __u16 directory_entries __attribute__ ((packed));
+ __u16 directory_size __attribute__ ((packed));
+ __u16 download_count __attribute__ ((packed));
+ __u16 usage_mask_size __attribute__ ((packed));
+} t_dsp_combifile_header;
+
+typedef struct tag_dsp_combifile_directory_entry
+{
+ __u16 card_type_number __attribute__ ((packed));
+ __u16 file_set_number __attribute__ ((packed));
+} t_dsp_combifile_directory_entry;
+
+typedef struct tag_dsp_file_header
+{
+ char format_identification[DSP_FILE_FORMAT_IDENTIFICATION_SIZE] __attribute__ ((packed));
+ __u16 format_version_bcd __attribute__ ((packed));
+ __u16 download_id __attribute__ ((packed));
+ __u16 download_flags __attribute__ ((packed));
+ __u16 required_processing_power __attribute__ ((packed));
+ __u16 interface_channel_count __attribute__ ((packed));
+ __u16 header_size __attribute__ ((packed));
+ __u16 download_description_size __attribute__ ((packed));
+ __u16 memory_block_table_size __attribute__ ((packed));
+ __u16 memory_block_count __attribute__ ((packed));
+ __u16 segment_table_size __attribute__ ((packed));
+ __u16 segment_count __attribute__ ((packed));
+ __u16 symbol_table_size __attribute__ ((packed));
+ __u16 symbol_count __attribute__ ((packed));
+ __u16 total_data_size_dm __attribute__ ((packed));
+ __u16 data_block_count_dm __attribute__ ((packed));
+ __u16 total_data_size_pm __attribute__ ((packed));
+ __u16 data_block_count_pm __attribute__ ((packed));
+} t_dsp_file_header;
+
+typedef struct tag_dsp_memory_block_desc
+{
+ __u16 alias_memory_block;
+ __u16 memory_type;
+ __u16 address;
+ __u16 size; /* DSP words */
+} t_dsp_memory_block_desc;
+
+typedef struct tag_dsp_segment_desc
+{
+ __u16 memory_block;
+ __u16 attributes;
+ __u16 base;
+ __u16 size;
+ __u16 alignment; /* ==0 -> no other legal start address than base */
+} t_dsp_segment_desc;
+
+typedef struct tag_dsp_symbol_desc
+{
+ __u16 symbol_id;
+ __u16 segment;
+ __u16 offset;
+ __u16 size; /* DSP words */
+} t_dsp_symbol_desc;
+
+typedef struct tag_dsp_data_block_header
+{
+ __u16 attributes;
+ __u16 segment;
+ __u16 offset;
+ __u16 size; /* DSP words */
+} t_dsp_data_block_header;
+
+typedef struct tag_dsp_download_desc /* be sure to keep native alignment for MAESTRA's */
+{
+ __u16 download_id;
+ __u16 download_flags;
+ __u16 required_processing_power;
+ __u16 interface_channel_count;
+ __u16 excess_header_size;
+ __u16 memory_block_count;
+ __u16 segment_count;
+ __u16 symbol_count;
+ __u16 data_block_count_dm;
+ __u16 data_block_count_pm;
+ __u8 * p_excess_header_data __attribute__ ((packed));
+ char * p_download_description __attribute__ ((packed));
+ t_dsp_memory_block_desc *p_memory_block_table __attribute__ ((packed));
+ t_dsp_segment_desc *p_segment_table __attribute__ ((packed));
+ t_dsp_symbol_desc *p_symbol_table __attribute__ ((packed));
+ __u16 * p_data_blocks_dm __attribute__ ((packed));
+ __u16 * p_data_blocks_pm __attribute__ ((packed));
+} t_dsp_download_desc;
+
+
+#ifdef __KERNEL__
+
+typedef struct {
+ __u8 Req; /* pending request */
+ __u8 Rc; /* return code received */
+ __u8 Ind; /* indication received */
+ __u8 ReqCh; /* channel of current Req */
+ __u8 RcCh; /* channel of current Rc */
+ __u8 IndCh; /* channel of current Ind */
+ __u8 D3Id; /* ID used by this entity */
+ __u8 B2Id; /* ID used by this entity */
+ __u8 GlobalId; /* reserved field */
+ __u8 XNum; /* number of X-buffers */
+ __u8 RNum; /* number of R-buffers */
+ struct sk_buff_head X; /* X-buffer queue */
+ struct sk_buff_head R; /* R-buffer queue */
+ __u8 RNR; /* receive not ready flag */
+ __u8 complete; /* receive complete status */
+ __u8 busy; /* busy flag */
+ __u16 ref; /* saved reference */
+} entity;
+
+
+typedef struct {
+ int No; /* Channel Number */
+ unsigned short callref; /* Call Reference */
+ unsigned short fsm_state; /* Current D-Channel state */
+ unsigned short eazmask; /* EAZ-Mask for this Channel */
+ unsigned int queued; /* User-Data Bytes in TX queue */
+ unsigned int waitq; /* User-Data Bytes in wait queue */
+ unsigned int waitpq; /* User-Data Bytes in packet queue */
+ unsigned short plci;
+ unsigned short ncci;
+ unsigned char l2prot; /* Layer 2 protocol */
+ unsigned char l3prot; /* Layer 3 protocol */
+ entity e; /* Entity */
+ char cpn[32]; /* remember cpn */
+ char oad[32]; /* remember oad */
+ unsigned char cause[2]; /* Last Cause */
+ unsigned char si1;
+ unsigned char si2;
+} eicon_chan;
+
+typedef struct {
+ eicon_chan *ptr;
+} eicon_chan_ptr;
+
+#include "eicon_pci.h"
+
+#define EICON_FLAGS_RUNNING 1 /* Cards driver activated */
+#define EICON_FLAGS_PVALID 2 /* Cards port is valid */
+#define EICON_FLAGS_IVALID 4 /* Cards irq is valid */
+#define EICON_FLAGS_MVALID 8 /* Cards membase is valid */
+#define EICON_FLAGS_LOADED 8 /* Firmware loaded */
+
+#define EICON_BCH 2 /* # of channels per card */
+
+/* D-Channel states */
+#define EICON_STATE_NULL 0
+#define EICON_STATE_ICALL 1
+#define EICON_STATE_OCALL 2
+#define EICON_STATE_IWAIT 3
+#define EICON_STATE_OWAIT 4
+#define EICON_STATE_IBWAIT 5
+#define EICON_STATE_OBWAIT 6
+#define EICON_STATE_BWAIT 7
+#define EICON_STATE_BHWAIT 8
+#define EICON_STATE_BHWAIT2 9
+#define EICON_STATE_DHWAIT 10
+#define EICON_STATE_DHWAIT2 11
+#define EICON_STATE_BSETUP 12
+#define EICON_STATE_ACTIVE 13
+#define EICON_STATE_ICALLW 14
+#define EICON_STATE_LISTEN 15
+#define EICON_STATE_WMCONN 16
+
+#define EICON_MAX_QUEUED 8000 /* 2 * maxbuff */
+
+#define EICON_LOCK_TX 0
+#define EICON_LOCK_RX 1
+
+typedef struct {
+ int dummy;
+} eicon_mca_card;
+
+typedef union {
+ eicon_isa_card isa;
+ eicon_pci_card pci;
+ eicon_mca_card mca;
+} eicon_hwif;
+
+typedef struct {
+ __u8 ret;
+ __u8 id;
+ __u8 ch;
+} eicon_ack;
+
+typedef struct {
+ __u8 code;
+ __u8 id;
+ __u8 ch;
+} eicon_req;
+
+typedef struct {
+ __u8 ret;
+ __u8 id;
+ __u8 ch;
+ __u8 more;
+} eicon_indhdr;
+
+typedef struct msn_entry {
+ char eaz;
+ char msn[16];
+ struct msn_entry * next;
+} msn_entry;
+
+/*
+ * Per card driver data
+ */
+typedef struct eicon_card {
+ eicon_hwif hwif; /* Hardware dependant interface */
+ u_char ptype; /* Protocol type (1TR6 or Euro) */
+ u_char bus; /* Bustype (ISA, MCA, PCI) */
+ u_char type; /* Cardtype (EICON_CTYPE_...) */
+ struct eicon_card *qnext; /* Pointer to next quadro adapter */
+ int Feature; /* Protocol Feature Value */
+ struct eicon_card *next; /* Pointer to next device struct */
+ int myid; /* Driver-Nr. assigned by linklevel */
+ unsigned long flags; /* Statusflags */
+ unsigned long ilock; /* Semaphores for IRQ-Routines */
+ struct sk_buff_head rcvq; /* Receive-Message queue */
+ struct sk_buff_head sndq; /* Send-Message queue */
+ struct sk_buff_head rackq; /* Req-Ack-Message queue */
+ struct sk_buff_head sackq; /* Data-Ack-Message queue */
+ u_char *ack_msg; /* Ptr to User Data in User skb */
+ __u16 need_b3ack; /* Flag: Need ACK for current skb */
+ struct sk_buff *sbuf; /* skb which is currently sent */
+ struct tq_struct snd_tq; /* Task struct for xmit bh */
+ struct tq_struct rcv_tq; /* Task struct for rcv bh */
+ struct tq_struct ack_tq; /* Task struct for ack bh */
+ msn_entry *msn_list;
+ unsigned short msgnum; /* Message number for sending */
+ eicon_chan* IdTable[256]; /* Table to find entity */
+ __u16 ref_in;
+ __u16 ref_out;
+ int nchannels; /* Number of B-Channels */
+ int ReadyInt; /* Ready Interrupt */
+ eicon_chan *bch; /* B-Channel status/control */
+ char status_buf[256]; /* Buffer for status messages */
+ char *status_buf_read;
+ char *status_buf_write;
+ char *status_buf_end;
+ isdn_if interface; /* Interface to upper layer */
+ char regname[35]; /* Name used for request_region */
+} eicon_card;
+
+/* -----------------------------------------------------------**
+** The PROTOCOL_FEATURE_STRING **
+** defines capabilities and **
+** features of the actual protocol code. It's used as a bit **
+** mask. **
+** The following Bits are defined: **
+** -----------------------------------------------------------*/
+#define PROTCAP_TELINDUS 0x0001 /* Telindus Variant of protocol code */
+#define PROTCAP_MANIF 0x0002 /* Management interface implemented */
+#define PROTCAP_V_42 0x0004 /* V42 implemented */
+#define PROTCAP_V90D 0x0008 /* V.90D (implies up to 384k DSP code) */
+#define PROTCAP_EXTD_FAX 0x0010 /* Extended FAX (ECM, 2D, T6, Polling) */
+#define PROTCAP_FREE4 0x0020 /* not used */
+#define PROTCAP_FREE5 0x0040 /* not used */
+#define PROTCAP_FREE6 0x0080 /* not used */
+#define PROTCAP_FREE7 0x0100 /* not used */
+#define PROTCAP_FREE8 0x0200 /* not used */
+#define PROTCAP_FREE9 0x0400 /* not used */
+#define PROTCAP_FREE10 0x0800 /* not used */
+#define PROTCAP_FREE11 0x1000 /* not used */
+#define PROTCAP_FREE12 0x2000 /* not used */
+#define PROTCAP_FREE13 0x4000 /* not used */
+#define PROTCAP_EXTENSION 0x8000 /* used for future extentions */
+
+#include "eicon_idi.h"
+
+extern eicon_card *cards;
+extern char *eicon_ctype_name[];
+
+
+extern __inline__ void eicon_schedule_tx(eicon_card *card)
+{
+ queue_task(&card->snd_tq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+extern __inline__ void eicon_schedule_rx(eicon_card *card)
+{
+ queue_task(&card->rcv_tq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+extern __inline__ void eicon_schedule_ack(eicon_card *card)
+{
+ queue_task(&card->ack_tq, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+extern char *eicon_find_eaz(eicon_card *, char);
+extern int eicon_addcard(int, int, int, char *);
+extern void eicon_io_transmit(eicon_card *card);
+extern void eicon_irq(int irq, void *dev_id, struct pt_regs *regs);
+extern void eicon_io_rcv_dispatch(eicon_card *ccard);
+extern void eicon_io_ack_dispatch(eicon_card *ccard);
+extern ulong DebugVar;
+
+#endif /* __KERNEL__ */
+
+#endif /* eicon_h */
diff --git a/drivers/isdn/eicon/eicon_dsp.h b/drivers/isdn/eicon/eicon_dsp.h
new file mode 100644
index 000000000..94a4595c8
--- /dev/null
+++ b/drivers/isdn/eicon/eicon_dsp.h
@@ -0,0 +1,304 @@
+/* $Id: eicon_dsp.h,v 1.2 1999/03/29 11:19:42 armin Exp $
+ *
+ * ISDN lowlevel-module for Eicon.Diehl active cards.
+ * DSP definitions
+ *
+ * Copyright 1999 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon_dsp.h,v $
+ * Revision 1.2 1999/03/29 11:19:42 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.1 1999/03/02 12:18:54 armin
+ * First checkin of DSP defines for audio features.
+ *
+ *
+ */
+
+#ifndef DSP_H
+#define DSP_H
+
+#define DSP_UDATA_REQUEST_RECONFIGURE 0
+/*
+parameters:
+ <word> reconfigure delay (in 8kHz samples)
+ <word> reconfigure code
+ <byte> reconfigure hdlc preamble flags
+*/
+
+#define DSP_RECONFIGURE_TX_FLAG 0x8000
+#define DSP_RECONFIGURE_SHORT_TRAIN_FLAG 0x4000
+#define DSP_RECONFIGURE_ECHO_PROTECT_FLAG 0x2000
+#define DSP_RECONFIGURE_HDLC_FLAG 0x1000
+#define DSP_RECONFIGURE_SYNC_FLAG 0x0800
+#define DSP_RECONFIGURE_PROTOCOL_MASK 0x00ff
+#define DSP_RECONFIGURE_IDLE 0
+#define DSP_RECONFIGURE_V25 1
+#define DSP_RECONFIGURE_V21_CH2 2
+#define DSP_RECONFIGURE_V27_2400 3
+#define DSP_RECONFIGURE_V27_4800 4
+#define DSP_RECONFIGURE_V29_7200 5
+#define DSP_RECONFIGURE_V29_9600 6
+#define DSP_RECONFIGURE_V33_12000 7
+#define DSP_RECONFIGURE_V33_14400 8
+#define DSP_RECONFIGURE_V17_7200 9
+#define DSP_RECONFIGURE_V17_9600 10
+#define DSP_RECONFIGURE_V17_12000 11
+#define DSP_RECONFIGURE_V17_14400 12
+
+/*
+data indications if transparent framer
+ <byte> data 0
+ <byte> data 1
+ ...
+
+data indications if HDLC framer
+ <byte> data 0
+ <byte> data 1
+ ...
+ <byte> CRC 0
+ <byte> CRC 1
+ <byte> preamble flags
+*/
+
+#define DSP_UDATA_REQUEST_SWITCH_FRAMER 1
+/*
+parameters:
+ <byte> transmit framer type
+ <byte> receive framer type
+*/
+
+#define DSP_REQUEST_SWITCH_FRAMER_HDLC 0
+#define DSP_REQUEST_SWITCH_FRAMER_TRANSPARENT 1
+#define DSP_REQUEST_SWITCH_FRAMER_ASYNC 2
+
+
+#define DSP_UDATA_REQUEST_CLEARDOWN 2
+/*
+parameters:
+ - none -
+*/
+
+
+#define DSP_UDATA_REQUEST_TX_CONFIRMATION_ON 3
+/*
+parameters:
+ - none -
+*/
+
+
+#define DSP_UDATA_REQUEST_TX_CONFIRMATION_OFF 4
+/*
+parameters:
+ - none -
+*/
+
+
+#define DSP_UDATA_INDICATION_SYNC 0
+/*
+returns:
+ <word> time of sync (sampled from counter at 8kHz)
+*/
+
+#define DSP_UDATA_INDICATION_DCD_OFF 1
+/*
+returns:
+ <word> time of DCD off (sampled from counter at 8kHz)
+*/
+
+#define DSP_UDATA_INDICATION_DCD_ON 2
+/*
+returns:
+ <word> time of DCD on (sampled from counter at 8kHz)
+ <byte> connected norm
+ <word> connected options
+ <dword> connected speed (bit/s, max of tx and rx speed)
+ <word> roundtrip delay (ms)
+ <dword> connected speed tx (bit/s)
+ <dword> connected speed rx (bit/s)
+*/
+
+#define DSP_UDATA_INDICATION_CTS_OFF 3
+/*
+returns:
+ <word> time of CTS off (sampled from counter at 8kHz)
+*/
+
+#define DSP_UDATA_INDICATION_CTS_ON 4
+/*
+returns:
+ <word> time of CTS on (sampled from counter at 8kHz)
+ <byte> connected norm
+ <word> connected options
+ <dword> connected speed (bit/s, max of tx and rx speed)
+ <word> roundtrip delay (ms)
+ <dword> connected speed tx (bit/s)
+ <dword> connected speed rx (bit/s)
+*/
+
+typedef struct eicon_dsp_ind {
+ __u16 time __attribute__ ((packed));
+ __u8 norm __attribute__ ((packed));
+ __u16 options __attribute__ ((packed));
+ __u32 speed __attribute__ ((packed));
+ __u16 delay __attribute__ ((packed));
+ __u32 txspeed __attribute__ ((packed));
+ __u32 rxspeed __attribute__ ((packed));
+} eicon_dsp_ind;
+
+#define DSP_CONNECTED_NORM_UNSPECIFIED 0
+#define DSP_CONNECTED_NORM_V21 1
+#define DSP_CONNECTED_NORM_V23 2
+#define DSP_CONNECTED_NORM_V22 3
+#define DSP_CONNECTED_NORM_V22_BIS 4
+#define DSP_CONNECTED_NORM_V32_BIS 5
+#define DSP_CONNECTED_NORM_V34 6
+#define DSP_CONNECTED_NORM_V8 7
+#define DSP_CONNECTED_NORM_BELL_212A 8
+#define DSP_CONNECTED_NORM_BELL_103 9
+#define DSP_CONNECTED_NORM_V29_LEASED_LINE 10
+#define DSP_CONNECTED_NORM_V33_LEASED_LINE 11
+#define DSP_CONNECTED_NORM_V90 12
+#define DSP_CONNECTED_NORM_V21_CH2 13
+#define DSP_CONNECTED_NORM_V27_TER 14
+#define DSP_CONNECTED_NORM_V29 15
+#define DSP_CONNECTED_NORM_V33 16
+#define DSP_CONNECTED_NORM_V17 17
+
+#define DSP_CONNECTED_OPTION_TRELLIS 0x0001
+#define DSP_CONNECTED_OPTION_V42_TRANS 0x0002
+#define DSP_CONNECTED_OPTION_V42_LAPM 0x0004
+#define DSP_CONNECTED_OPTION_SHORT_TRAIN 0x0008
+#define DSP_CONNECTED_OPTION_TALKER_ECHO_PROTECT 0x0010
+
+
+#define DSP_UDATA_INDICATION_DISCONNECT 5
+/*
+returns:
+ <byte> cause
+*/
+
+#define DSP_DISCONNECT_CAUSE_NONE 0x00
+#define DSP_DISCONNECT_CAUSE_BUSY_TONE 0x01
+#define DSP_DISCONNECT_CAUSE_CONGESTION_TONE 0x02
+#define DSP_DISCONNECT_CAUSE_INCOMPATIBILITY 0x03
+#define DSP_DISCONNECT_CAUSE_CLEARDOWN 0x04
+#define DSP_DISCONNECT_CAUSE_TRAINING_TIMEOUT 0x05
+
+
+#define DSP_UDATA_INDICATION_TX_CONFIRMATION 6
+/*
+returns:
+ <word> confirmation number
+*/
+
+
+#define DSP_UDATA_REQUEST_SEND_DTMF_DIGITS 16
+/*
+parameters:
+ <word> tone duration (ms)
+ <word> gap duration (ms)
+ <byte> digit 0 tone code
+ ...
+ <byte> digit n tone code
+*/
+
+#define DSP_SEND_DTMF_DIGITS_HEADER_LENGTH 5
+
+#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_697_HZ 0x00
+#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_770_HZ 0x01
+#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_852_HZ 0x02
+#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_941_HZ 0x03
+#define DSP_DTMF_DIGIT_TONE_LOW_GROUP_MASK 0x03
+#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_1209_HZ 0x00
+#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_1336_HZ 0x04
+#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_1477_HZ 0x08
+#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_1633_HZ 0x0c
+#define DSP_DTMF_DIGIT_TONE_HIGH_GROUP_MASK 0x0c
+
+#define DSP_DTMF_DIGIT_TONE_CODE_0 0x07
+#define DSP_DTMF_DIGIT_TONE_CODE_1 0x00
+#define DSP_DTMF_DIGIT_TONE_CODE_2 0x04
+#define DSP_DTMF_DIGIT_TONE_CODE_3 0x08
+#define DSP_DTMF_DIGIT_TONE_CODE_4 0x01
+#define DSP_DTMF_DIGIT_TONE_CODE_5 0x05
+#define DSP_DTMF_DIGIT_TONE_CODE_6 0x09
+#define DSP_DTMF_DIGIT_TONE_CODE_7 0x02
+#define DSP_DTMF_DIGIT_TONE_CODE_8 0x06
+#define DSP_DTMF_DIGIT_TONE_CODE_9 0x0a
+#define DSP_DTMF_DIGIT_TONE_CODE_STAR 0x03
+#define DSP_DTMF_DIGIT_TONE_CODE_HASHMARK 0x0b
+#define DSP_DTMF_DIGIT_TONE_CODE_A 0x0c
+#define DSP_DTMF_DIGIT_TONE_CODE_B 0x0d
+#define DSP_DTMF_DIGIT_TONE_CODE_C 0x0e
+#define DSP_DTMF_DIGIT_TONE_CODE_D 0x0f
+
+
+#define DSP_UDATA_INDICATION_DTMF_DIGITS_SENT 16
+/*
+returns:
+ - none -
+ One indication will be sent for every request.
+*/
+
+
+#define DSP_UDATA_REQUEST_ENABLE_DTMF_RECEIVER 17
+/*
+parameters:
+ <word> tone duration (ms)
+ <word> gap duration (ms)
+*/
+
+#define DSP_UDATA_REQUEST_DISABLE_DTMF_RECEIVER 18
+/*
+parameters:
+ - none -
+*/
+
+#define DSP_UDATA_INDICATION_DTMF_DIGITS_RECEIVED 17
+/*
+returns:
+ <byte> digit 0 tone code
+ ...
+ <byte> digit n tone code
+*/
+
+#define DSP_DTMF_DIGITS_RECEIVED_HEADER_LENGTH 1
+
+
+#define DSP_UDATA_INDICATION_MODEM_CALLING_TONE 18
+/*
+returns:
+ - none -
+*/
+
+#define DSP_UDATA_INDICATION_FAX_CALLING_TONE 19
+/*
+returns:
+ - none -
+*/
+
+#define DSP_UDATA_INDICATION_ANSWER_TONE 20
+/*
+returns:
+ - none -
+*/
+
+#endif /* DSP_H */
+
diff --git a/drivers/isdn/eicon/eicon_idi.c b/drivers/isdn/eicon/eicon_idi.c
new file mode 100644
index 000000000..a28f316c1
--- /dev/null
+++ b/drivers/isdn/eicon/eicon_idi.c
@@ -0,0 +1,1479 @@
+/* $Id: eicon_idi.c,v 1.9 1999/03/29 11:19:42 armin Exp $
+ *
+ * ISDN lowlevel-module for Eicon.Diehl active cards.
+ * IDI interface
+ *
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon_idi.c,v $
+ * Revision 1.9 1999/03/29 11:19:42 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.8 1999/03/02 12:37:43 armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.7 1999/02/03 18:34:35 armin
+ * Channel selection for outgoing calls w/o CHI.
+ * Added channel # in debug messages.
+ * L2 Transparent should work with 800 byte/packet now.
+ *
+ * Revision 1.6 1999/01/26 07:18:59 armin
+ * Bug with wrong added CPN fixed.
+ *
+ * Revision 1.5 1999/01/24 20:14:11 armin
+ * Changed and added debug stuff.
+ * Better data sending. (still problems with tty's flip buffer)
+ *
+ * Revision 1.4 1999/01/10 18:46:05 armin
+ * Bug with wrong values in HLC fixed.
+ * Bytes to send are counted and limited now.
+ *
+ * Revision 1.3 1999/01/05 14:49:34 armin
+ * Added experimental usage of full BC and HLC for
+ * speech, 3.1kHz audio, fax gr.2/3
+ *
+ * Revision 1.2 1999/01/04 13:19:29 armin
+ * Channel status with listen-request wrong - fixed.
+ *
+ * Revision 1.1 1999/01/01 18:09:41 armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "eicon.h"
+#include "eicon_idi.h"
+#include "eicon_dsp.h"
+
+#undef EICON_FULL_SERVICE_OKTETT
+
+char *eicon_idi_revision = "$Revision: 1.9 $";
+
+eicon_manifbuf *manbuf;
+
+static char BC_Speech[3] = { 0x80, 0x90, 0xa3 };
+static char BC_31khz[3] = { 0x90, 0x90, 0xa3 };
+static char BC_64k[2] = { 0x88, 0x90 };
+static char BC_video[3] = { 0x91, 0x90, 0xa5 };
+
+#ifdef EICON_FULL_SERVICE_OKTETT
+/*
+static char HLC_telephony[2] = { 0x91, 0x81 };
+*/
+static char HLC_faxg3[2] = { 0x91, 0x84 };
+#endif
+
+int eicon_idi_manage_assign(eicon_card *card);
+int eicon_idi_manage_remove(eicon_card *card);
+
+int
+idi_assign_req(eicon_REQ *reqbuf, int signet, eicon_chan *chan)
+{
+ int l = 0;
+ if (!signet) {
+ /* Signal Layer */
+ reqbuf->XBuffer.P[l++] = CAI;
+ reqbuf->XBuffer.P[l++] = 1;
+ reqbuf->XBuffer.P[l++] = 0;
+ reqbuf->XBuffer.P[l++] = KEY;
+ reqbuf->XBuffer.P[l++] = 3;
+ reqbuf->XBuffer.P[l++] = 'I';
+ reqbuf->XBuffer.P[l++] = '4';
+ reqbuf->XBuffer.P[l++] = 'L';
+ reqbuf->XBuffer.P[l++] = SHIFT|6;
+ reqbuf->XBuffer.P[l++] = SIN;
+ reqbuf->XBuffer.P[l++] = 2;
+ reqbuf->XBuffer.P[l++] = 0;
+ reqbuf->XBuffer.P[l++] = 0;
+ reqbuf->XBuffer.P[l++] = 0; /* end */
+ reqbuf->Req = ASSIGN;
+ reqbuf->ReqCh = 0;
+ reqbuf->ReqId = 0;
+ reqbuf->XBuffer.length = l;
+ reqbuf->Reference = 0; /* Sig Entity */
+ }
+ else {
+ /* Network Layer */
+ reqbuf->XBuffer.P[l++] = CAI;
+ reqbuf->XBuffer.P[l++] = 1;
+ reqbuf->XBuffer.P[l++] = chan->e.D3Id;
+ reqbuf->XBuffer.P[l++] = LLC;
+ reqbuf->XBuffer.P[l++] = 2;
+ switch(chan->l2prot) {
+ case ISDN_PROTO_L2_HDLC:
+ reqbuf->XBuffer.P[l++] = 2;
+ break;
+ case ISDN_PROTO_L2_X75I:
+ case ISDN_PROTO_L2_X75UI:
+ case ISDN_PROTO_L2_X75BUI:
+ reqbuf->XBuffer.P[l++] = 5;
+ break;
+ case ISDN_PROTO_L2_TRANS:
+ case ISDN_PROTO_L2_MODEM:
+ reqbuf->XBuffer.P[l++] = 2;
+ break;
+ default:
+ reqbuf->XBuffer.P[l++] = 1;
+ }
+ switch(chan->l3prot) {
+ case ISDN_PROTO_L3_TRANS:
+ default:
+ reqbuf->XBuffer.P[l++] = 4;
+ }
+ reqbuf->XBuffer.P[l++] = 0; /* end */
+ reqbuf->Req = ASSIGN;
+ reqbuf->ReqCh = 0;
+ reqbuf->ReqId = 0x20;
+ reqbuf->XBuffer.length = l;
+ reqbuf->Reference = 1; /* Net Entity */
+ }
+ return(0);
+}
+
+int
+idi_put_req(eicon_REQ *reqbuf, int rq, int signet)
+{
+ reqbuf->Req = rq;
+ reqbuf->ReqCh = 0;
+ reqbuf->ReqId = 1;
+ reqbuf->XBuffer.length = 1;
+ reqbuf->XBuffer.P[0] = 0;
+ reqbuf->Reference = signet;
+ return(0);
+}
+
+int
+idi_call_res_req(eicon_REQ *reqbuf, eicon_chan *chan)
+{
+ int l = 9;
+ reqbuf->Req = CALL_RES;
+ reqbuf->ReqCh = 0;
+ reqbuf->ReqId = 1;
+ reqbuf->XBuffer.P[0] = CAI;
+ reqbuf->XBuffer.P[1] = 6;
+ reqbuf->XBuffer.P[2] = 9;
+ reqbuf->XBuffer.P[3] = 0;
+ reqbuf->XBuffer.P[4] = 0;
+ reqbuf->XBuffer.P[5] = 0;
+ reqbuf->XBuffer.P[6] = 32;
+ reqbuf->XBuffer.P[7] = 3;
+ switch(chan->l2prot) {
+ case ISDN_PROTO_L2_X75I:
+ case ISDN_PROTO_L2_X75UI:
+ case ISDN_PROTO_L2_X75BUI:
+ case ISDN_PROTO_L2_HDLC:
+ reqbuf->XBuffer.P[1] = 1;
+ reqbuf->XBuffer.P[2] = 0x05;
+ l = 4;
+ break;
+ case ISDN_PROTO_L2_V11096:
+ reqbuf->XBuffer.P[2] = 0x0d;
+ reqbuf->XBuffer.P[3] = 5;
+ reqbuf->XBuffer.P[4] = 0;
+ break;
+ case ISDN_PROTO_L2_V11019:
+ reqbuf->XBuffer.P[2] = 0x0d;
+ reqbuf->XBuffer.P[3] = 6;
+ reqbuf->XBuffer.P[4] = 0;
+ break;
+ case ISDN_PROTO_L2_V11038:
+ reqbuf->XBuffer.P[2] = 0x0d;
+ reqbuf->XBuffer.P[3] = 7;
+ reqbuf->XBuffer.P[4] = 0;
+ break;
+ case ISDN_PROTO_L2_MODEM:
+ reqbuf->XBuffer.P[2] = 0x11;
+ reqbuf->XBuffer.P[3] = 7;
+ reqbuf->XBuffer.P[4] = 0;
+ reqbuf->XBuffer.P[5] = 0;
+ reqbuf->XBuffer.P[6] = 128;
+ reqbuf->XBuffer.P[7] = 0;
+ break;
+ }
+ reqbuf->XBuffer.P[8] = 0;
+ reqbuf->XBuffer.length = l;
+ reqbuf->Reference = 0; /* Sig Entity */
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_req: Ch%d: Call_Res\n", chan->No);
+ return(0);
+}
+
+int
+idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer)
+{
+ struct sk_buff *skb;
+ struct sk_buff *skb2;
+ eicon_REQ *reqbuf;
+ eicon_chan_ptr *chan2;
+
+ skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
+ skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
+
+ if ((!skb) || (!skb2)) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No);
+ return -ENOMEM;
+ }
+
+ chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
+ chan2->ptr = chan;
+
+ reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
+ if (DebugVar & 8)
+ printk(KERN_DEBUG "idi_req: Ch%d: 0x%02x (%s)\n", chan->No, cmd, (layer)?"Net":"Sig");
+ if (layer) cmd |= 0x700;
+ switch(cmd) {
+ case ASSIGN:
+ case ASSIGN|0x700:
+ idi_assign_req(reqbuf, layer, chan);
+ break;
+ case REMOVE:
+ case REMOVE|0x700:
+ idi_put_req(reqbuf, REMOVE, layer);
+ break;
+ case INDICATE_REQ:
+ idi_put_req(reqbuf, INDICATE_REQ, 0);
+ break;
+ case HANGUP:
+ idi_put_req(reqbuf, HANGUP, 0);
+ break;
+ case REJECT:
+ idi_put_req(reqbuf, REJECT, 0);
+ break;
+ case CALL_ALERT:
+ idi_put_req(reqbuf, CALL_ALERT, 0);
+ break;
+ case CALL_RES:
+ idi_call_res_req(reqbuf, chan);
+ break;
+ case IDI_N_CONNECT|0x700:
+ idi_put_req(reqbuf, IDI_N_CONNECT, 1);
+ break;
+ case IDI_N_CONNECT_ACK|0x700:
+ idi_put_req(reqbuf, IDI_N_CONNECT_ACK, 1);
+ break;
+ case IDI_N_DISC|0x700:
+ idi_put_req(reqbuf, IDI_N_DISC, 1);
+ break;
+ case IDI_N_DISC_ACK|0x700:
+ idi_put_req(reqbuf, IDI_N_DISC_ACK, 1);
+ break;
+ default:
+ if (DebugVar & 1)
+ printk(KERN_ERR "idi_req: Ch%d: Unknown request\n", chan->No);
+ return(-1);
+ }
+
+ skb_queue_tail(&chan->e.X, skb);
+ skb_queue_tail(&card->sndq, skb2);
+ eicon_schedule_tx(card);
+ return(0);
+}
+
+int
+eicon_idi_listen_req(eicon_card *card, eicon_chan *chan)
+{
+ if (DebugVar & 16)
+ printk(KERN_DEBUG"idi_req: Ch%d: Listen_Req eazmask=0x%x\n",chan->No, chan->eazmask);
+ if (!chan->e.D3Id) {
+ idi_do_req(card, chan, ASSIGN, 0);
+ }
+ if (chan->fsm_state == EICON_STATE_NULL) {
+ idi_do_req(card, chan, INDICATE_REQ, 0);
+ chan->fsm_state = EICON_STATE_LISTEN;
+ }
+ return(0);
+}
+
+unsigned char
+idi_si2bc(int si1, int si2, char *bc, char *hlc)
+{
+ hlc[0] = 0;
+ switch(si1) {
+ case 1:
+ bc[0] = 0x90; /* 3,1 kHz audio */
+ bc[1] = 0x90; /* 64 kbit/s */
+ bc[2] = 0xa3; /* G.711 A-law */
+#ifdef EICON_FULL_SERVICE_OKTETT
+ if (si2 == 1) {
+ bc[0] = 0x80; /* Speech */
+ hlc[0] = 0x02; /* hlc len */
+ hlc[1] = 0x91; /* first hic */
+ hlc[2] = 0x81; /* Telephony */
+ }
+#endif
+ return(3);
+ case 2:
+ bc[0] = 0x90; /* 3,1 kHz audio */
+ bc[1] = 0x90; /* 64 kbit/s */
+ bc[2] = 0xa3; /* G.711 A-law */
+#ifdef EICON_FULL_SERVICE_OKTETT
+ if (si2 == 2) {
+ hlc[0] = 0x02; /* hlc len */
+ hlc[1] = 0x91; /* first hic */
+ hlc[2] = 0x84; /* Fax Gr.2/3 */
+ }
+#endif
+ return(3);
+ case 5:
+ case 7:
+ default:
+ bc[0] = 0x88;
+ bc[1] = 0x90;
+ return(2);
+ }
+ return (0);
+}
+
+int
+idi_hangup(eicon_card *card, eicon_chan *chan)
+{
+ if ((chan->fsm_state == EICON_STATE_ACTIVE) ||
+ (chan->fsm_state == EICON_STATE_WMCONN)) {
+ if (chan->e.B2Id) idi_do_req(card, chan, IDI_N_DISC, 1);
+ }
+ if (chan->e.B2Id) idi_do_req(card, chan, REMOVE, 1);
+ idi_do_req(card, chan, HANGUP, 0);
+ chan->fsm_state = EICON_STATE_NULL;
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_req: Ch%d: Hangup\n", chan->No);
+ return(0);
+}
+
+int
+idi_connect_res(eicon_card *card, eicon_chan *chan)
+{
+ chan->fsm_state = EICON_STATE_IWAIT;
+ idi_do_req(card, chan, CALL_RES, 0);
+ idi_do_req(card, chan, ASSIGN, 1);
+ return(0);
+}
+
+int
+idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone,
+ char *eazmsn, int si1, int si2)
+{
+ int l = 0;
+ int i;
+ unsigned char tmp;
+ unsigned char bc[5];
+ unsigned char hlc[5];
+ struct sk_buff *skb;
+ struct sk_buff *skb2;
+ eicon_REQ *reqbuf;
+ eicon_chan_ptr *chan2;
+
+ skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
+ skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
+
+ if ((!skb) || (!skb2)) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No);
+ return -ENOMEM;
+ }
+
+ chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
+ chan2->ptr = chan;
+
+ reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
+ reqbuf->Req = CALL_REQ;
+ reqbuf->ReqCh = 0;
+ reqbuf->ReqId = 1;
+
+ reqbuf->XBuffer.P[l++] = CPN;
+ reqbuf->XBuffer.P[l++] = strlen(phone) + 1;
+ reqbuf->XBuffer.P[l++] = 0xc1;
+ for(i=0; i<strlen(phone);i++)
+ reqbuf->XBuffer.P[l++] = phone[i];
+
+ reqbuf->XBuffer.P[l++] = OAD;
+ reqbuf->XBuffer.P[l++] = strlen(eazmsn) + 2;
+ reqbuf->XBuffer.P[l++] = 0x01;
+ reqbuf->XBuffer.P[l++] = 0x81;
+ for(i=0; i<strlen(eazmsn);i++)
+ reqbuf->XBuffer.P[l++] = eazmsn[i];
+
+ if ((tmp = idi_si2bc(si1, si2, bc, hlc)) > 0) {
+ reqbuf->XBuffer.P[l++] = BC;
+ reqbuf->XBuffer.P[l++] = tmp;
+ for(i=0; i<tmp;i++)
+ reqbuf->XBuffer.P[l++] = bc[i];
+ if ((tmp=hlc[0])) {
+ reqbuf->XBuffer.P[l++] = HLC;
+ reqbuf->XBuffer.P[l++] = tmp;
+ for(i=1; i<=tmp;i++)
+ reqbuf->XBuffer.P[l++] = hlc[i];
+ }
+ }
+ reqbuf->XBuffer.P[l++] = CAI;
+ reqbuf->XBuffer.P[l++] = 6;
+ reqbuf->XBuffer.P[l++] = 0x09;
+ reqbuf->XBuffer.P[l++] = 0;
+ reqbuf->XBuffer.P[l++] = 0;
+ reqbuf->XBuffer.P[l++] = 0;
+ reqbuf->XBuffer.P[l++] = 32;
+ reqbuf->XBuffer.P[l++] = 3;
+ switch(chan->l2prot) {
+ case ISDN_PROTO_L2_X75I:
+ case ISDN_PROTO_L2_X75UI:
+ case ISDN_PROTO_L2_X75BUI:
+ case ISDN_PROTO_L2_HDLC:
+ reqbuf->XBuffer.P[l-6] = 5;
+ reqbuf->XBuffer.P[l-7] = 1;
+ l -= 5;
+ break;
+ case ISDN_PROTO_L2_V11096:
+ reqbuf->XBuffer.P[l-7] = 3;
+ reqbuf->XBuffer.P[l-6] = 0x0d;
+ reqbuf->XBuffer.P[l-5] = 5;
+ reqbuf->XBuffer.P[l-4] = 0;
+ l -= 3;
+ break;
+ case ISDN_PROTO_L2_V11019:
+ reqbuf->XBuffer.P[l-7] = 3;
+ reqbuf->XBuffer.P[l-6] = 0x0d;
+ reqbuf->XBuffer.P[l-5] = 6;
+ reqbuf->XBuffer.P[l-4] = 0;
+ l -= 3;
+ break;
+ case ISDN_PROTO_L2_V11038:
+ reqbuf->XBuffer.P[l-7] = 3;
+ reqbuf->XBuffer.P[l-6] = 0x0d;
+ reqbuf->XBuffer.P[l-5] = 7;
+ reqbuf->XBuffer.P[l-4] = 0;
+ l -= 3;
+ break;
+ case ISDN_PROTO_L2_MODEM:
+ reqbuf->XBuffer.P[l-6] = 0x11;
+ reqbuf->XBuffer.P[l-5] = 7;
+ reqbuf->XBuffer.P[l-4] = 0;
+ reqbuf->XBuffer.P[l-3] = 0;
+ reqbuf->XBuffer.P[l-2] = 128;
+ reqbuf->XBuffer.P[l-1] = 0;
+ break;
+ }
+
+ reqbuf->XBuffer.P[l++] = 0; /* end */
+ reqbuf->XBuffer.length = l;
+ reqbuf->Reference = 0; /* Sig Entity */
+
+ skb_queue_tail(&chan->e.X, skb);
+ skb_queue_tail(&card->sndq, skb2);
+ eicon_schedule_tx(card);
+
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_req: Ch%d: Conn_Req %s -> %s\n",chan->No, eazmsn, phone);
+ return(0);
+}
+
+
+void
+idi_IndParse(eicon_card *ccard, eicon_chan *chan, idi_ind_message *message, unsigned char *buffer, int len)
+{
+ int i,j;
+ int pos = 0;
+ int codeset = 0;
+ int wlen = 0;
+ int lock = 0;
+ __u8 w;
+ __u16 code;
+ isdn_ctrl cmd;
+
+ memset(message, 0, sizeof(idi_ind_message));
+
+ if ((!len) || (!buffer[pos])) return;
+ while(pos <= len) {
+ w = buffer[pos++];
+ if (!w) return;
+ if (w & 0x80) {
+ wlen = 0;
+ }
+ else {
+ wlen = buffer[pos++];
+ }
+
+ if (pos > len) return;
+
+ if (lock & 0x80) lock &= 0x7f;
+ else codeset = lock;
+
+ if((w&0xf0) == SHIFT) {
+ codeset = w;
+ if(!(codeset & 0x08)) lock = codeset & 7;
+ codeset &= 7;
+ lock |= 0x80;
+ }
+ else {
+ if (w==ESC && wlen >=2) {
+ code = buffer[pos++]|0x800;
+ wlen--;
+ }
+ else code = w;
+ code |= (codeset<<8);
+
+ switch(code) {
+ case OAD:
+ j = 1;
+ if (wlen) {
+ message->plan = buffer[pos++];
+ if (message->plan &0x80)
+ message->screen = 0;
+ else {
+ message->screen = buffer[pos++];
+ j = 2;
+ }
+ }
+ for(i=0; i < wlen-j; i++)
+ message->oad[i] = buffer[pos++];
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: OAD=(0x%02x,0x%02x) %s\n", chan->No,
+ message->plan, message->screen, message->oad);
+ break;
+ case RDN:
+ j = 1;
+ if (wlen) {
+ if (!(buffer[pos++] & 0x80)) {
+ pos++;
+ j = 2;
+ }
+ }
+ for(i=0; i < wlen-j; i++)
+ message->rdn[i] = buffer[pos++];
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: RDN= %s\n", chan->No,
+ message->rdn);
+ break;
+ case CPN:
+ for(i=0; i < wlen; i++)
+ message->cpn[i] = buffer[pos++];
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: CPN=(0x%02x) %s\n", chan->No,
+ (__u8)message->cpn[0], message->cpn + 1);
+ break;
+ case DSA:
+ pos++;
+ for(i=0; i < wlen-1; i++)
+ message->dsa[i] = buffer[pos++];
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: DSA=%s\n", chan->No, message->dsa);
+ break;
+ case OSA:
+ pos++;
+ for(i=0; i < wlen-1; i++)
+ message->osa[i] = buffer[pos++];
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: OSA=%s\n", chan->No, message->osa);
+ break;
+ case BC:
+ for(i=0; i < wlen; i++)
+ message->bc[i] = buffer[pos++];
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: BC = 0x%02x 0x%02x 0x%02x\n", chan->No,
+ message->bc[0],message->bc[1],message->bc[2]);
+ break;
+ case 0x800|BC:
+ for(i=0; i < wlen; i++)
+ message->e_bc[i] = buffer[pos++];
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: ESC/BC=%d\n", chan->No, message->bc[0]);
+ break;
+ case LLC:
+ for(i=0; i < wlen; i++)
+ message->llc[i] = buffer[pos++];
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: LLC=%d %d %d %d\n", chan->No, message->llc[0],
+ message->llc[1],message->llc[2],message->llc[3]);
+ break;
+ case HLC:
+ for(i=0; i < wlen; i++)
+ message->hlc[i] = buffer[pos++];
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: HLC=%x %x %x %x %x\n", chan->No,
+ message->hlc[0], message->hlc[1],
+ message->hlc[2], message->hlc[3], message->hlc[4]);
+ break;
+ case DSP:
+ case 0x600|DSP:
+ for(i=0; i < wlen; i++)
+ message->display[i] = buffer[pos++];
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: Display: %s\n", chan->No,
+ message->display);
+ break;
+ case 0x600|KEY:
+ for(i=0; i < wlen; i++)
+ message->keypad[i] = buffer[pos++];
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: Keypad: %s\n", chan->No,
+ message->keypad);
+ break;
+ case NI:
+ case 0x600|NI:
+ if (wlen) {
+ if (DebugVar & 4) {
+ switch(buffer[pos] & 127) {
+ case 0:
+ printk(KERN_DEBUG"idi_inf: Ch%d: User suspended.\n", chan->No);
+ break;
+ case 1:
+ printk(KERN_DEBUG"idi_inf: Ch%d: User resumed.\n", chan->No);
+ break;
+ case 2:
+ printk(KERN_DEBUG"idi_inf: Ch%d: Bearer service change.\n", chan->No);
+ break;
+ default:
+ printk(KERN_DEBUG"idi_inf: Ch%d: Unknown Notification %x.\n",
+ chan->No, buffer[pos] & 127);
+ }
+ }
+ pos += wlen;
+ }
+ break;
+ case PI:
+ case 0x600|PI:
+ if (wlen > 1) {
+ if (DebugVar & 4) {
+ switch(buffer[pos+1] & 127) {
+ case 1:
+ printk(KERN_DEBUG"idi_inf: Ch%d: Call is not end-to-end ISDN.\n", chan->No);
+ break;
+ case 2:
+ printk(KERN_DEBUG"idi_inf: Ch%d: Destination address is non ISDN.\n", chan->No);
+ break;
+ case 3:
+ printk(KERN_DEBUG"idi_inf: Ch%d: Origination address is non ISDN.\n", chan->No);
+ break;
+ case 4:
+ printk(KERN_DEBUG"idi_inf: Ch%d: Call has returned to the ISDN.\n", chan->No);
+ break;
+ case 5:
+ printk(KERN_DEBUG"idi_inf: Ch%d: Interworking has occurred.\n", chan->No);
+ break;
+ case 8:
+ printk(KERN_DEBUG"idi_inf: Ch%d: In-band information available.\n", chan->No);
+ break;
+ default:
+ printk(KERN_DEBUG"idi_inf: Ch%d: Unknown Progress %x.\n",
+ chan->No, buffer[pos+1] & 127);
+ }
+ }
+ }
+ pos += wlen;
+ break;
+ case CAU:
+ for(i=0; i < wlen; i++)
+ message->cau[i] = buffer[pos++];
+ memcpy(&chan->cause, &message->cau, 2);
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: CAU=%d %d\n", chan->No,
+ message->cau[0],message->cau[1]);
+ break;
+ case 0x800|CAU:
+ for(i=0; i < wlen; i++)
+ message->e_cau[i] = buffer[pos++];
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: ECAU=%d %d\n", chan->No,
+ message->e_cau[0],message->e_cau[1]);
+ break;
+ case 0x800|CHI:
+ for(i=0; i < wlen; i++)
+ message->e_chi[i] = buffer[pos++];
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: ESC/CHI=%d\n", chan->No,
+ message->e_cau[0]);
+ break;
+ case 0x800|0x7a:
+ pos ++;
+ message->e_mt=buffer[pos++];
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: EMT=0x%x\n", chan->No, message->e_mt);
+ break;
+ case DT:
+ for(i=0; i < wlen; i++)
+ message->dt[i] = buffer[pos++];
+ if (DebugVar & 4)
+ printk(KERN_DEBUG"idi_inf: Ch%d: DT: %02d.%02d.%02d %02d:%02d:%02d\n", chan->No,
+ message->dt[2], message->dt[1], message->dt[0],
+ message->dt[3], message->dt[4], message->dt[5]);
+ break;
+ case 0x600|SIN:
+ for(i=0; i < wlen; i++)
+ message->sin[i] = buffer[pos++];
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: SIN=%d %d\n", chan->No,
+ message->sin[0],message->sin[1]);
+ break;
+ case 0x600|CPS:
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: Called Party Status in ind\n", chan->No);
+ pos += wlen;
+ break;
+ case 0x600|CIF:
+ for (i = 0; i < wlen; i++)
+ if (buffer[pos + i] != '0') break;
+ memcpy(&cmd.parm.num, &buffer[pos + i], wlen - i);
+ cmd.parm.num[wlen - i] = 0;
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: CIF=%s\n", chan->No, cmd.parm.num);
+ pos += wlen;
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_CINF;
+ cmd.arg = chan->No;
+ ccard->interface.statcallb(&cmd);
+ break;
+ case 0x600|DATE:
+ if (DebugVar & 2)
+ printk(KERN_DEBUG"idi_inf: Ch%d: Date in ind\n", chan->No);
+ pos += wlen;
+ break;
+ case 0xe08:
+ case 0xe7a:
+ case 0xe04:
+ case 0xe00:
+ /* *** TODO *** */
+ case CHA:
+ /* Charge advice */
+ case FTY:
+ case 0x600|FTY:
+ case CHI:
+ case 0x800:
+ /* Not yet interested in this */
+ pos += wlen;
+ break;
+ case 0x880:
+ /* Managment Information Element */
+ if (!manbuf) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING"idi_err: manbuf not allocated\n");
+ }
+ else {
+ memcpy(&manbuf->data[manbuf->pos], &buffer[pos], wlen);
+ manbuf->length[manbuf->count] = wlen;
+ manbuf->count++;
+ manbuf->pos += wlen;
+ }
+ pos += wlen;
+ break;
+ default:
+ pos += wlen;
+ if (DebugVar & 6)
+ printk(KERN_WARNING"idi_inf: Ch%d: unknown information element 0x%x in ind, len:%x\n",
+ chan->No, code, wlen);
+ }
+ }
+ }
+}
+
+void
+idi_bc2si(unsigned char *bc, unsigned char *hlc, unsigned char *si1, unsigned char *si2)
+{
+ si1[0] = 0;
+ si2[0] = 0;
+ if (memcmp(bc, BC_Speech, 3) == 0) { /* Speech */
+ si1[0] = 1;
+#ifdef EICON_FULL_SERVICE_OKTETT
+ si2[0] = 1;
+#endif
+ }
+ if (memcmp(bc, BC_31khz, 3) == 0) { /* 3.1kHz audio */
+ si1[0] = 1;
+#ifdef EICON_FULL_SERVICE_OKTETT
+ si2[0] = 2;
+ if (memcmp(hlc, HLC_faxg3, 2) == 0) { /* Fax Gr.2/3 */
+ si1[0] = 2;
+ }
+#endif
+ }
+ if (memcmp(bc, BC_64k, 2) == 0) { /* unrestricted 64 kbits */
+ si1[0] = 7;
+ }
+ if (memcmp(bc, BC_video, 3) == 0) { /* video */
+ si1[0] = 4;
+ }
+}
+
+void
+idi_parse_udata(eicon_card *ccard, eicon_chan *chan, unsigned char *buffer, int len)
+{
+ isdn_ctrl cmd;
+ eicon_dsp_ind *p = (eicon_dsp_ind *) (&buffer[1]);
+ static char *connmsg[] =
+ {"", "V.21", "V.23", "V.22", "V.22bis", "V.32bis", "V.34",
+ "V.8", "Bell 212A", "Bell 103", "V.29 Leased", "V.33 Leased", "V.90",
+ "V.21 CH2", "V.27ter", "V.29", "V.33", "V.17"};
+
+ switch (buffer[0]) {
+ case DSP_UDATA_INDICATION_SYNC:
+ if (DebugVar & 16)
+ printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_SYNC time %d\n", chan->No, p->time);
+ break;
+ case DSP_UDATA_INDICATION_DCD_OFF:
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DCD_OFF time %d\n", chan->No, p->time);
+ break;
+ case DSP_UDATA_INDICATION_DCD_ON:
+ if ((chan->l2prot == ISDN_PROTO_L2_MODEM) &&
+ (chan->fsm_state == EICON_STATE_WMCONN)) {
+ chan->fsm_state = EICON_STATE_ACTIVE;
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_BCONN;
+ cmd.arg = chan->No;
+ sprintf(cmd.parm.num, "%d/%s", p->speed, connmsg[p->norm]);
+ ccard->interface.statcallb(&cmd);
+ }
+ if (DebugVar & 8) {
+ printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DCD_ON time %d\n", chan->No, p->time);
+ printk(KERN_DEBUG"idi_ind: Ch%d: %d %d %d %d\n", chan->No,
+ p->norm, p->options, p->speed, p->delay);
+ }
+ break;
+ case DSP_UDATA_INDICATION_CTS_OFF:
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_CTS_OFF time %d\n", chan->No, p->time);
+ break;
+ case DSP_UDATA_INDICATION_CTS_ON:
+ if (DebugVar & 8) {
+ printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_CTS_ON time %d\n", chan->No, p->time);
+ printk(KERN_DEBUG"idi_ind: Ch%d: %d %d %d %d\n", chan->No,
+ p->norm, p->options, p->speed, p->delay);
+ }
+ break;
+ case DSP_UDATA_INDICATION_DISCONNECT:
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_ind: Ch%d: UDATA_DISCONNECT cause %d\n", chan->No, buffer[1]);
+ break;
+ default:
+ if (DebugVar & 8)
+ printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED UDATA Indication 0x%02x\n", chan->No, buffer[0]);
+ }
+}
+
+void
+idi_handle_ind(eicon_card *ccard, struct sk_buff *skb)
+{
+ int tmp;
+ int free_buff;
+ struct sk_buff *skb2;
+ eicon_IND *ind = (eicon_IND *)skb->data;
+ eicon_chan *chan;
+ idi_ind_message message;
+ isdn_ctrl cmd;
+
+ if ((chan = ccard->IdTable[ind->IndId]) == NULL) {
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ if ((DebugVar & 128) ||
+ ((DebugVar & 16) && (ind->Ind != 8))) {
+ printk(KERN_DEBUG "idi_hdl: Ch%d: Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n", chan->No,
+ ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length);
+ }
+
+ free_buff = 1;
+ /* Signal Layer */
+ if (chan->e.D3Id == ind->IndId) {
+ idi_IndParse(ccard, chan, &message, ind->RBuffer.P, ind->RBuffer.length);
+ switch(ind->Ind) {
+ case HANGUP:
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_ind: Ch%d: Hangup\n", chan->No);
+ while((skb2 = skb_dequeue(&chan->e.X))) {
+ dev_kfree_skb(skb2);
+ }
+ chan->e.busy = 0;
+ chan->queued = 0;
+ chan->waitq = 0;
+ chan->waitpq = 0;
+ chan->fsm_state = EICON_STATE_NULL;
+ if (message.e_cau[0] & 0x7f) {
+ cmd.driver = ccard->myid;
+ cmd.arg = chan->No;
+ sprintf(cmd.parm.num,"E%02x%02x",
+ chan->cause[0]&0x7f, message.e_cau[0]&0x7f);
+ cmd.command = ISDN_STAT_CAUSE;
+ ccard->interface.statcallb(&cmd);
+ }
+ chan->cause[0] = 0;
+ cmd.driver = ccard->myid;
+ cmd.arg = chan->No;
+ cmd.command = ISDN_STAT_DHUP;
+ ccard->interface.statcallb(&cmd);
+ eicon_idi_listen_req(ccard, chan);
+ break;
+ case INDICATE_IND:
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_ind: Ch%d: Indicate_Ind\n", chan->No);
+ chan->fsm_state = EICON_STATE_ICALL;
+ idi_bc2si(message.bc, message.hlc, &chan->si1, &chan->si2);
+ strcpy(chan->cpn, message.cpn + 1);
+ if (strlen(message.dsa)) {
+ strcat(chan->cpn, ".");
+ strcat(chan->cpn, message.dsa);
+ }
+ strcpy(chan->oad, message.oad);
+ try_stat_icall_again:
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_ICALL;
+ cmd.arg = chan->No;
+ cmd.parm.setup.si1 = chan->si1;
+ cmd.parm.setup.si2 = chan->si2;
+ strcpy(cmd.parm.setup.eazmsn, chan->cpn);
+ strcpy(cmd.parm.setup.phone, chan->oad);
+ cmd.parm.setup.plan = message.plan;
+ cmd.parm.setup.screen = message.screen;
+ tmp = ccard->interface.statcallb(&cmd);
+ switch(tmp) {
+ case 0: /* no user responding */
+ idi_do_req(ccard, chan, HANGUP, 0);
+ break;
+ case 1: /* alert */
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_req: Ch%d: Call Alert\n", chan->No);
+ if ((chan->fsm_state == EICON_STATE_ICALL) || (chan->fsm_state == EICON_STATE_ICALLW)) {
+ chan->fsm_state = EICON_STATE_ICALL;
+ idi_do_req(ccard, chan, CALL_ALERT, 0);
+ }
+ break;
+ case 2: /* reject */
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_req: Ch%d: Call Reject\n", chan->No);
+ idi_do_req(ccard, chan, REJECT, 0);
+ break;
+ case 3: /* incomplete number */
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_req: Ch%d: Incomplete Number\n", chan->No);
+ switch(ccard->type) {
+ case EICON_CTYPE_MAESTRAP:
+ case EICON_CTYPE_S2M:
+ /* TODO (other protocols) */
+ chan->fsm_state = EICON_STATE_ICALLW;
+ break;
+ default:
+ idi_do_req(ccard, chan, HANGUP, 0);
+ }
+ break;
+ }
+ break;
+ case INFO_IND:
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_ind: Ch%d: Info_Ind\n", chan->No);
+ if ((chan->fsm_state == EICON_STATE_ICALLW) &&
+ (message.cpn[0])) {
+ strcat(chan->cpn, message.cpn + 1);
+ goto try_stat_icall_again;
+ }
+ break;
+ case CALL_IND:
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_ind: Ch%d: Call_Ind\n", chan->No);
+ if ((chan->fsm_state == EICON_STATE_ICALL) || (chan->fsm_state == EICON_STATE_IWAIT)) {
+ chan->fsm_state = EICON_STATE_IBWAIT;
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_DCONN;
+ cmd.arg = chan->No;
+ ccard->interface.statcallb(&cmd);
+ idi_do_req(ccard, chan, IDI_N_CONNECT, 1);
+ } else
+ idi_hangup(ccard, chan);
+ break;
+ case CALL_CON:
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_ind: Ch%d: Call_Con\n", chan->No);
+ if (chan->fsm_state == EICON_STATE_OCALL) {
+ chan->fsm_state = EICON_STATE_OBWAIT;
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_DCONN;
+ cmd.arg = chan->No;
+ ccard->interface.statcallb(&cmd);
+ idi_do_req(ccard, chan, ASSIGN, 1);
+ idi_do_req(ccard, chan, IDI_N_CONNECT, 1);
+ } else
+ idi_hangup(ccard, chan);
+ break;
+ case AOC_IND:
+ if (DebugVar & 8)
+ printk(KERN_DEBUG"idi_ind: Ch%d: Advice of Charge\n", chan->No);
+ break;
+ default:
+ if (DebugVar & 8)
+ printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED SigIndication 0x%02x\n", chan->No, ind->Ind);
+ }
+ }
+ /* Network Layer */
+ else if (chan->e.B2Id == ind->IndId) {
+
+ if (chan->No == ccard->nchannels) {
+ /* Management Indication */
+ idi_IndParse(ccard, chan, &message, ind->RBuffer.P, ind->RBuffer.length);
+ chan->fsm_state = 1;
+ }
+ else
+ switch(ind->Ind) {
+ case IDI_N_CONNECT_ACK:
+ if (DebugVar & 16)
+ printk(KERN_DEBUG"idi_ind: Ch%d: N_Connect_Ack\n", chan->No);
+ if (chan->l2prot == ISDN_PROTO_L2_MODEM) {
+ chan->fsm_state = EICON_STATE_WMCONN;
+ break;
+ }
+ chan->fsm_state = EICON_STATE_ACTIVE;
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_BCONN;
+ cmd.arg = chan->No;
+ ccard->interface.statcallb(&cmd);
+ break;
+ case IDI_N_CONNECT:
+ if (DebugVar & 16)
+ printk(KERN_DEBUG"idi_ind: Ch%d: N_Connect\n", chan->No);
+ if (chan->e.B2Id) idi_do_req(ccard, chan, IDI_N_CONNECT_ACK, 1);
+ if (chan->l2prot == ISDN_PROTO_L2_MODEM) {
+ chan->fsm_state = EICON_STATE_WMCONN;
+ break;
+ }
+ chan->fsm_state = EICON_STATE_ACTIVE;
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_BCONN;
+ cmd.arg = chan->No;
+ ccard->interface.statcallb(&cmd);
+ break;
+ case IDI_N_DISC:
+ if (DebugVar & 16)
+ printk(KERN_DEBUG"idi_ind: Ch%d: N_DISC\n", chan->No);
+ if (chan->e.B2Id) {
+ idi_do_req(ccard, chan, IDI_N_DISC_ACK, 1);
+ idi_do_req(ccard, chan, REMOVE, 1);
+ }
+ chan->queued = 0;
+ chan->waitq = 0;
+ chan->waitpq = 0;
+ if (chan->fsm_state == EICON_STATE_ACTIVE) {
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_BHUP;
+ cmd.arg = chan->No;
+ ccard->interface.statcallb(&cmd);
+ }
+ break;
+ case IDI_N_DISC_ACK:
+ if (DebugVar & 16)
+ printk(KERN_DEBUG"idi_ind: Ch%d: N_DISC_ACK\n", chan->No);
+ break;
+ case IDI_N_DATA_ACK:
+ if (DebugVar & 16)
+ printk(KERN_DEBUG"idi_ind: Ch%d: N_DATA_ACK\n", chan->No);
+ break;
+ case IDI_N_DATA:
+ skb_pull(skb, sizeof(eicon_IND) - 1);
+ if (DebugVar & 128)
+ printk(KERN_DEBUG"idi_rcv: Ch%d: %d bytes\n", chan->No, skb->len);
+ ccard->interface.rcvcallb_skb(ccard->myid, chan->No, skb);
+ free_buff = 0;
+ break;
+ case IDI_N_UDATA:
+ idi_parse_udata(ccard, chan, ind->RBuffer.P, ind->RBuffer.length);
+ break;
+ default:
+ if (DebugVar & 8)
+ printk(KERN_WARNING "idi_ind: Ch%d: UNHANDLED NetIndication 0x%02x\n", chan->No, ind->Ind);
+ }
+ }
+ else {
+ if (DebugVar & 1)
+ printk(KERN_ERR "idi_ind: Ch%d: Ind is neither SIG nor NET !\n", chan->No);
+ }
+ if (free_buff) dev_kfree_skb(skb);
+}
+
+void
+idi_handle_ack(eicon_card *ccard, struct sk_buff *skb)
+{
+ int j;
+ eicon_RC *ack = (eicon_RC *)skb->data;
+ eicon_chan *chan;
+ isdn_ctrl cmd;
+
+ if ((ack->Rc != ASSIGN_OK) && (ack->Rc != OK)) {
+ if ((chan = ccard->IdTable[ack->RcId]) != NULL) {
+ chan->e.busy = 0;
+ if (DebugVar & 24)
+ printk(KERN_ERR "eicon_ack: Ch%d: Not OK: Rc=%d Id=%d Ch=%d\n", chan->No,
+ ack->Rc, ack->RcId, ack->RcCh);
+ if (chan->No == ccard->nchannels) { /* Management */
+ chan->fsm_state = 2;
+ } else { /* any other channel */
+ /* card reports error: we hangup */
+ idi_hangup(ccard, chan);
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.arg = chan->No;
+ ccard->interface.statcallb(&cmd);
+ }
+ }
+ }
+ else {
+ if ((chan = ccard->IdTable[ack->RcId]) != NULL) {
+ if (ack->RcId != ((chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id)) {
+ if (DebugVar & 16)
+ printk(KERN_DEBUG "idi_ack: Ch%d: RcId %d not equal to last %d\n", chan->No,
+ ack->RcId, (chan->e.ReqCh) ? chan->e.B2Id : chan->e.D3Id);
+ } else {
+ if (chan->No == ccard->nchannels) { /* Management */
+ if (chan->e.Req == 0x04) chan->fsm_state = 1;
+ }
+ if (chan->e.ReqCh) {
+ switch(chan->e.Req & 0x0f) {
+ case IDI_N_MDATA:
+ case IDI_N_DATA:
+ chan->queued -= chan->waitq;
+ if (chan->queued < 0) chan->queued = 0;
+ if ((chan->e.Req & 0x0f) == IDI_N_DATA) {
+ cmd.driver = ccard->myid;
+ cmd.command = ISDN_STAT_BSENT;
+ cmd.arg = chan->No;
+ cmd.parm.length = chan->waitpq;
+ chan->waitpq = 0;
+ ccard->interface.statcallb(&cmd);
+ }
+ break;
+ default:
+ if (DebugVar & 16)
+ printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%d Ch=%d (ref:%d)\n", chan->No,
+ ack->RcId, ack->RcCh, ack->Reference);
+ }
+ }
+ else {
+ if (DebugVar & 16)
+ printk(KERN_DEBUG "idi_ack: Ch%d: RC OK Id=%d Ch=%d (ref:%d)\n", chan->No,
+ ack->RcId, ack->RcCh, ack->Reference);
+ }
+
+ if (chan->e.Req == REMOVE) {
+ if (ack->Reference == chan->e.ref) {
+ ccard->IdTable[ack->RcId] = NULL;
+ if (DebugVar & 16)
+ printk(KERN_DEBUG "idi_ack: Ch%d: Removed : Id=%d Ch=%d (%s)\n", chan->No,
+ ack->RcId, ack->RcCh, (chan->e.ReqCh)? "Net":"Sig");
+ if (!chan->e.ReqCh)
+ chan->e.D3Id = 0;
+ else
+ chan->e.B2Id = 0;
+ }
+ else {
+ if (DebugVar & 16)
+ printk(KERN_DEBUG "idi_ack: Ch%d: Rc-Ref %d not equal to stored %d\n", chan->No,
+ ack->Reference, chan->e.ref);
+ }
+ }
+ chan->e.busy = 0;
+ }
+ }
+ else {
+ for(j = 0; j < ccard->nchannels + 1; j++) {
+ if (ccard->bch[j].e.ref == ack->Reference) {
+ if (!ccard->bch[j].e.ReqCh)
+ ccard->bch[j].e.D3Id = ack->RcId;
+ else
+ ccard->bch[j].e.B2Id = ack->RcId;
+ ccard->IdTable[ack->RcId] = &ccard->bch[j];
+ ccard->bch[j].e.busy = 0;
+ ccard->bch[j].e.ref = 0;
+ if (DebugVar & 16)
+ printk(KERN_DEBUG"idi_ack: Ch%d: Id %d assigned (%s)\n", j,
+ ack->RcId, (ccard->bch[j].e.ReqCh)? "Net":"Sig");
+ break;
+ }
+ }
+ if (j > ccard->nchannels) {
+ if (DebugVar & 24)
+ printk(KERN_DEBUG"idi_ack: Ch??: ref %d not found for Id %d\n",
+ ack->Reference, ack->RcId);
+ }
+ }
+ }
+ dev_kfree_skb(skb);
+ eicon_schedule_tx(ccard);
+}
+
+int
+idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb)
+{
+ struct sk_buff *xmit_skb;
+ struct sk_buff *skb2;
+ eicon_REQ *reqbuf;
+ eicon_chan_ptr *chan2;
+ int len, plen = 0, offset = 0;
+ unsigned long flags;
+
+ if (chan->fsm_state != EICON_STATE_ACTIVE) {
+ if (DebugVar & 1)
+ printk(KERN_DEBUG"idi_snd: Ch%d: send bytes on state %d !\n", chan->No, chan->fsm_state);
+ return -ENODEV;
+ }
+
+ len = skb->len;
+ if (len > 2138) /* too much for the shared memory */
+ return -1;
+ if (!len)
+ return 0;
+ if (chan->queued + len > ((chan->l2prot == ISDN_PROTO_L2_TRANS) ? 4000 : EICON_MAX_QUEUED))
+ return 0;
+ if (DebugVar & 128)
+ printk(KERN_DEBUG"idi_snd: Ch%d: %d bytes\n", chan->No, len);
+ save_flags(flags);
+ cli();
+ while(offset < len) {
+
+ plen = ((len - offset) > 270) ? 270 : len - offset;
+
+ xmit_skb = alloc_skb(plen + sizeof(eicon_REQ), GFP_ATOMIC);
+ skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
+
+ if ((!skb) || (!skb2)) {
+ restore_flags(flags);
+ if (DebugVar & 1)
+ printk(KERN_WARNING "idi_err: Ch%d: alloc_skb failed\n", chan->No);
+ return -ENOMEM;
+ }
+
+ chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
+ chan2->ptr = chan;
+
+ reqbuf = (eicon_REQ *)skb_put(xmit_skb, plen + sizeof(eicon_REQ));
+ if (((len - offset) > 270) &&
+ (chan->l2prot != ISDN_PROTO_L2_TRANS)) {
+ reqbuf->Req = IDI_N_MDATA;
+ } else {
+ reqbuf->Req = IDI_N_DATA;
+ if (ack) reqbuf->Req |= N_D_BIT;
+ }
+ reqbuf->ReqCh = 0;
+ reqbuf->ReqId = 1;
+ memcpy(&reqbuf->XBuffer.P, skb->data + offset, plen);
+ reqbuf->XBuffer.length = plen;
+ reqbuf->Reference = 1; /* Net Entity */
+
+ skb_queue_tail(&chan->e.X, xmit_skb);
+ skb_queue_tail(&card->sndq, skb2);
+
+ offset += plen;
+ }
+ chan->queued += len;
+ restore_flags(flags);
+ eicon_schedule_tx(card);
+ dev_kfree_skb(skb);
+ return len;
+}
+
+
+
+int
+eicon_idi_manage_assign(eicon_card *card)
+{
+ struct sk_buff *skb;
+ struct sk_buff *skb2;
+ eicon_REQ *reqbuf;
+ eicon_chan *chan;
+ eicon_chan_ptr *chan2;
+
+ chan = &(card->bch[card->nchannels]);
+
+ skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
+ skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
+
+ if ((!skb) || (!skb2)) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "idi_err: alloc_skb failed\n");
+ return -ENOMEM;
+ }
+
+ chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
+ chan2->ptr = chan;
+
+ reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
+
+ reqbuf->XBuffer.P[0] = 0;
+ reqbuf->Req = ASSIGN;
+ reqbuf->ReqCh = 0;
+ reqbuf->ReqId = 0xe0;
+ reqbuf->XBuffer.length = 1;
+ reqbuf->Reference = 2; /* Man Entity */
+
+ skb_queue_tail(&chan->e.X, skb);
+ skb_queue_tail(&card->sndq, skb2);
+ eicon_schedule_tx(card);
+ return(0);
+}
+
+
+int
+eicon_idi_manage_remove(eicon_card *card)
+{
+ struct sk_buff *skb;
+ struct sk_buff *skb2;
+ eicon_REQ *reqbuf;
+ eicon_chan *chan;
+ eicon_chan_ptr *chan2;
+
+ chan = &(card->bch[card->nchannels]);
+
+ skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
+ skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
+
+ if ((!skb) || (!skb2)) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "idi_err: alloc_skb failed\n");
+ return -ENOMEM;
+ }
+
+ chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
+ chan2->ptr = chan;
+
+ reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
+
+ reqbuf->Req = REMOVE;
+ reqbuf->ReqCh = 0;
+ reqbuf->ReqId = 1;
+ reqbuf->XBuffer.length = 0;
+ reqbuf->Reference = 2; /* Man Entity */
+
+ skb_queue_tail(&chan->e.X, skb);
+ skb_queue_tail(&card->sndq, skb2);
+ eicon_schedule_tx(card);
+ return(0);
+}
+
+
+int
+eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb)
+{
+ int l = 0;
+ int ret = 0;
+ int timeout;
+ int i;
+ struct sk_buff *skb;
+ struct sk_buff *skb2;
+ eicon_REQ *reqbuf;
+ eicon_chan *chan;
+ eicon_chan_ptr *chan2;
+
+ chan = &(card->bch[card->nchannels]);
+
+ if (chan->e.D3Id) return -EBUSY;
+ chan->e.D3Id = 1;
+ while((skb2 = skb_dequeue(&chan->e.X)))
+ dev_kfree_skb(skb2);
+ chan->e.busy = 0;
+
+ if ((ret = eicon_idi_manage_assign(card))) {
+ chan->e.D3Id = 0;
+ return(ret);
+ }
+
+ timeout = jiffies + 50;
+ while (timeout > jiffies) {
+ if (chan->e.B2Id) break;
+ SLEEP(10);
+ }
+ if (!chan->e.B2Id) {
+ chan->e.D3Id = 0;
+ return -EIO;
+ }
+
+ chan->fsm_state = 0;
+
+ if (!(manbuf = kmalloc(sizeof(eicon_manifbuf), GFP_KERNEL))) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "idi_err: alloc_manifbuf failed\n");
+ chan->e.D3Id = 0;
+ return -ENOMEM;
+ }
+ if (copy_from_user(manbuf, mb, sizeof(eicon_manifbuf))) {
+ chan->e.D3Id = 0;
+ return -EFAULT;
+ }
+
+ skb = alloc_skb(270 + sizeof(eicon_REQ), GFP_ATOMIC);
+ skb2 = alloc_skb(sizeof(eicon_chan_ptr), GFP_ATOMIC);
+
+ if ((!skb) || (!skb2)) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "idi_err_manif: alloc_skb failed\n");
+ kfree(manbuf);
+ chan->e.D3Id = 0;
+ return -ENOMEM;
+ }
+
+ chan2 = (eicon_chan_ptr *)skb_put(skb2, sizeof(eicon_chan_ptr));
+ chan2->ptr = chan;
+
+ reqbuf = (eicon_REQ *)skb_put(skb, 270 + sizeof(eicon_REQ));
+
+ reqbuf->XBuffer.P[l++] = ESC;
+ reqbuf->XBuffer.P[l++] = 6;
+ reqbuf->XBuffer.P[l++] = 0x80;
+ for (i = 0; i < manbuf->length[0]; i++)
+ reqbuf->XBuffer.P[l++] = manbuf->data[i];
+ reqbuf->XBuffer.P[1] = manbuf->length[0] + 1;
+
+ reqbuf->XBuffer.P[l++] = 0;
+ reqbuf->Req = (manbuf->count) ? manbuf->count : 0x02; /* Request */
+ reqbuf->ReqCh = 0;
+ reqbuf->ReqId = 1;
+ reqbuf->XBuffer.length = l;
+ reqbuf->Reference = 2; /* Man Entity */
+
+ skb_queue_tail(&chan->e.X, skb);
+ skb_queue_tail(&card->sndq, skb2);
+
+ manbuf->count = 0;
+ manbuf->pos = 0;
+
+ eicon_schedule_tx(card);
+
+ timeout = jiffies + 50;
+ while (timeout > jiffies) {
+ if (chan->fsm_state) break;
+ SLEEP(10);
+ }
+ if ((!chan->fsm_state) || (chan->fsm_state == 2)) {
+ eicon_idi_manage_remove(card);
+ kfree(manbuf);
+ chan->e.D3Id = 0;
+ return -EIO;
+ }
+
+ if ((ret = eicon_idi_manage_remove(card))) {
+ chan->e.D3Id = 0;
+ return(ret);
+ }
+
+ if (copy_to_user(mb, manbuf, sizeof(eicon_manifbuf))) {
+ chan->e.D3Id = 0;
+ return -EFAULT;
+ }
+
+ kfree(manbuf);
+ chan->e.D3Id = 0;
+ return(0);
+}
diff --git a/drivers/isdn/eicon/eicon_idi.h b/drivers/isdn/eicon/eicon_idi.h
new file mode 100644
index 000000000..a0605cdef
--- /dev/null
+++ b/drivers/isdn/eicon/eicon_idi.h
@@ -0,0 +1,248 @@
+/* $Id: eicon_idi.h,v 1.4 1999/03/29 11:19:44 armin Exp $
+ *
+ * ISDN lowlevel-module for the Eicon.Diehl active cards.
+ * IDI-Interface
+ *
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon_idi.h,v $
+ * Revision 1.4 1999/03/29 11:19:44 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.3 1999/03/02 12:37:45 armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.2 1999/01/24 20:14:18 armin
+ * Changed and added debug stuff.
+ * Better data sending. (still problems with tty's flip buffer)
+ *
+ * Revision 1.1 1999/01/01 18:09:42 armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#ifndef IDI_H
+#define IDI_H
+
+
+#define ASSIGN 0x01
+#define REMOVE 0xff
+
+#define CALL_REQ 1 /* call request */
+#define CALL_CON 1 /* call confirmation */
+#define CALL_IND 2 /* incoming call connected */
+#define LISTEN_REQ 2 /* listen request */
+#define HANGUP 3 /* hangup request/indication */
+#define SUSPEND 4 /* call suspend request/confirm */
+#define RESUME 5 /* call resume request/confirm */
+#define SUSPEND_REJ 6 /* suspend rejected indication */
+#define USER_DATA 8 /* user data for user to user signaling */
+#define CONGESTION 9 /* network congestion indication */
+#define INDICATE_REQ 10 /* request to indicate an incoming call */
+#define INDICATE_IND 10 /* indicates that there is an incoming call */
+#define CALL_RES 11 /* accept an incoming call */
+#define CALL_ALERT 12 /* send ALERT for incoming call */
+#define INFO_REQ 13 /* INFO request */
+#define INFO_IND 13 /* INFO indication */
+#define REJECT 14 /* reject an incoming call */
+#define RESOURCES 15 /* reserve B-Channel hardware resources */
+#define TEL_CTRL 16 /* Telephone control request/indication */
+#define STATUS_REQ 17 /* Request D-State (returned in INFO_IND) */
+#define FAC_REG_REQ 18 /* connection idependent fac registration */
+#define FAC_REG_ACK 19 /* fac registration acknowledge */
+#define FAC_REG_REJ 20 /* fac registration reject */
+#define CALL_COMPLETE 21/* send a CALL_PROC for incoming call */
+#define AOC_IND 26/* Advice of Charge */
+
+#define IDI_N_MDATA (0x01)
+#define IDI_N_CONNECT (0x02)
+#define IDI_N_CONNECT_ACK (0x03)
+#define IDI_N_DISC (0x04)
+#define IDI_N_DISC_ACK (0x05)
+#define IDI_N_RESET (0x06)
+#define IDI_N_RESET_ACK (0x07)
+#define IDI_N_DATA (0x08)
+#define IDI_N_EDATA (0x09)
+#define IDI_N_UDATA (0x0a)
+#define IDI_N_BDATA (0x0b)
+#define IDI_N_DATA_ACK (0x0c)
+#define IDI_N_EDATA_ACK (0x0d)
+
+#define N_Q_BIT 0x10 /* Q-bit for req/ind */
+#define N_M_BIT 0x20 /* M-bit for req/ind */
+#define N_D_BIT 0x40 /* D-bit for req/ind */
+
+
+#define SHIFT 0x90 /* codeset shift */
+#define MORE 0xa0 /* more data */
+#define CL 0xb0 /* congestion level */
+
+ /* codeset 0 */
+
+#define BC 0x04 /* Bearer Capability */
+#define CAU 0x08 /* cause */
+#define CAD 0x0c /* Connected address */
+#define CAI 0x10 /* call identity */
+#define CHI 0x18 /* channel identification */
+#define LLI 0x19 /* logical link id */
+#define CHA 0x1a /* charge advice */
+#define FTY 0x1c
+#define PI 0x1e /* Progress Indicator */
+#define NI 0x27 /* Notification Indicator */
+#define DT 0x29 /* ETSI date/time */
+#define KEY 0x2c /* keypad information element */
+#define DSP 0x28 /* display */
+#define OAD 0x6c /* origination address */
+#define OSA 0x6d /* origination sub-address */
+#define CPN 0x70 /* called party number */
+#define DSA 0x71 /* destination sub-address */
+#define RDN 0x74 /* redirecting number */
+#define LLC 0x7c /* low layer compatibility */
+#define HLC 0x7d /* high layer compatibility */
+#define UUI 0x7e /* user user information */
+#define ESC 0x7f /* escape extension */
+
+#define DLC 0x20 /* data link layer configuration */
+#define NLC 0x21 /* network layer configuration */
+
+ /* codeset 6 */
+
+#define SIN 0x01 /* service indicator */
+#define CIF 0x02 /* charging information */
+#define DATE 0x03 /* date */
+#define CPS 0x07 /* called party status */
+
+/*------------------------------------------------------------------*/
+/* return code coding */
+/*------------------------------------------------------------------*/
+
+#define UNKNOWN_COMMAND 0x01 /* unknown command */
+#define WRONG_COMMAND 0x02 /* wrong command */
+#define WRONG_ID 0x03 /* unknown task/entity id */
+#define WRONG_CH 0x04 /* wrong task/entity id */
+#define UNKNOWN_IE 0x05 /* unknown information el. */
+#define WRONG_IE 0x06 /* wrong information el. */
+#define OUT_OF_RESOURCES 0x07 /* card out of res. */
+#define N_FLOW_CONTROL 0x10 /* Flow-Control, retry */
+#define ASSIGN_RC 0xe0 /* ASSIGN acknowledgement */
+#define ASSIGN_OK 0xef /* ASSIGN OK */
+#define OK_FC 0xfc /* Flow-Control RC */
+#define READY_INT 0xfd /* Ready interrupt */
+#define TIMER_INT 0xfe /* timer interrupt */
+#define OK 0xff /* command accepted */
+
+/*------------------------------------------------------------------*/
+
+typedef struct {
+ char cpn[32];
+ char oad[32];
+ char dsa[32];
+ char osa[32];
+ __u8 plan;
+ __u8 screen;
+ __u8 sin[4];
+ __u8 chi[4];
+ __u8 e_chi[4];
+ __u8 bc[12];
+ __u8 e_bc[12];
+ __u8 llc[18];
+ __u8 hlc[5];
+ __u8 cau[4];
+ __u8 e_cau[2];
+ __u8 e_mt;
+ __u8 dt[6];
+ char display[83];
+ char keypad[35];
+ char rdn[32];
+} idi_ind_message;
+
+typedef struct {
+ __u16 next __attribute__ ((packed));
+ __u8 Req __attribute__ ((packed));
+ __u8 ReqId __attribute__ ((packed));
+ __u8 ReqCh __attribute__ ((packed));
+ __u8 Reserved1 __attribute__ ((packed));
+ __u16 Reference __attribute__ ((packed));
+ __u8 Reserved[8] __attribute__ ((packed));
+ eicon_PBUFFER XBuffer;
+} eicon_REQ;
+
+typedef struct {
+ __u16 next __attribute__ ((packed));
+ __u8 Rc __attribute__ ((packed));
+ __u8 RcId __attribute__ ((packed));
+ __u8 RcCh __attribute__ ((packed));
+ __u8 Reserved1 __attribute__ ((packed));
+ __u16 Reference __attribute__ ((packed));
+ __u8 Reserved2[8] __attribute__ ((packed));
+} eicon_RC;
+
+typedef struct {
+ __u16 next __attribute__ ((packed));
+ __u8 Ind __attribute__ ((packed));
+ __u8 IndId __attribute__ ((packed));
+ __u8 IndCh __attribute__ ((packed));
+ __u8 MInd __attribute__ ((packed));
+ __u16 MLength __attribute__ ((packed));
+ __u16 Reference __attribute__ ((packed));
+ __u8 RNR __attribute__ ((packed));
+ __u8 Reserved __attribute__ ((packed));
+ __u32 Ack __attribute__ ((packed));
+ eicon_PBUFFER RBuffer;
+} eicon_IND;
+
+typedef struct {
+ __u16 NextReq __attribute__ ((packed)); /* pointer to next Req Buffer */
+ __u16 NextRc __attribute__ ((packed)); /* pointer to next Rc Buffer */
+ __u16 NextInd __attribute__ ((packed)); /* pointer to next Ind Buffer */
+ __u8 ReqInput __attribute__ ((packed)); /* number of Req Buffers sent */
+ __u8 ReqOutput __attribute__ ((packed)); /* number of Req Buffers returned */
+ __u8 ReqReserved __attribute__ ((packed));/*number of Req Buffers reserved */
+ __u8 Int __attribute__ ((packed)); /* ISDN-P interrupt */
+ __u8 XLock __attribute__ ((packed)); /* Lock field for arbitration */
+ __u8 RcOutput __attribute__ ((packed)); /* number of Rc buffers received */
+ __u8 IndOutput __attribute__ ((packed)); /* number of Ind buffers received */
+ __u8 IMask __attribute__ ((packed)); /* Interrupt Mask Flag */
+ __u8 Reserved1[2] __attribute__ ((packed)); /* reserved field, do not use */
+ __u8 ReadyInt __attribute__ ((packed)); /* request field for ready int */
+ __u8 Reserved2[12] __attribute__ ((packed)); /* reserved field, do not use */
+ __u8 InterfaceType __attribute__ ((packed)); /* interface type 1=16K */
+ __u16 Signature __attribute__ ((packed)); /* ISDN-P initialized ind */
+ __u8 B[1]; /* buffer space for Req,Ind and Rc */
+} eicon_pr_ram;
+
+
+extern int idi_do_req(eicon_card *card, eicon_chan *chan, int cmd, int layer);
+extern int idi_hangup(eicon_card *card, eicon_chan *chan);
+extern int idi_connect_res(eicon_card *card, eicon_chan *chan);
+extern int eicon_idi_listen_req(eicon_card *card, eicon_chan *chan);
+extern int idi_connect_req(eicon_card *card, eicon_chan *chan, char *phone,
+ char *eazmsn, int si1, int si2);
+
+extern void idi_handle_ack(eicon_card *card, struct sk_buff *skb);
+extern void idi_handle_ind(eicon_card *card, struct sk_buff *skb);
+extern int eicon_idi_manage(eicon_card *card, eicon_manifbuf *mb);
+extern int idi_send_data(eicon_card *card, eicon_chan *chan, int ack, struct sk_buff *skb);
+
+#endif
diff --git a/drivers/isdn/eicon/eicon_io.c b/drivers/isdn/eicon/eicon_io.c
new file mode 100644
index 000000000..1c69d37cd
--- /dev/null
+++ b/drivers/isdn/eicon/eicon_io.c
@@ -0,0 +1,755 @@
+/* $Id: eicon_io.c,v 1.1 1999/03/29 11:19:45 armin Exp $
+ *
+ * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ * Code for communicating with hardware.
+ *
+ * Copyright 1999 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * Thanks to Eicon Technology Diehl GmbH & Co. oHG for
+ * documents, informations and hardware.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon_io.c,v $
+ * Revision 1.1 1999/03/29 11:19:45 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ *
+ */
+
+
+#include "eicon.h"
+
+void
+eicon_io_rcv_dispatch(eicon_card *ccard) {
+ struct sk_buff *skb, *skb2, *skb_new;
+ eicon_IND *ind, *ind2, *ind_new;
+ eicon_chan *chan;
+
+ if (!ccard) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "eicon_io_rcv_dispatch: NULL card!\n");
+ return;
+ }
+
+ while((skb = skb_dequeue(&ccard->rcvq))) {
+ ind = (eicon_IND *)skb->data;
+
+ if ((chan = ccard->IdTable[ind->IndId]) == NULL) {
+ if (DebugVar & 1) {
+ switch(ind->Ind) {
+ case IDI_N_DISC_ACK:
+ /* doesn't matter if this happens */
+ break;
+ default:
+ printk(KERN_ERR "idi: Indication for unknown channel Ind=%d Id=%d\n", ind->Ind, ind->IndId);
+ printk(KERN_DEBUG "idi_hdl: Ch??: Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n",
+ ind->Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,ind->RBuffer.length);
+ }
+ }
+ dev_kfree_skb(skb);
+ continue;
+ }
+
+ if (chan->e.complete) { /* check for rec-buffer chaining */
+ if (ind->MLength == ind->RBuffer.length) {
+ chan->e.complete = 1;
+ idi_handle_ind(ccard, skb);
+ continue;
+ }
+ else {
+ chan->e.complete = 0;
+ ind->Ind = ind->MInd;
+ skb_queue_tail(&chan->e.R, skb);
+ continue;
+ }
+ }
+ else {
+ if (!(skb2 = skb_dequeue(&chan->e.R))) {
+ chan->e.complete = 1;
+ if (DebugVar & 1)
+ printk(KERN_ERR "eicon: buffer incomplete, but 0 in queue\n");
+ dev_kfree_skb(skb);
+ dev_kfree_skb(skb2);
+ continue;
+ }
+ ind2 = (eicon_IND *)skb2->data;
+ skb_new = alloc_skb(((sizeof(eicon_IND)-1)+ind->RBuffer.length+ind2->RBuffer.length),
+ GFP_ATOMIC);
+ ind_new = (eicon_IND *)skb_put(skb_new,
+ ((sizeof(eicon_IND)-1)+ind->RBuffer.length+ind2->RBuffer.length));
+ ind_new->Ind = ind2->Ind;
+ ind_new->IndId = ind2->IndId;
+ ind_new->IndCh = ind2->IndCh;
+ ind_new->MInd = ind2->MInd;
+ ind_new->MLength = ind2->MLength;
+ ind_new->RBuffer.length = ind2->RBuffer.length + ind->RBuffer.length;
+ memcpy(&ind_new->RBuffer.P, &ind2->RBuffer.P, ind2->RBuffer.length);
+ memcpy((&ind_new->RBuffer.P)+ind2->RBuffer.length, &ind->RBuffer.P, ind->RBuffer.length);
+ dev_kfree_skb(skb);
+ dev_kfree_skb(skb2);
+ if (ind->MLength == ind->RBuffer.length) {
+ chan->e.complete = 2;
+ idi_handle_ind(ccard, skb_new);
+ continue;
+ }
+ else {
+ chan->e.complete = 0;
+ skb_queue_tail(&chan->e.R, skb_new);
+ continue;
+ }
+ }
+ }
+}
+
+void
+eicon_io_ack_dispatch(eicon_card *ccard) {
+ struct sk_buff *skb;
+
+ if (!ccard) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "eicon_io_ack_dispatch: NULL card!\n");
+ return;
+ }
+ while((skb = skb_dequeue(&ccard->rackq))) {
+ idi_handle_ack(ccard, skb);
+ }
+}
+
+
+/*
+ * IO-Functions for different card-types
+ */
+
+u8 ram_inb(eicon_card *card, void *adr) {
+ eicon_pci_card *pcard;
+ eicon_isa_card *icard;
+ u32 addr = (u32) adr;
+
+ pcard = &card->hwif.pci;
+ icard = &card->hwif.isa;
+
+ switch(card->type) {
+ case EICON_CTYPE_MAESTRA:
+ outw((u16)addr, (u16)pcard->PCIreg + M_ADDR);
+ return(inb((u16)pcard->PCIreg + M_DATA));
+ case EICON_CTYPE_MAESTRAP:
+ case EICON_CTYPE_S2M:
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ return(readb(addr));
+ }
+ return(0);
+}
+
+u16 ram_inw(eicon_card *card, void *adr) {
+ eicon_pci_card *pcard;
+ eicon_isa_card *icard;
+ u32 addr = (u32) adr;
+
+ pcard = &card->hwif.pci;
+ icard = &card->hwif.isa;
+
+ switch(card->type) {
+ case EICON_CTYPE_MAESTRA:
+ outw((u16)addr, (u16)pcard->PCIreg + M_ADDR);
+ return(inw((u16)pcard->PCIreg + M_DATA));
+ case EICON_CTYPE_MAESTRAP:
+ case EICON_CTYPE_S2M:
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ return(readw(addr));
+ }
+ return(0);
+}
+
+void ram_outb(eicon_card *card, void *adr, u8 data) {
+ eicon_pci_card *pcard;
+ eicon_isa_card *icard;
+ u32 addr = (u32) adr;
+
+ pcard = &card->hwif.pci;
+ icard = &card->hwif.isa;
+
+ switch(card->type) {
+ case EICON_CTYPE_MAESTRA:
+ outw((u16)addr, (u16)pcard->PCIreg + M_ADDR);
+ outb((u8)data, (u16)pcard->PCIreg + M_DATA);
+ break;
+ case EICON_CTYPE_MAESTRAP:
+ case EICON_CTYPE_S2M:
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ writeb(data, addr);
+ break;
+ }
+}
+
+void ram_outw(eicon_card *card, void *adr , u16 data) {
+ eicon_pci_card *pcard;
+ eicon_isa_card *icard;
+ u32 addr = (u32) adr;
+
+ pcard = &card->hwif.pci;
+ icard = &card->hwif.isa;
+
+ switch(card->type) {
+ case EICON_CTYPE_MAESTRA:
+ outw((u16)addr, (u16)pcard->PCIreg + M_ADDR);
+ outw((u16)data, (u16)pcard->PCIreg + M_DATA);
+ break;
+ case EICON_CTYPE_MAESTRAP:
+ case EICON_CTYPE_S2M:
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ writew(data, addr);
+ break;
+ }
+}
+
+void ram_copyfromcard(eicon_card *card, void *adrto, void *adr, int len) {
+ int i;
+ switch(card->type) {
+ case EICON_CTYPE_MAESTRA:
+ for(i = 0; i < len; i++) {
+ writeb(ram_inb(card, adr + i), adrto + i);
+ }
+ break;
+ case EICON_CTYPE_MAESTRAP:
+ memcpy(adrto, adr, len);
+ break;
+ case EICON_CTYPE_S2M:
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ memcpy_fromio(adrto, adr, len);
+ break;
+ }
+}
+
+void ram_copytocard(eicon_card *card, void *adrto, void *adr, int len) {
+ int i;
+ switch(card->type) {
+ case EICON_CTYPE_MAESTRA:
+ for(i = 0; i < len; i++) {
+ ram_outb(card, adrto + i, readb(adr + i));
+ }
+ break;
+ case EICON_CTYPE_MAESTRAP:
+ memcpy(adrto, adr, len);
+ break;
+ case EICON_CTYPE_S2M:
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ memcpy_toio(adrto, adr, len);
+ break;
+ }
+}
+
+/*
+ * Transmit-Function
+ */
+void
+eicon_io_transmit(eicon_card *ccard) {
+ eicon_pci_card *pci_card;
+ eicon_isa_card *isa_card;
+ struct sk_buff *skb;
+ struct sk_buff *skb2;
+ unsigned long flags;
+ char *ram, *reg, *cfg;
+ eicon_pr_ram *prram = 0;
+ eicon_isa_com *com = 0;
+ eicon_REQ *ReqOut = 0;
+ eicon_REQ *reqbuf = 0;
+ eicon_chan *chan;
+ eicon_chan_ptr *chan2;
+ int ReqCount;
+ int scom = 0;
+ int tmp = 0;
+ int quloop = 1;
+
+ pci_card = &ccard->hwif.pci;
+ isa_card = &ccard->hwif.isa;
+
+ if (!ccard) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "eicon_transmit: NULL card!\n");
+ return;
+ }
+
+ switch(ccard->type) {
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ scom = 1;
+ com = (eicon_isa_com *)isa_card->shmem;
+ break;
+ case EICON_CTYPE_S2M:
+ scom = 0;
+ prram = (eicon_pr_ram *)isa_card->shmem;
+ break;
+ case EICON_CTYPE_MAESTRAP:
+ scom = 0;
+ ram = (char *)pci_card->PCIram;
+ reg = (char *)pci_card->PCIreg;
+ cfg = (char *)pci_card->PCIcfg;
+ prram = (eicon_pr_ram *)ram;
+ break;
+ case EICON_CTYPE_MAESTRA:
+ scom = 0;
+ ram = (char *)pci_card->PCIram;
+ reg = (char *)pci_card->PCIreg;
+ cfg = (char *)pci_card->PCIcfg;
+ prram = 0;
+ break;
+ default:
+ printk(KERN_WARNING "eicon_transmit: unsupported card-type!\n");
+ return;
+ }
+
+ ReqCount = 0;
+ if (!(skb2 = skb_dequeue(&ccard->sndq)))
+ quloop = 0;
+ while(quloop) {
+ save_flags(flags);
+ cli();
+ if (scom) {
+ if (ram_inb(ccard, &com->Req)) {
+ if (!ccard->ReadyInt) {
+ tmp = ram_inb(ccard, &com->ReadyInt) + 1;
+ ram_outb(ccard, &com->ReadyInt, tmp);
+ ccard->ReadyInt++;
+ }
+ restore_flags(flags);
+ skb_queue_head(&ccard->sndq, skb2);
+ if (DebugVar & 32)
+ printk(KERN_INFO "eicon: transmit: Card not ready\n");
+ return;
+ }
+ } else {
+ if (!(ram_inb(ccard, &prram->ReqOutput) - ram_inb(ccard, &prram->ReqInput))) {
+ restore_flags(flags);
+ skb_queue_head(&ccard->sndq, skb2);
+ if (DebugVar & 32)
+ printk(KERN_INFO "eicon: transmit: Card not ready\n");
+ return;
+ }
+ }
+ restore_flags(flags);
+ chan2 = (eicon_chan_ptr *)skb2->data;
+ chan = chan2->ptr;
+ if (!chan->e.busy) {
+ if((skb = skb_dequeue(&chan->e.X))) {
+ save_flags(flags);
+ cli();
+ reqbuf = (eicon_REQ *)skb->data;
+ if (scom) {
+ ram_outw(ccard, &com->XBuffer.length, reqbuf->XBuffer.length);
+ ram_copytocard(ccard, &com->XBuffer.P, &reqbuf->XBuffer.P, reqbuf->XBuffer.length);
+ ram_outb(ccard, &com->ReqCh, reqbuf->ReqCh);
+
+ } else {
+ /* get address of next available request buffer */
+ ReqOut = (eicon_REQ *)&prram->B[ram_inw(ccard, &prram->NextReq)];
+ ram_outw(ccard, &ReqOut->XBuffer.length, reqbuf->XBuffer.length);
+ ram_copytocard(ccard, &ReqOut->XBuffer.P, &reqbuf->XBuffer.P, reqbuf->XBuffer.length);
+ ram_outb(ccard, &ReqOut->ReqCh, reqbuf->ReqCh);
+ ram_outb(ccard, &ReqOut->Req, reqbuf->Req);
+ }
+
+ if (reqbuf->ReqId &0x1f) { /* if this is no ASSIGN */
+
+ if (!reqbuf->Reference) { /* Signal Layer */
+ if (scom)
+ ram_outb(ccard, &com->ReqId, chan->e.D3Id);
+ else
+ ram_outb(ccard, &ReqOut->ReqId, chan->e.D3Id);
+
+ chan->e.ReqCh = 0;
+ }
+ else { /* Net Layer */
+ if (scom)
+ ram_outb(ccard, &com->ReqId, chan->e.B2Id);
+ else
+ ram_outb(ccard, &ReqOut->ReqId, chan->e.B2Id);
+
+ chan->e.ReqCh = 1;
+ if (((reqbuf->Req & 0x0f) == 0x08) ||
+ ((reqbuf->Req & 0x0f) == 0x01)) { /* Send Data */
+ chan->waitq = reqbuf->XBuffer.length;
+ chan->waitpq += reqbuf->XBuffer.length;
+ }
+ }
+
+ } else { /* It is an ASSIGN */
+
+ if (scom)
+ ram_outb(ccard, &com->ReqId, reqbuf->ReqId);
+ else
+ ram_outb(ccard, &ReqOut->ReqId, reqbuf->ReqId);
+
+ if (!reqbuf->Reference)
+ chan->e.ReqCh = 0;
+ else
+ chan->e.ReqCh = 1;
+ }
+ if (scom)
+ chan->e.ref = ccard->ref_out++;
+ else
+ chan->e.ref = ram_inw(ccard, &ReqOut->Reference);
+
+ chan->e.Req = reqbuf->Req;
+ ReqCount++;
+ if (scom)
+ ram_outb(ccard, &com->Req, reqbuf->Req);
+ else
+ ram_outw(ccard, &prram->NextReq, ram_inw(ccard, &ReqOut->next));
+
+ chan->e.busy = 1;
+ restore_flags(flags);
+ if (DebugVar & 32)
+ printk(KERN_DEBUG "eicon: Req=%x Id=%x Ch=%x Len=%x Ref=%d\n",
+ reqbuf->Req,
+ ram_inb(ccard, &ReqOut->ReqId),
+ reqbuf->ReqCh, reqbuf->XBuffer.length,
+ chan->e.ref);
+ dev_kfree_skb(skb);
+ }
+ dev_kfree_skb(skb2);
+ }
+ else {
+ skb_queue_tail(&ccard->sackq, skb2);
+ if (DebugVar & 32)
+ printk(KERN_INFO "eicon: transmit: busy chan %d\n", chan->No);
+ }
+
+ if (scom)
+ quloop = 0;
+ else
+ if (!(skb2 = skb_dequeue(&ccard->sndq)))
+ quloop = 0;
+
+ }
+ if (!scom)
+ ram_outb(ccard, &prram->ReqInput, (__u8)(ram_inb(ccard, &prram->ReqInput) + ReqCount));
+
+ while((skb = skb_dequeue(&ccard->sackq))) {
+ skb_queue_tail(&ccard->sndq, skb);
+ }
+}
+
+
+/*
+ * IRQ handler
+ */
+void
+eicon_irq(int irq, void *dev_id, struct pt_regs *regs) {
+ eicon_card *ccard = (eicon_card *)dev_id;
+ eicon_pci_card *pci_card;
+ eicon_isa_card *isa_card;
+ char *ram = 0;
+ char *reg = 0;
+ char *cfg = 0;
+ eicon_pr_ram *prram = 0;
+ eicon_isa_com *com = 0;
+ eicon_RC *RcIn;
+ eicon_IND *IndIn;
+ struct sk_buff *skb;
+ int Count = 0;
+ int Rc = 0;
+ int Ind = 0;
+ unsigned char *irqprobe = 0;
+ int scom = 0;
+ int tmp = 0;
+
+
+ if (!ccard) {
+ printk(KERN_WARNING "eicon_irq: spurious interrupt %d\n", irq);
+ return;
+ }
+
+ if (ccard->type == EICON_CTYPE_QUADRO) {
+ tmp = 4;
+ while(tmp) {
+ com = (eicon_isa_com *)ccard->hwif.isa.shmem;
+ if ((readb(ccard->hwif.isa.intack))) { /* quadro found */
+ break;
+ }
+ ccard = ccard->qnext;
+ tmp--;
+ }
+ }
+
+ pci_card = &ccard->hwif.pci;
+ isa_card = &ccard->hwif.isa;
+
+ switch(ccard->type) {
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ scom = 1;
+ com = (eicon_isa_com *)isa_card->shmem;
+ irqprobe = &isa_card->irqprobe;
+ break;
+ case EICON_CTYPE_S2M:
+ scom = 0;
+ prram = (eicon_pr_ram *)isa_card->shmem;
+ irqprobe = &isa_card->irqprobe;
+ break;
+ case EICON_CTYPE_MAESTRAP:
+ scom = 0;
+ ram = (char *)pci_card->PCIram;
+ reg = (char *)pci_card->PCIreg;
+ cfg = (char *)pci_card->PCIcfg;
+ irqprobe = &pci_card->irqprobe;
+ prram = (eicon_pr_ram *)ram;
+ break;
+ case EICON_CTYPE_MAESTRA:
+ scom = 0;
+ ram = (char *)pci_card->PCIram;
+ reg = (char *)pci_card->PCIreg;
+ cfg = (char *)pci_card->PCIcfg;
+ irqprobe = &pci_card->irqprobe;
+ prram = 0;
+ break;
+ default:
+ printk(KERN_WARNING "eicon_irq: unsupported card-type!\n");
+ return;
+ }
+
+ if (*irqprobe) {
+ switch(ccard->type) {
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ if (readb(isa_card->intack)) {
+ writeb(0, &com->Rc);
+ writeb(0, isa_card->intack);
+ }
+ (*irqprobe)++;
+ break;
+ case EICON_CTYPE_S2M:
+ if (readb(isa_card->intack)) {
+ writeb(0, &prram->RcOutput);
+ writeb(0, isa_card->intack);
+ }
+ (*irqprobe)++;
+ break;
+ case EICON_CTYPE_MAESTRAP:
+ if (readb(&ram[0x3fe])) {
+ writeb(0, &prram->RcOutput);
+ writew(MP_IRQ_RESET_VAL, &cfg[MP_IRQ_RESET]);
+ writew(0, &cfg[MP_IRQ_RESET + 2]);
+ writeb(0, &ram[0x3fe]);
+ }
+ *irqprobe = 0;
+ break;
+ case EICON_CTYPE_MAESTRA:
+ outb(0x08, pci_card->PCIreg + M_RESET);
+ *irqprobe = 0;
+ break;
+ }
+ return;
+ }
+
+ switch(ccard->type) {
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ case EICON_CTYPE_S2M:
+ if (!(readb(isa_card->intack))) { /* card did not interrupt */
+ if (DebugVar & 1)
+ printk(KERN_DEBUG "eicon: IRQ: card tells no interrupt!\n");
+ return;
+ }
+ break;
+ case EICON_CTYPE_MAESTRAP:
+ if (!(readb(&ram[0x3fe]))) { /* card did not interrupt */
+ if (DebugVar & 1)
+ printk(KERN_DEBUG "eicon: IRQ: card tells no interrupt!\n");
+ return;
+ }
+ break;
+ case EICON_CTYPE_MAESTRA:
+ outw(0x3fe, pci_card->PCIreg + M_ADDR);
+ if (!(inb(pci_card->PCIreg + M_DATA))) { /* card did not interrupt */
+ if (DebugVar & 1)
+ printk(KERN_DEBUG "eicon: IRQ: card tells no interrupt!\n");
+ return;
+ }
+ break;
+ }
+
+ if (scom) {
+
+ /* if a return code is available ... */
+ if ((tmp = ram_inb(ccard, &com->Rc))) {
+ eicon_RC *ack;
+ if (tmp == READY_INT) {
+ if (DebugVar & 64)
+ printk(KERN_INFO "eicon: IRQ Rc=READY_INT\n");
+ if (ccard->ReadyInt) {
+ ccard->ReadyInt--;
+ ram_outb(ccard, &com->Rc, 0);
+ }
+ } else {
+ skb = alloc_skb(sizeof(eicon_RC), GFP_ATOMIC);
+ ack = (eicon_RC *)skb_put(skb, sizeof(eicon_RC));
+ ack->Rc = tmp;
+ ack->RcId = ram_inb(ccard, &com->RcId);
+ ack->RcCh = ram_inb(ccard, &com->RcCh);
+ ack->Reference = ccard->ref_in++;
+ if (DebugVar & 64)
+ printk(KERN_INFO "eicon: IRQ Rc=%d Id=%d Ch=%d Ref=%d\n",
+ tmp,ack->RcId,ack->RcCh,ack->Reference);
+ skb_queue_tail(&ccard->rackq, skb);
+ eicon_schedule_ack(ccard);
+ ram_outb(ccard, &com->Req, 0);
+ ram_outb(ccard, &com->Rc, 0);
+ }
+
+ } else {
+
+ /* if an indication is available ... */
+ if ((tmp = ram_inb(ccard, &com->Ind))) {
+ eicon_IND *ind;
+ int len = ram_inw(ccard, &com->RBuffer.length);
+ skb = alloc_skb((sizeof(eicon_IND) + len - 1), GFP_ATOMIC);
+ ind = (eicon_IND *)skb_put(skb, (sizeof(eicon_IND) + len - 1));
+ ind->Ind = tmp;
+ ind->IndId = ram_inb(ccard, &com->IndId);
+ ind->IndCh = ram_inb(ccard, &com->IndCh);
+ ind->MInd = ram_inb(ccard, &com->MInd);
+ ind->MLength = ram_inw(ccard, &com->MLength);
+ ind->RBuffer.length = len;
+ if (DebugVar & 64)
+ printk(KERN_INFO "eicon: IRQ Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n",
+ tmp,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len);
+ ram_copyfromcard(ccard, &ind->RBuffer.P, &com->RBuffer.P, len);
+ skb_queue_tail(&ccard->rcvq, skb);
+ eicon_schedule_rx(ccard);
+ ram_outb(ccard, &com->Ind, 0);
+ }
+ }
+
+ } else {
+
+ /* if return codes are available ... */
+ if((Count = ram_inb(ccard, &prram->RcOutput))) {
+ eicon_RC *ack;
+ /* get the buffer address of the first return code */
+ RcIn = (eicon_RC *)&prram->B[ram_inw(ccard, &prram->NextRc)];
+ /* for all return codes do ... */
+ while(Count--) {
+
+ if((Rc=ram_inb(ccard, &RcIn->Rc))) {
+ skb = alloc_skb(sizeof(eicon_RC), GFP_ATOMIC);
+ ack = (eicon_RC *)skb_put(skb, sizeof(eicon_RC));
+ ack->Rc = Rc;
+ ack->RcId = ram_inb(ccard, &RcIn->RcId);
+ ack->RcCh = ram_inb(ccard, &RcIn->RcCh);
+ ack->Reference = ram_inw(ccard, &RcIn->Reference);
+ if (DebugVar & 64)
+ printk(KERN_INFO "eicon: IRQ Rc=%d Id=%d Ch=%d Ref=%d\n",
+ Rc,ack->RcId,ack->RcCh,ack->Reference);
+ ram_outb(ccard, &RcIn->Rc, 0);
+ skb_queue_tail(&ccard->rackq, skb);
+ eicon_schedule_ack(ccard);
+ }
+ /* get buffer address of next return code */
+ RcIn = (eicon_RC *)&prram->B[ram_inw(ccard, &RcIn->next)];
+ }
+ /* clear all return codes (no chaining!) */
+ ram_outb(ccard, &prram->RcOutput, 0);
+ }
+
+ /* if indications are available ... */
+ if((Count = ram_inb(ccard, &prram->IndOutput))) {
+ eicon_IND *ind;
+ /* get the buffer address of the first indication */
+ IndIn = (eicon_IND *)&prram->B[ram_inw(ccard, &prram->NextInd)];
+ /* for all indications do ... */
+ while(Count--) {
+ Ind = ram_inb(ccard, &IndIn->Ind);
+ if(Ind) {
+ int len = ram_inw(ccard, &IndIn->RBuffer.length);
+ skb = alloc_skb((sizeof(eicon_IND) + len - 1), GFP_ATOMIC);
+ ind = (eicon_IND *)skb_put(skb, (sizeof(eicon_IND) + len - 1));
+ ind->Ind = Ind;
+ ind->IndId = ram_inb(ccard, &IndIn->IndId);
+ ind->IndCh = ram_inb(ccard, &IndIn->IndCh);
+ ind->MInd = ram_inb(ccard, &IndIn->MInd);
+ ind->MLength = ram_inw(ccard, &IndIn->MLength);
+ ind->RBuffer.length = len;
+ if (DebugVar & 64)
+ printk(KERN_INFO "eicon: IRQ Ind=%d Id=%d Ch=%d MInd=%d MLen=%d Len=%d\n",
+ Ind,ind->IndId,ind->IndCh,ind->MInd,ind->MLength,len);
+ ram_copyfromcard(ccard, &ind->RBuffer.P, &IndIn->RBuffer.P, len);
+ skb_queue_tail(&ccard->rcvq, skb);
+ eicon_schedule_rx(ccard);
+ ram_outb(ccard, &IndIn->Ind, 0);
+ }
+ /* get buffer address of next indication */
+ IndIn = (eicon_IND *)&prram->B[ram_inw(ccard, &IndIn->next)];
+ }
+ ram_outb(ccard, &prram->IndOutput, 0);
+ }
+
+ }
+
+ /* clear interrupt */
+ switch(ccard->type) {
+ case EICON_CTYPE_QUADRO:
+ writeb(0, isa_card->intack);
+ writeb(0, &com[0x401]);
+ break;
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_S2M:
+ writeb(0, isa_card->intack);
+ break;
+ case EICON_CTYPE_MAESTRAP:
+ writew(MP_IRQ_RESET_VAL, &cfg[MP_IRQ_RESET]);
+ writew(0, &cfg[MP_IRQ_RESET + 2]);
+ writeb(0, &ram[0x3fe]);
+ break;
+ case EICON_CTYPE_MAESTRA:
+ outb(0x08, pci_card->PCIreg + M_RESET);
+ outw(0x3fe, pci_card->PCIreg + M_ADDR);
+ outb(0, pci_card->PCIreg + M_DATA);
+ break;
+ }
+
+ return;
+}
+
diff --git a/drivers/isdn/eicon/eicon_isa.c b/drivers/isdn/eicon/eicon_isa.c
new file mode 100644
index 000000000..184f1c394
--- /dev/null
+++ b/drivers/isdn/eicon/eicon_isa.c
@@ -0,0 +1,432 @@
+/* $Id: eicon_isa.c,v 1.5 1999/04/01 12:48:33 armin Exp $
+ *
+ * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ * Hardware-specific code for old ISA cards.
+ *
+ * Copyright 1998 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon_isa.c,v $
+ * Revision 1.5 1999/04/01 12:48:33 armin
+ * Changed some log outputs.
+ *
+ * Revision 1.4 1999/03/29 11:19:46 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.3 1999/03/02 12:37:45 armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.2 1999/01/24 20:14:19 armin
+ * Changed and added debug stuff.
+ * Better data sending. (still problems with tty's flip buffer)
+ *
+ * Revision 1.1 1999/01/01 18:09:43 armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#include "eicon.h"
+#include "eicon_isa.h"
+
+#define check_shmem check_region
+#define release_shmem release_region
+#define request_shmem request_region
+
+char *eicon_isa_revision = "$Revision: 1.5 $";
+
+/* Mask for detecting invalid IRQ parameter */
+static int eicon_isa_valid_irq[] = {
+ 0x1c1c, /* 2, 3, 4, 10, 11, 12 (S)*/
+ 0x1c1c, /* 2, 3, 4, 10, 11, 12 (SX) */
+ 0x1cbc, /* 2, 3, 4, 5, 7, 10, 11, 12 (SCOM) */
+ 0x1cbc, /* 2, 3, 4, 5, 6, 10, 11, 12 (Quadro) */
+ 0x1cbc /* 2, 3, 4, 5, 7, 10, 11, 12 (S2M) */
+};
+
+static void
+eicon_isa_release_shmem(eicon_isa_card *card) {
+ if (card->mvalid)
+ release_shmem((unsigned long)card->shmem, card->ramsize);
+ card->mvalid = 0;
+}
+
+static void
+eicon_isa_release_irq(eicon_isa_card *card) {
+ if (!card->master)
+ return;
+ if (card->ivalid)
+ free_irq(card->irq, card);
+ card->ivalid = 0;
+}
+
+void
+eicon_isa_release(eicon_isa_card *card) {
+ eicon_isa_release_irq(card);
+ eicon_isa_release_shmem(card);
+}
+
+void
+eicon_isa_printpar(eicon_isa_card *card) {
+ switch (card->type) {
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ case EICON_CTYPE_S2M:
+ printk(KERN_INFO "Eicon %s at 0x%lx, irq %d\n",
+ eicon_ctype_name[card->type],
+ (unsigned long)card->shmem,
+ card->irq);
+ }
+}
+
+int
+eicon_isa_find_card(int Mem, int Irq, char * Id)
+{
+ int primary = 1;
+
+ if (!strlen(Id))
+ return -1;
+
+ /* Check for valid membase address */
+ if ((Mem < 0x0c0000) ||
+ (Mem > 0x0fc000) ||
+ (Mem & 0xfff)) {
+ printk(KERN_WARNING "eicon_isa: illegal membase 0x%x for %s\n",
+ Mem, Id);
+ return -1;
+ }
+ if (check_shmem(Mem, RAMSIZE)) {
+ printk(KERN_WARNING "eicon_isa_boot: memory at 0x%x already in use.\n", Mem);
+ return -1;
+ }
+
+ writew(0x55aa, Mem + 0x402);
+ if (readw(Mem + 0x402) != 0x55aa) primary = 0;
+ writew(0, Mem + 0x402);
+ if (readw(Mem + 0x402) != 0) primary = 0;
+
+ printk(KERN_INFO "Eicon: Driver-ID: %s\n", Id);
+ if (primary) {
+ printk(KERN_INFO "Eicon: assuming pri card at 0x%x\n", Mem);
+ writeb(0, Mem + 0x3ffe);
+ return EICON_CTYPE_ISAPRI;
+ } else {
+ printk(KERN_INFO "Eicon: assuming bri card at 0x%x\n", Mem);
+ writeb(0, Mem + 0x400);
+ return EICON_CTYPE_ISABRI;
+ }
+ return -1;
+}
+
+int
+eicon_isa_bootload(eicon_isa_card *card, eicon_isa_codebuf *cb) {
+ int tmp;
+ int timeout;
+ eicon_isa_codebuf cbuf;
+ unsigned char *code;
+ eicon_isa_boot *boot;
+
+ if (copy_from_user(&cbuf, cb, sizeof(eicon_isa_codebuf)))
+ return -EFAULT;
+
+ /* Allocate code-buffer and copy code from userspace */
+ if (cbuf.bootstrap_len > 1024) {
+ printk(KERN_WARNING "eicon_isa_boot: Invalid startup-code size %ld\n",
+ cbuf.bootstrap_len);
+ return -EINVAL;
+ }
+ if (!(code = kmalloc(cbuf.bootstrap_len, GFP_KERNEL))) {
+ printk(KERN_WARNING "eicon_isa_boot: Couldn't allocate code buffer\n");
+ return -ENOMEM;
+ }
+ if (copy_from_user(code, &cb->code, cbuf.bootstrap_len)) {
+ kfree(code);
+ return -EFAULT;
+ }
+
+ switch(card->type) {
+ case EICON_CTYPE_S:
+ case EICON_CTYPE_SX:
+ case EICON_CTYPE_SCOM:
+ case EICON_CTYPE_QUADRO:
+ case EICON_CTYPE_ISABRI:
+ card->ramsize = RAMSIZE;
+ card->intack = (__u8 *)card->shmem + INTACK;
+ card->startcpu = (__u8 *)card->shmem + STARTCPU;
+ card->stopcpu = (__u8 *)card->shmem + STOPCPU;
+ break;
+ case EICON_CTYPE_S2M:
+ case EICON_CTYPE_ISAPRI:
+ card->ramsize = RAMSIZE_P;
+ card->intack = (__u8 *)card->shmem + INTACK_P;
+ card->startcpu = (__u8 *)card->shmem + STARTCPU_P;
+ card->stopcpu = (__u8 *)card->shmem + STOPCPU_P;
+ break;
+ default:
+ printk(KERN_WARNING "eicon_isa_boot: Invalid card type %d\n", card->type);
+ return -EINVAL;
+ }
+
+ /* Register shmem */
+ if (check_shmem((unsigned long)card->shmem, card->ramsize)) {
+ printk(KERN_WARNING "eicon_isa_boot: memory at 0x%lx already in use.\n",
+ (unsigned long)card->shmem);
+ kfree(code);
+ return -EBUSY;
+ }
+ request_shmem((unsigned long)card->shmem, card->ramsize, "Eicon ISA ISDN");
+ card->mvalid = 1;
+
+ /* clear any pending irq's */
+ readb(card->intack);
+ /* set reset-line active */
+ writeb(0, card->stopcpu);
+ /* clear irq-requests */
+ writeb(0, card->intack);
+ readb(card->intack);
+
+ /* Copy code into card */
+ memcpy_toio(&card->shmem->c, code, cbuf.bootstrap_len);
+
+ /* Check for properly loaded code */
+ if (!check_signature((unsigned long)&card->shmem->c, code, 1020)) {
+ printk(KERN_WARNING "eicon_isa_boot: Could not load startup-code\n");
+ eicon_isa_release_shmem(card);
+ kfree(code);
+ return -EIO;
+ }
+ /* if 16k-ramsize, duplicate the reset-jump-code */
+ if (card->ramsize == RAMSIZE_P)
+ memcpy_toio((__u8 *)card->shmem + 0x3ff0, &code[0x3f0], 12);
+
+ kfree(code);
+ boot = &card->shmem->boot;
+
+ /* Delay 0.2 sec. */
+ SLEEP(20);
+
+ /* Start CPU */
+ writeb(cbuf.boot_opt, &boot->ctrl);
+ writeb(0, card->startcpu);
+
+ /* Delay 0.2 sec. */
+ SLEEP(20);
+
+ timeout = jiffies + (HZ * 22);
+ while (timeout > jiffies) {
+ if (readb(&boot->ctrl) == 0)
+ break;
+ SLEEP(10);
+ }
+ if (readb(&boot->ctrl) != 0) {
+ printk(KERN_WARNING "eicon_isa_boot: CPU test failed\n");
+ eicon_isa_release_shmem(card);
+ return -EIO;
+ }
+
+ /* Check for memory-test errors */
+ if (readw(&boot->ebit)) {
+ printk(KERN_WARNING "eicon_isa_boot: memory test failed (bit 0x%04x at 0x%08x)\n",
+ readw(&boot->ebit), readl(&boot->eloc));
+ eicon_isa_release_shmem(card);
+ return -EIO;
+ }
+
+ /* Check card type and memory size */
+ tmp = readb(&boot->card);
+ if ((tmp < 0) || (tmp > 4)) {
+ printk(KERN_WARNING "eicon_isa_boot: Type detect failed\n");
+ eicon_isa_release_shmem(card);
+ return -EIO;
+ }
+ card->type = tmp;
+ ((eicon_card *)card->card)->type = tmp;
+
+ tmp = readb(&boot->msize);
+ if (tmp != 8 && tmp != 16 && tmp != 24 &&
+ tmp != 32 && tmp != 48 && tmp != 60) {
+ printk(KERN_WARNING "eicon_isa_boot: invalid memsize\n");
+ eicon_isa_release_shmem(card);
+ return -EIO;
+ }
+ printk(KERN_INFO "%s: startup-code loaded\n", eicon_ctype_name[card->type]);
+ if ((card->type == EICON_CTYPE_QUADRO) && (card->master)) {
+ tmp = eicon_addcard(card->type, (unsigned long)card->shmem, card->irq,
+ ((eicon_card *)card->card)->regname);
+ printk(KERN_INFO "Eicon: %d adapters added\n", tmp);
+ }
+ return 0;
+}
+
+int
+eicon_isa_load(eicon_isa_card *card, eicon_isa_codebuf *cb) {
+ eicon_isa_boot *boot;
+ int tmp;
+ int timeout;
+ int j;
+ eicon_isa_codebuf cbuf;
+ unsigned char *code;
+ unsigned char *p;
+
+ if (copy_from_user(&cbuf, cb, sizeof(eicon_isa_codebuf)))
+ return -EFAULT;
+
+ if (!(code = kmalloc(cbuf.firmware_len, GFP_KERNEL))) {
+ printk(KERN_WARNING "eicon_isa_boot: Couldn't allocate code buffer\n");
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(code, &cb->code, cbuf.firmware_len)) {
+ kfree(code);
+ return -EFAULT;
+ }
+
+ boot = &card->shmem->boot;
+
+ if ((!card->ivalid) && card->master) {
+ card->irqprobe = 1;
+ /* Check for valid IRQ */
+ if ((card->irq < 0) || (card->irq > 15) ||
+ (!((1 << card->irq) & eicon_isa_valid_irq[card->type & 0x0f]))) {
+ printk(KERN_WARNING "eicon_isa_boot: illegal irq: %d\n", card->irq);
+ eicon_isa_release_shmem(card);
+ kfree(code);
+ return -EINVAL;
+ }
+ /* Register irq */
+ if (!request_irq(card->irq, &eicon_irq, 0, "Eicon ISA ISDN", card))
+ card->ivalid = 1;
+ else {
+ printk(KERN_WARNING "eicon_isa_boot: irq %d already in use.\n",
+ card->irq);
+ eicon_isa_release_shmem(card);
+ kfree(code);
+ return -EBUSY;
+ }
+ }
+
+ tmp = readb(&boot->msize);
+ if (tmp != 8 && tmp != 16 && tmp != 24 &&
+ tmp != 32 && tmp != 48 && tmp != 60) {
+ printk(KERN_WARNING "eicon_isa_boot: invalid memsize\n");
+ eicon_isa_release_shmem(card);
+ return -EIO;
+ }
+
+ eicon_isa_printpar(card);
+
+ /* Download firmware */
+ printk(KERN_INFO "%s %dkB, loading firmware ...\n",
+ eicon_ctype_name[card->type],
+ tmp * 16);
+ tmp = cbuf.firmware_len >> 8;
+ p = code;
+ while (tmp--) {
+ memcpy_toio(&boot->b, p, 256);
+ writeb(1, &boot->ctrl);
+ timeout = jiffies + 10;
+ while (timeout > jiffies) {
+ if (readb(&boot->ctrl) == 0)
+ break;
+ SLEEP(2);
+ }
+ if (readb(&boot->ctrl)) {
+ printk(KERN_WARNING "eicon_isa_boot: download timeout at 0x%x\n", p-code);
+ eicon_isa_release(card);
+ kfree(code);
+ return -EIO;
+ }
+ p += 256;
+ }
+ kfree(code);
+
+ /* Initialize firmware parameters */
+ memcpy_toio(&card->shmem->c[8], &cbuf.tei, 14);
+ memcpy_toio(&card->shmem->c[32], &cbuf.oad, 96);
+ memcpy_toio(&card->shmem->c[128], &cbuf.oad, 96);
+
+ /* Start firmware, wait for signature */
+ writeb(2, &boot->ctrl);
+ timeout = jiffies + (5*HZ);
+ while (timeout > jiffies) {
+ if (readw(&boot->signature) == 0x4447)
+ break;
+ SLEEP(2);
+ }
+ if (readw(&boot->signature) != 0x4447) {
+ printk(KERN_WARNING "eicon_isa_boot: firmware selftest failed %04x\n",
+ readw(&boot->signature));
+ eicon_isa_release(card);
+ return -EIO;
+ }
+
+ card->channels = readb(&card->shmem->c[0x3f6]);
+
+ /* clear irq-requests, reset irq-count */
+ readb(card->intack);
+ writeb(0, card->intack);
+
+ if (card->master) {
+ card->irqprobe = 1;
+ /* Trigger an interrupt and check if it is delivered */
+ tmp = readb(&card->shmem->com.ReadyInt);
+ tmp ++;
+ writeb(tmp, &card->shmem->com.ReadyInt);
+ timeout = jiffies + 20;
+ while (timeout > jiffies) {
+ if (card->irqprobe > 1)
+ break;
+ SLEEP(2);
+ }
+ if (card->irqprobe == 1) {
+ printk(KERN_WARNING "eicon_isa_boot: IRQ test failed\n");
+ eicon_isa_release(card);
+ return -EIO;
+ }
+ }
+ writeb(card->irq, &card->shmem->com.Int);
+
+ /* initializing some variables */
+ ((eicon_card *)card->card)->ReadyInt = 0;
+ ((eicon_card *)card->card)->ref_in = 1;
+ ((eicon_card *)card->card)->ref_out = 1;
+ for(j=0; j<256; j++) ((eicon_card *)card->card)->IdTable[j] = NULL;
+ for(j=0; j< (card->channels + 1); j++) {
+ ((eicon_card *)card->card)->bch[j].e.busy = 0;
+ ((eicon_card *)card->card)->bch[j].e.D3Id = 0;
+ ((eicon_card *)card->card)->bch[j].e.B2Id = 0;
+ ((eicon_card *)card->card)->bch[j].e.ref = 0;
+ ((eicon_card *)card->card)->bch[j].e.Req = 0;
+ ((eicon_card *)card->card)->bch[j].e.complete = 1;
+ ((eicon_card *)card->card)->bch[j].fsm_state = EICON_STATE_NULL;
+ }
+
+ printk(KERN_INFO "Eicon: Supported channels: %d\n", card->channels);
+ printk(KERN_INFO "%s successfully started\n", eicon_ctype_name[card->type]);
+
+ /* Enable normal IRQ processing */
+ card->irqprobe = 0;
+ return 0;
+}
diff --git a/drivers/isdn/eicon/eicon_isa.h b/drivers/isdn/eicon/eicon_isa.h
new file mode 100644
index 000000000..5b0dc1a6a
--- /dev/null
+++ b/drivers/isdn/eicon/eicon_isa.h
@@ -0,0 +1,144 @@
+/* $Id: eicon_isa.h,v 1.3 1999/03/29 11:19:47 armin Exp $
+ *
+ * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ *
+ * Copyright 1998 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon_isa.h,v $
+ * Revision 1.3 1999/03/29 11:19:47 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.2 1999/03/02 12:37:46 armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.1 1999/01/01 18:09:44 armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#ifndef eicon_isa_h
+#define eicon_isa_h
+
+#ifdef __KERNEL__
+
+/* Factory defaults for ISA-Cards */
+#define EICON_ISA_MEMBASE 0xd0000
+#define EICON_ISA_IRQ 3
+/* shmem offset for Quadro parts */
+#define EICON_ISA_QOFFSET 0x0800
+
+typedef struct {
+ __u16 length __attribute__ ((packed)); /* length of data/parameter field */
+ __u8 P[270]; /* data/parameter field */
+} eicon_scom_PBUFFER;
+
+/* General communication buffer */
+typedef struct {
+ __u8 Req; /* request register */
+ __u8 ReqId; /* request task/entity identification */
+ __u8 Rc; /* return code register */
+ __u8 RcId; /* return code task/entity identification */
+ __u8 Ind; /* Indication register */
+ __u8 IndId; /* Indication task/entity identification */
+ __u8 IMask; /* Interrupt Mask Flag */
+ __u8 RNR; /* Receiver Not Ready (set by PC) */
+ __u8 XLock; /* XBuffer locked Flag */
+ __u8 Int; /* ISDN interrupt */
+ __u8 ReqCh; /* Channel field for layer-3 Requests */
+ __u8 RcCh; /* Channel field for layer-3 Returncodes */
+ __u8 IndCh; /* Channel field for layer-3 Indications */
+ __u8 MInd; /* more data indication field */
+ __u16 MLength; /* more data total packet length */
+ __u8 ReadyInt; /* request field for ready interrupt */
+ __u8 Reserved[12]; /* reserved space */
+ __u8 IfType; /* 1 = 16k-Interface */
+ __u16 Signature __attribute__ ((packed)); /* ISDN adapter Signature */
+ eicon_scom_PBUFFER XBuffer; /* Transmit Buffer */
+ eicon_scom_PBUFFER RBuffer; /* Receive Buffer */
+} eicon_isa_com;
+
+/* struct for downloading firmware */
+typedef struct {
+ __u8 ctrl;
+ __u8 card;
+ __u8 msize;
+ __u8 fill0;
+ __u16 ebit __attribute__ ((packed));
+ __u32 eloc __attribute__ ((packed));
+ __u8 reserved[20];
+ __u16 signature __attribute__ ((packed));
+ __u8 fill[224];
+ __u8 b[256];
+} eicon_isa_boot;
+
+/* Shared memory */
+typedef union {
+ unsigned char c[0x400];
+ eicon_isa_com com;
+ eicon_isa_boot boot;
+} eicon_isa_shmem;
+
+/*
+ * card's description
+ */
+typedef struct {
+ int ramsize;
+ int irq; /* IRQ */
+ void* card;
+ eicon_isa_shmem* shmem; /* Shared-memory area */
+ unsigned char* intack; /* Int-Acknowledge */
+ unsigned char* stopcpu; /* Writing here stops CPU */
+ unsigned char* startcpu; /* Writing here starts CPU */
+ unsigned char type; /* card type */
+ int channels; /* No. of channels */
+ unsigned char irqprobe; /* Flag: IRQ-probing */
+ unsigned char mvalid; /* Flag: Memory is valid */
+ unsigned char ivalid; /* Flag: IRQ is valid */
+ unsigned char master; /* Flag: Card ist Quadro 1/4 */
+ void* generic; /* Ptr to generic card struct */
+} eicon_isa_card;
+
+/* Offsets for special locations on standard cards */
+#define INTACK 0x03fe
+#define STOPCPU 0x0400
+#define STARTCPU 0x0401
+#define RAMSIZE 0x0400
+/* Offsets for special location on PRI card */
+#define INTACK_P 0x3ffc
+#define STOPCPU_P 0x3ffe
+#define STARTCPU_P 0x3fff
+#define RAMSIZE_P 0x4000
+
+
+extern int eicon_isa_load(eicon_isa_card *card, eicon_isa_codebuf *cb);
+extern int eicon_isa_bootload(eicon_isa_card *card, eicon_isa_codebuf *cb);
+extern void eicon_isa_release(eicon_isa_card *card);
+extern void eicon_isa_printpar(eicon_isa_card *card);
+extern void eicon_isa_transmit(eicon_isa_card *card);
+extern int eicon_isa_find_card(int Mem, int Irq, char * Id);
+
+#endif /* __KERNEL__ */
+
+#endif /* eicon_isa_h */
diff --git a/drivers/isdn/eicon/eicon_mod.c b/drivers/isdn/eicon/eicon_mod.c
new file mode 100644
index 000000000..c14d91d7e
--- /dev/null
+++ b/drivers/isdn/eicon/eicon_mod.c
@@ -0,0 +1,1210 @@
+/* $Id: eicon_mod.c,v 1.5 1999/04/01 12:48:35 armin Exp $
+ *
+ * ISDN lowlevel-module for Eicon.Diehl active cards.
+ *
+ * Copyright 1997 by Fritz Elfert (fritz@wuemaus.franken.de)
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * Thanks to Eicon Technology Diehl GmbH & Co. oHG for
+ * documents, informations and hardware.
+ *
+ * Deutsche Telekom AG for S2M support.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon_mod.c,v $
+ * Revision 1.5 1999/04/01 12:48:35 armin
+ * Changed some log outputs.
+ *
+ * Revision 1.4 1999/03/29 11:19:47 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.3 1999/03/02 12:37:47 armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.2 1999/01/24 20:14:21 armin
+ * Changed and added debug stuff.
+ * Better data sending. (still problems with tty's flip buffer)
+ *
+ * Revision 1.1 1999/01/01 18:09:44 armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include "eicon.h"
+
+#define INCLUDE_INLINE_FUNCS
+
+static eicon_card *cards = (eicon_card *) NULL;
+
+static char *eicon_revision = "$Revision: 1.5 $";
+
+extern char *eicon_pci_revision;
+extern char *eicon_isa_revision;
+extern char *eicon_idi_revision;
+
+#ifdef MODULE
+#define MOD_USE_COUNT (GET_USE_COUNT (&__this_module))
+#endif
+
+#define EICON_CTRL_VERSION 1
+
+ulong DebugVar;
+
+/* Parameters to be set by insmod */
+static int membase = -1;
+static int irq = -1;
+static char *id = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+
+MODULE_DESCRIPTION( "Driver for Eicon.Diehl active ISDN cards");
+MODULE_AUTHOR( "Armin Schindler");
+MODULE_SUPPORTED_DEVICE( "ISDN subsystem");
+MODULE_PARM_DESC(membase, "Base address of first ISA card");
+MODULE_PARM_DESC(irq, "IRQ of first card");
+MODULE_PARM_DESC(id, "ID-String of first card");
+MODULE_PARM(membase, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(id, "s");
+
+char *eicon_ctype_name[] = {
+ "ISDN-S",
+ "ISDN-SX",
+ "ISDN-SCOM",
+ "ISDN-QUADRO",
+ "ISDN-S2M",
+ "DIVA Server BRI/PCI",
+ "DIVA Server 4BRI/PCI",
+ "DIVA Server 4BRI/PCI",
+ "DIVA Server PRI/PCI"
+};
+
+static int
+getrel(char *p)
+{
+ int v = 0;
+ char *tmp = 0;
+
+ if ((tmp = strchr(p, '.')))
+ p = tmp + 1;
+ while (p[0] >= '0' && p[0] <= '9') {
+ v = ((v < 0) ? 0 : (v * 10)) + (int) (p[0] - '0');
+ p++;
+ }
+ return v;
+
+
+}
+
+static char *
+eicon_getrev(const char *revision)
+{
+ char *rev;
+ char *p;
+ if ((p = strchr(revision, ':'))) {
+ rev = p + 2;
+ p = strchr(rev, '$');
+ *--p = 0;
+ } else rev = "?.??";
+ return rev;
+
+}
+
+static eicon_chan *
+find_channel(eicon_card *card, int channel)
+{
+ if ((channel >= 0) && (channel < card->nchannels))
+ return &(card->bch[channel]);
+ if (DebugVar & 1)
+ printk(KERN_WARNING "eicon: Invalid channel %d\n", channel);
+ return NULL;
+}
+
+/*
+ * Free MSN list
+ */
+static void
+eicon_clear_msn(eicon_card *card)
+{
+ struct msn_entry *p = card->msn_list;
+ struct msn_entry *q;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ card->msn_list = NULL;
+ restore_flags(flags);
+ while (p) {
+ q = p->next;
+ kfree(p);
+ p = q;
+ }
+}
+
+/*
+ * Find an MSN entry in the list.
+ * If ia5 != 0, return IA5-encoded EAZ, else
+ * return a bitmask with corresponding bit set.
+ */
+static __u16
+eicon_find_msn(eicon_card *card, char *msn, int ia5)
+{
+ struct msn_entry *p = card->msn_list;
+ __u8 eaz = '0';
+
+ while (p) {
+ if (!strcmp(p->msn, msn)) {
+ eaz = p->eaz;
+ break;
+ }
+ p = p->next;
+ }
+ if (!ia5)
+ return (1 << (eaz - '0'));
+ else
+ return eaz;
+}
+
+/*
+ * Find an EAZ entry in the list.
+ * return a string with corresponding msn.
+ */
+char *
+eicon_find_eaz(eicon_card *card, char eaz)
+{
+ struct msn_entry *p = card->msn_list;
+
+ while (p) {
+ if (p->eaz == eaz)
+ return(p->msn);
+ p = p->next;
+ }
+ return("\0");
+}
+
+#if 0
+/*
+ * Add or delete an MSN to the MSN list
+ *
+ * First character of msneaz is EAZ, rest is MSN.
+ * If length of eazmsn is 1, delete that entry.
+ */
+static int
+eicon_set_msn(eicon_card *card, char *eazmsn)
+{
+ struct msn_entry *p = card->msn_list;
+ struct msn_entry *q = NULL;
+ unsigned long flags;
+ int i;
+
+ if (!strlen(eazmsn))
+ return 0;
+ if (strlen(eazmsn) > 16)
+ return -EINVAL;
+ for (i = 0; i < strlen(eazmsn); i++)
+ if (!isdigit(eazmsn[i]))
+ return -EINVAL;
+ if (strlen(eazmsn) == 1) {
+ /* Delete a single MSN */
+ while (p) {
+ if (p->eaz == eazmsn[0]) {
+ save_flags(flags);
+ cli();
+ if (q)
+ q->next = p->next;
+ else
+ card->msn_list = p->next;
+ restore_flags(flags);
+ kfree(p);
+ if (DebugVar & 8)
+ printk(KERN_DEBUG
+ "Mapping for EAZ %c deleted\n",
+ eazmsn[0]);
+ return 0;
+ }
+ q = p;
+ p = p->next;
+ }
+ return 0;
+ }
+ /* Add a single MSN */
+ while (p) {
+ /* Found in list, replace MSN */
+ if (p->eaz == eazmsn[0]) {
+ save_flags(flags);
+ cli();
+ strcpy(p->msn, &eazmsn[1]);
+ restore_flags(flags);
+ if (DebugVar & 8)
+ printk(KERN_DEBUG
+ "Mapping for EAZ %c changed to %s\n",
+ eazmsn[0],
+ &eazmsn[1]);
+ return 0;
+ }
+ p = p->next;
+ }
+ /* Not found in list, add new entry */
+ p = kmalloc(sizeof(msn_entry), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+ p->eaz = eazmsn[0];
+ strcpy(p->msn, &eazmsn[1]);
+ p->next = card->msn_list;
+ save_flags(flags);
+ cli();
+ card->msn_list = p;
+ restore_flags(flags);
+ if (DebugVar & 8)
+ printk(KERN_DEBUG
+ "Mapping %c -> %s added\n",
+ eazmsn[0],
+ &eazmsn[1]);
+ return 0;
+}
+#endif
+
+static void
+eicon_rcv_dispatch(struct eicon_card *card)
+{
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ case EICON_BUS_PCI:
+ eicon_io_rcv_dispatch(card);
+ break;
+ case EICON_BUS_MCA:
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon_ack_dispatch: Illegal bustype %d\n", card->bus);
+ }
+}
+
+static void
+eicon_ack_dispatch(struct eicon_card *card)
+{
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ case EICON_BUS_PCI:
+ eicon_io_ack_dispatch(card);
+ break;
+ case EICON_BUS_MCA:
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon_ack_dispatch: Illegal bustype %d\n", card->bus);
+ }
+}
+
+static void
+eicon_transmit(struct eicon_card *card)
+{
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ case EICON_BUS_PCI:
+ eicon_io_transmit(card);
+ break;
+ case EICON_BUS_MCA:
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon_transmit: Illegal bustype %d\n", card->bus);
+ }
+}
+
+static int
+eicon_command(eicon_card * card, isdn_ctrl * c)
+{
+ ulong a;
+ eicon_chan *chan;
+ eicon_cdef cdef;
+ isdn_ctrl cmd;
+ char tmp[17];
+ int ret = 0;
+ unsigned long flags;
+
+ switch (c->command) {
+ case ISDN_CMD_IOCTL:
+ memcpy(&a, c->parm.num, sizeof(ulong));
+ switch (c->arg) {
+ case EICON_IOCTL_GETVER:
+ return(EICON_CTRL_VERSION);
+ case EICON_IOCTL_GETTYPE:
+ return(card->type);
+ case EICON_IOCTL_GETMMIO:
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ return (int)card->hwif.isa.shmem;
+#if CONFIG_PCI
+ case EICON_BUS_PCI:
+ return card->hwif.pci.PCIram;
+#endif
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon: Illegal BUS type %d\n",
+ card->bus);
+ ret = -ENODEV;
+ }
+ case EICON_IOCTL_SETMMIO:
+ if (card->flags & EICON_FLAGS_LOADED)
+ return -EBUSY;
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ if (eicon_isa_find_card(a,
+ card->hwif.isa.irq,
+ card->regname) < 0)
+ return -EFAULT;
+ card->hwif.isa.shmem = (eicon_isa_shmem *)a;
+ return 0;
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon: Illegal BUS type %d\n",
+ card->bus);
+ ret = -ENODEV;
+ }
+ case EICON_IOCTL_GETIRQ:
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ return card->hwif.isa.irq;
+#if CONFIG_PCI
+ case EICON_BUS_PCI:
+ return card->hwif.pci.irq;
+#endif
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon: Illegal BUS type %d\n",
+ card->bus);
+ ret = -ENODEV;
+ }
+ case EICON_IOCTL_SETIRQ:
+ if (card->flags & EICON_FLAGS_LOADED)
+ return -EBUSY;
+ if ((a < 2) || (a > 15))
+ return -EFAULT;
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ card->hwif.isa.irq = a;
+ return 0;
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon: Illegal BUS type %d\n",
+ card->bus);
+ ret = -ENODEV;
+ }
+ case EICON_IOCTL_LOADBOOT:
+ if (card->flags & EICON_FLAGS_RUNNING)
+ return -EBUSY;
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ ret = eicon_isa_bootload(
+ &(card->hwif.isa),
+ &(((eicon_codebuf *)a)->isa));
+ break;
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon: Illegal BUS type %d\n",
+ card->bus);
+ ret = -ENODEV;
+ }
+ return ret;
+ case EICON_IOCTL_LOADISA:
+ if (card->flags & EICON_FLAGS_RUNNING)
+ return -EBUSY;
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ ret = eicon_isa_load(
+ &(card->hwif.isa),
+ &(((eicon_codebuf *)a)->isa));
+ if (!ret) {
+ card->flags |= EICON_FLAGS_LOADED;
+ card->flags |= EICON_FLAGS_RUNNING;
+ if (card->hwif.isa.channels > 1) {
+ cmd.command = ISDN_STAT_ADDCH;
+ cmd.driver = card->myid;
+ cmd.arg = card->hwif.isa.channels - 1;
+ card->interface.statcallb(&cmd);
+ }
+ cmd.command = ISDN_STAT_RUN;
+ cmd.driver = card->myid;
+ cmd.arg = 0;
+ card->interface.statcallb(&cmd);
+ }
+ break;
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon: Illegal BUS type %d\n",
+ card->bus);
+ ret = -ENODEV;
+ }
+ return ret;
+
+ case EICON_IOCTL_MANIF:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (!card->Feature & PROTCAP_MANIF)
+ return -ENODEV;
+ ret = eicon_idi_manage(
+ card,
+ (eicon_manifbuf *)a);
+ return ret;
+#if CONFIG_PCI
+ case EICON_IOCTL_LOADPCI:
+ if (card->flags & EICON_FLAGS_RUNNING)
+ return -EBUSY;
+ if (card->bus == EICON_BUS_PCI) {
+ switch(card->type) {
+ case EICON_CTYPE_MAESTRA:
+ ret = eicon_pci_load_bri(
+ &(card->hwif.pci),
+ &(((eicon_codebuf *)a)->pci));
+ break;
+
+ case EICON_CTYPE_MAESTRAP:
+ ret = eicon_pci_load_pri(
+ &(card->hwif.pci),
+ &(((eicon_codebuf *)a)->pci));
+ break;
+ }
+ if (!ret) {
+ card->flags |= EICON_FLAGS_LOADED;
+ card->flags |= EICON_FLAGS_RUNNING;
+ if (card->hwif.pci.channels > 1) {
+ cmd.command = ISDN_STAT_ADDCH;
+ cmd.driver = card->myid;
+ cmd.arg = card->hwif.pci.channels - 1;
+ card->interface.statcallb(&cmd);
+ }
+ cmd.command = ISDN_STAT_RUN;
+ cmd.driver = card->myid;
+ cmd.arg = 0;
+ card->interface.statcallb(&cmd);
+ }
+ return ret;
+ } else return -ENODEV;
+#endif
+#if 0
+ case EICON_IOCTL_SETMSN:
+ if ((ret = copy_from_user(tmp, (char *)a, sizeof(tmp))))
+ return -EFAULT;
+ if ((ret = eicon_set_msn(card, tmp)))
+ return ret;
+#if 0
+ if (card->flags & EICON_FLAGS_RUNNING)
+ return(eicon_capi_manufacturer_req_msn(card));
+#endif
+ return 0;
+#endif
+ case EICON_IOCTL_ADDCARD:
+ if ((ret = copy_from_user(&cdef, (char *)a, sizeof(cdef))))
+ return -EFAULT;
+ if (!(eicon_addcard(0, cdef.membase, cdef.irq, cdef.id)))
+ return -EIO;
+ return 0;
+ case EICON_IOCTL_DEBUGVAR:
+ DebugVar = a;
+ printk(KERN_DEBUG"Eicon: Debug Value set to %ld\n", DebugVar);
+ return 0;
+#ifdef MODULE
+ case EICON_IOCTL_FREEIT:
+ while (MOD_USE_COUNT > 0) MOD_DEC_USE_COUNT;
+ MOD_INC_USE_COUNT;
+ return 0;
+#endif
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ISDN_CMD_DIAL:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x1f)))
+ break;
+ save_flags(flags);
+ cli();
+ if ((chan->fsm_state != EICON_STATE_NULL) && (chan->fsm_state != EICON_STATE_LISTEN)) {
+ restore_flags(flags);
+ if (DebugVar & 1)
+ printk(KERN_WARNING "Dial on channel %d with state %d\n",
+ chan->No, chan->fsm_state);
+ return -EBUSY;
+ }
+ if (card->ptype == ISDN_PTYPE_EURO)
+ tmp[0] = eicon_find_msn(card, c->parm.setup.eazmsn, 1);
+ else
+ tmp[0] = c->parm.setup.eazmsn[0];
+ chan->fsm_state = EICON_STATE_OCALL;
+ chan->callref = 0xffff;
+ restore_flags(flags);
+
+ ret = idi_connect_req(card, chan, c->parm.setup.phone,
+ c->parm.setup.eazmsn,
+ c->parm.setup.si1,
+ c->parm.setup.si2);
+ if (ret) {
+ cmd.driver = card->myid;
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.arg &= 0x1f;
+ card->interface.statcallb(&cmd);
+ }
+ return ret;
+ case ISDN_CMD_ACCEPTD:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x1f)))
+ break;
+ if (chan->fsm_state == EICON_STATE_ICALL) {
+ idi_connect_res(card, chan);
+ }
+ return 0;
+ case ISDN_CMD_ACCEPTB:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ return 0;
+ case ISDN_CMD_HANGUP:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x1f)))
+ break;
+ idi_hangup(card, chan);
+ return 0;
+ case ISDN_CMD_SETEAZ:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x1f)))
+ break;
+ if (strlen(c->parm.num)) {
+ if (card->ptype == ISDN_PTYPE_EURO) {
+ chan->eazmask = eicon_find_msn(card, c->parm.num, 0);
+ }
+ if (card->ptype == ISDN_PTYPE_1TR6) {
+ int i;
+ chan->eazmask = 0;
+ for (i = 0; i < strlen(c->parm.num); i++)
+ if (isdigit(c->parm.num[i]))
+ chan->eazmask |= (1 << (c->parm.num[i] - '0'));
+ }
+ } else
+ chan->eazmask = 0x3ff;
+ eicon_idi_listen_req(card, chan);
+ return 0;
+ case ISDN_CMD_CLREAZ:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x1f)))
+ break;
+ chan->eazmask = 0;
+ eicon_idi_listen_req(card, chan);
+ return 0;
+ case ISDN_CMD_SETL2:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x1f)))
+ break;
+ chan->l2prot = (c->arg >> 8);
+ return 0;
+ case ISDN_CMD_GETL2:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x1f)))
+ break;
+ return chan->l2prot;
+ case ISDN_CMD_SETL3:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if ((c->arg >> 8) != ISDN_PROTO_L3_TRANS) {
+ if (DebugVar & 1)
+ printk(KERN_WARNING "L3 protocol unknown\n");
+ return -1;
+ }
+ if (!(chan = find_channel(card, c->arg & 0x1f)))
+ break;
+ chan->l3prot = (c->arg >> 8);
+ return 0;
+ case ISDN_CMD_GETL3:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x1f)))
+ break;
+ return chan->l3prot;
+ case ISDN_CMD_GETEAZ:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (DebugVar & 1)
+ printk(KERN_DEBUG "eicon CMD_GETEAZ not implemented\n");
+ return 0;
+ case ISDN_CMD_SETSIL:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (DebugVar & 1)
+ printk(KERN_DEBUG "eicon CMD_SETSIL not implemented\n");
+ return 0;
+ case ISDN_CMD_GETSIL:
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ if (DebugVar & 1)
+ printk(KERN_DEBUG "eicon CMD_GETSIL not implemented\n");
+ return 0;
+ case ISDN_CMD_LOCK:
+ MOD_INC_USE_COUNT;
+ return 0;
+ case ISDN_CMD_UNLOCK:
+ MOD_DEC_USE_COUNT;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Find card with given driverId
+ */
+static inline eicon_card *
+eicon_findcard(int driverid)
+{
+ eicon_card *p = cards;
+
+ while (p) {
+ if (p->myid == driverid)
+ return p;
+ p = p->next;
+ }
+ return (eicon_card *) 0;
+}
+
+/*
+ * Wrapper functions for interface to linklevel
+ */
+static int
+if_command(isdn_ctrl * c)
+{
+ eicon_card *card = eicon_findcard(c->driver);
+
+ if (card)
+ return (eicon_command(card, c));
+ printk(KERN_ERR
+ "eicon: if_command %d called with invalid driverId %d!\n",
+ c->command, c->driver);
+ return -ENODEV;
+}
+
+static int
+if_writecmd(const u_char * buf, int len, int user, int id, int channel)
+{
+ eicon_card *card = eicon_findcard(id);
+
+ if (card) {
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ return (len);
+ }
+ printk(KERN_ERR
+ "eicon: if_writecmd called with invalid driverId!\n");
+ return -ENODEV;
+}
+
+static int
+if_readstatus(u_char * buf, int len, int user, int id, int channel)
+{
+#if 0
+ /* Not yet used */
+ eicon_card *card = eicon_findcard(id);
+
+ if (card) {
+ if (!card->flags & EICON_FLAGS_RUNNING)
+ return -ENODEV;
+ return (eicon_readstatus(buf, len, user, card));
+ }
+ printk(KERN_ERR
+ "eicon: if_readstatus called with invalid driverId!\n");
+#endif
+ return -ENODEV;
+}
+
+static int
+if_sendbuf(int id, int channel, int ack, struct sk_buff *skb)
+{
+ eicon_card *card = eicon_findcard(id);
+ eicon_chan *chan;
+
+ if (card) {
+ if (!card->flags & EICON_FLAGS_RUNNING) {
+ dev_kfree_skb(skb);
+ return -ENODEV;
+ }
+ if (!(chan = find_channel(card, channel))) {
+ dev_kfree_skb(skb);
+ return -ENODEV;
+ }
+ if (chan->fsm_state == EICON_STATE_ACTIVE)
+ return (idi_send_data(card, chan, ack, skb));
+ else {
+ dev_kfree_skb(skb);
+ return -ENODEV;
+ }
+ }
+ printk(KERN_ERR
+ "eicon: if_sendbuf called with invalid driverId!\n");
+ dev_kfree_skb(skb);
+ return -ENODEV;
+}
+
+
+/*
+ * Allocate a new card-struct, initialize it
+ * link it into cards-list.
+ */
+static void
+eicon_alloccard(int Type, int membase, int irq, char *id)
+{
+ int i;
+ int j;
+ int qloop;
+ char qid[5];
+ eicon_card *card;
+#if CONFIG_PCI
+ eicon_pci_card *pcic;
+#endif
+
+ qloop = (Type == EICON_CTYPE_QUADRO)?2:0;
+ for (i = 0; i <= qloop; i++) {
+ if (!(card = (eicon_card *) kmalloc(sizeof(eicon_card), GFP_KERNEL))) {
+ printk(KERN_WARNING
+ "eicon: (%s) Could not allocate card-struct.\n", id);
+ return;
+ }
+ memset((char *) card, 0, sizeof(eicon_card));
+ skb_queue_head_init(&card->sndq);
+ skb_queue_head_init(&card->rcvq);
+ skb_queue_head_init(&card->rackq);
+ skb_queue_head_init(&card->sackq);
+ card->snd_tq.routine = (void *) (void *) eicon_transmit;
+ card->snd_tq.data = card;
+ card->rcv_tq.routine = (void *) (void *) eicon_rcv_dispatch;
+ card->rcv_tq.data = card;
+ card->ack_tq.routine = (void *) (void *) eicon_ack_dispatch;
+ card->ack_tq.data = card;
+ card->interface.maxbufsize = 4000;
+ card->interface.command = if_command;
+ card->interface.writebuf_skb = if_sendbuf;
+ card->interface.writecmd = if_writecmd;
+ card->interface.readstat = if_readstatus;
+ card->interface.features =
+ ISDN_FEATURE_L2_X75I |
+ ISDN_FEATURE_L2_HDLC |
+ ISDN_FEATURE_L2_TRANS |
+ ISDN_FEATURE_L3_TRANS |
+ ISDN_FEATURE_P_UNKNOWN;
+ card->interface.hl_hdrlen = 20;
+ card->ptype = ISDN_PTYPE_UNKNOWN;
+ strncpy(card->interface.id, id, sizeof(card->interface.id) - 1);
+ card->myid = -1;
+ card->type = Type;
+ switch (Type) {
+ case EICON_CTYPE_QUADRO:
+ if (membase == -1)
+ membase = EICON_ISA_MEMBASE;
+ if (irq == -1)
+ irq = EICON_ISA_IRQ;
+ card->bus = EICON_BUS_ISA;
+ card->hwif.isa.card = (void *)card;
+ card->hwif.isa.shmem = (eicon_isa_shmem *)(membase + (i+1) * EICON_ISA_QOFFSET);
+ card->hwif.isa.master = 0;
+ strcpy(card->interface.id, id);
+ if (id[strlen(id) - 1] == 'a') {
+ card->interface.id[strlen(id) - 1] = 'a' + i + 1;
+ } else {
+ sprintf(qid, "_%c",'2' + i);
+ strcat(card->interface.id, qid);
+ }
+ printk(KERN_INFO "Eicon: Quadro: Driver-Id %s added.\n",
+ card->interface.id);
+ if (i == 0) {
+ eicon_card *p = cards;
+ while(p) {
+ if ((p->hwif.isa.master) && (p->hwif.isa.irq == irq)) {
+ p->qnext = card;
+ break;
+ }
+ p = p->next;
+ }
+ if (!p) {
+ printk(KERN_WARNING "eicon_alloccard: Quadro Master not found.\n");
+ kfree(card);
+ return;
+ }
+ } else {
+ cards->qnext = card;
+ }
+ card->hwif.isa.irq = irq;
+ card->hwif.isa.type = Type;
+ card->nchannels = 2;
+ card->interface.channels = 1;
+ break;
+#if CONFIG_PCI
+ case EICON_CTYPE_MAESTRA:
+ (eicon_pci_card *)pcic = (eicon_pci_card *)membase;
+ card->bus = EICON_BUS_PCI;
+ card->interface.features |=
+ ISDN_FEATURE_L2_V11096 |
+ ISDN_FEATURE_L2_V11019 |
+ ISDN_FEATURE_L2_V11038 |
+ ISDN_FEATURE_L2_MODEM;
+ card->hwif.pci.card = (void *)card;
+ card->hwif.pci.PCIreg = pcic->PCIreg;
+ card->hwif.pci.PCIcfg = pcic->PCIcfg;
+ card->hwif.pci.master = 1;
+ card->hwif.pci.mvalid = pcic->mvalid;
+ card->hwif.pci.ivalid = 0;
+ card->hwif.pci.irq = irq;
+ card->hwif.pci.type = Type;
+ card->flags = 0;
+ card->nchannels = 2;
+ card->interface.channels = 1;
+ break;
+
+ case EICON_CTYPE_MAESTRAP:
+ (eicon_pci_card *)pcic = (eicon_pci_card *)membase;
+ card->bus = EICON_BUS_PCI;
+ card->interface.features |=
+ ISDN_FEATURE_L2_V11096 |
+ ISDN_FEATURE_L2_V11019 |
+ ISDN_FEATURE_L2_V11038 |
+ ISDN_FEATURE_L2_MODEM;
+ card->hwif.pci.card = (void *)card;
+ card->hwif.pci.shmem = (eicon_pci_shmem *)pcic->shmem;
+ card->hwif.pci.PCIreg = pcic->PCIreg;
+ card->hwif.pci.PCIram = pcic->PCIram;
+ card->hwif.pci.PCIcfg = pcic->PCIcfg;
+ card->hwif.pci.master = 1;
+ card->hwif.pci.mvalid = pcic->mvalid;
+ card->hwif.pci.ivalid = 0;
+ card->hwif.pci.irq = irq;
+ card->hwif.pci.type = Type;
+ card->flags = 0;
+ card->nchannels = 30;
+ card->interface.channels = 1;
+ break;
+#endif
+ case EICON_CTYPE_ISABRI:
+ if (membase == -1)
+ membase = EICON_ISA_MEMBASE;
+ if (irq == -1)
+ irq = EICON_ISA_IRQ;
+ card->bus = EICON_BUS_ISA;
+ card->hwif.isa.card = (void *)card;
+ card->hwif.isa.shmem = (eicon_isa_shmem *)membase;
+ card->hwif.isa.master = 1;
+ card->hwif.isa.irq = irq;
+ card->hwif.isa.type = Type;
+ card->nchannels = 2;
+ card->interface.channels = 1;
+ break;
+ case EICON_CTYPE_ISAPRI:
+ if (membase == -1)
+ membase = EICON_ISA_MEMBASE;
+ if (irq == -1)
+ irq = EICON_ISA_IRQ;
+ card->bus = EICON_BUS_ISA;
+ card->hwif.isa.card = (void *)card;
+ card->hwif.isa.shmem = (eicon_isa_shmem *)membase;
+ card->hwif.isa.master = 1;
+ card->hwif.isa.irq = irq;
+ card->hwif.isa.type = Type;
+ card->nchannels = 30;
+ card->interface.channels = 1;
+ break;
+ default:
+ printk(KERN_WARNING "eicon_alloccard: Invalid type %d\n", Type);
+ kfree(card);
+ return;
+ }
+ if (!(card->bch = (eicon_chan *) kmalloc(sizeof(eicon_chan) * (card->nchannels + 1)
+ , GFP_KERNEL))) {
+ printk(KERN_WARNING
+ "eicon: (%s) Could not allocate bch-struct.\n", id);
+ kfree(card);
+ return;
+ }
+ for (j=0; j< (card->nchannels + 1); j++) {
+ memset((char *)&card->bch[j], 0, sizeof(eicon_chan));
+ card->bch[j].plci = 0x8000;
+ card->bch[j].ncci = 0x8000;
+ card->bch[j].l2prot = ISDN_PROTO_L2_X75I;
+ card->bch[j].l3prot = ISDN_PROTO_L3_TRANS;
+ card->bch[j].e.D3Id = 0;
+ card->bch[j].e.B2Id = 0;
+ card->bch[j].e.Req = 0;
+ card->bch[j].No = j;
+ skb_queue_head_init(&card->bch[j].e.X);
+ skb_queue_head_init(&card->bch[j].e.R);
+ }
+ card->next = cards;
+ cards = card;
+ }
+}
+
+/*
+ * register card at linklevel
+ */
+static int
+eicon_registercard(eicon_card * card)
+{
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ /* TODO something to print */
+ break;
+ case EICON_BUS_PCI:
+#if CONFIG_PCI
+ eicon_pci_printpar(&card->hwif.pci);
+ break;
+#endif
+ case EICON_BUS_MCA:
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon_registercard: Illegal BUS type %d\n",
+ card->bus);
+ return -1;
+ }
+ if (!register_isdn(&card->interface)) {
+ printk(KERN_WARNING
+ "eicon_registercard: Unable to register %s\n",
+ card->interface.id);
+ return -1;
+ }
+ card->myid = card->interface.channels;
+ sprintf(card->regname, "%s", card->interface.id);
+ return 0;
+}
+
+#ifdef MODULE
+static void
+unregister_card(eicon_card * card)
+{
+ isdn_ctrl cmd;
+
+ cmd.command = ISDN_STAT_UNLOAD;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+ switch (card->bus) {
+ case EICON_BUS_ISA:
+ eicon_isa_release(&card->hwif.isa);
+ break;
+ case EICON_BUS_PCI:
+#if CONFIG_PCI
+ eicon_pci_release(&card->hwif.pci);
+ break;
+#endif
+ case EICON_BUS_MCA:
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon: Invalid BUS type %d\n",
+ card->bus);
+ break;
+ }
+}
+#endif /* MODULE */
+
+static void
+eicon_freecard(eicon_card *card) {
+ eicon_clear_msn(card);
+ kfree(card->bch);
+ kfree(card);
+}
+
+int
+eicon_addcard(int Type, int membase, int irq, char *id)
+{
+ eicon_card *p;
+ eicon_card *q = NULL;
+ int registered;
+ int added = 0;
+ int failed = 0;
+
+ if (!Type) /* ISA */
+ if ((Type = eicon_isa_find_card(membase, irq, id)) < 0)
+ return 0;
+ eicon_alloccard(Type, membase, irq, id);
+ p = cards;
+ while (p) {
+ registered = 0;
+ if (!p->interface.statcallb) {
+ /* Not yet registered.
+ * Try to register and activate it.
+ */
+ added++;
+ switch (p->bus) {
+ case EICON_BUS_ISA:
+ if (eicon_registercard(p))
+ break;
+ registered = 1;
+ break;
+ case EICON_BUS_PCI:
+#if CONFIG_PCI
+ if (eicon_registercard(p))
+ break;
+ registered = 1;
+ break;
+#endif
+ case EICON_BUS_MCA:
+ default:
+ if (DebugVar & 1)
+ printk(KERN_WARNING
+ "eicon: addcard: Invalid BUS type %d\n",
+ p->bus);
+ }
+ } else
+ /* Card already registered */
+ registered = 1;
+ if (registered) {
+ /* Init OK, next card ... */
+ q = p;
+ p = p->next;
+ } else {
+ /* registering failed, remove card from list, free memory */
+ printk(KERN_WARNING
+ "eicon: Initialization of %s failed\n",
+ p->interface.id);
+ if (q) {
+ q->next = p->next;
+ eicon_freecard(p);
+ p = q->next;
+ } else {
+ cards = p->next;
+ eicon_freecard(p);
+ p = cards;
+ }
+ failed++;
+ }
+ }
+ return (added - failed);
+}
+
+#define DRIVERNAME "Eicon active ISDN driver"
+#define DRIVERRELEASE "1"
+
+#ifdef MODULE
+#define eicon_init init_module
+#endif
+
+__initfunc(int
+eicon_init(void))
+{
+ int tmp = 0;
+ int release = 0;
+ char tmprev[50];
+
+ DebugVar = 1;
+
+ printk(KERN_INFO "%s Rev: ", DRIVERNAME);
+ strcpy(tmprev, eicon_revision);
+ printk("%s/", eicon_getrev(tmprev));
+ release += getrel(tmprev);
+ strcpy(tmprev, eicon_pci_revision);
+ printk("%s/", eicon_getrev(tmprev));
+ release += getrel(tmprev);
+ strcpy(tmprev, eicon_isa_revision);
+ printk("%s/", eicon_getrev(tmprev));
+ release += getrel(tmprev);
+ strcpy(tmprev, eicon_idi_revision);
+ printk("%s\n", eicon_getrev(tmprev));
+ release += getrel(tmprev);
+ sprintf(tmprev,"%d", release);
+ printk(KERN_INFO "%s Release: %s.%s\n", DRIVERNAME,
+ DRIVERRELEASE, tmprev);
+
+ tmp = eicon_addcard(0, membase, irq, id);
+#if CONFIG_PCI
+ tmp += eicon_pci_find_card(id);
+#endif
+ if (!cards) {
+#ifdef MODULE
+ printk(KERN_INFO "Eicon: No cards defined, driver not loaded !\n");
+#endif
+ return -ENODEV;
+
+ } else
+ printk(KERN_INFO "Eicon: %d card%s added\n", tmp, (tmp>1)?"s":"");
+ /* No symbols to export, hide all symbols */
+ EXPORT_NO_SYMBOLS;
+ return 0;
+}
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+ eicon_card *card = cards;
+ eicon_card *last;
+ while (card) {
+ unregister_card(card);
+ card = card->next;
+ }
+ card = cards;
+ while (card) {
+ last = card;
+ card = card->next;
+ eicon_freecard(last);
+ }
+ printk(KERN_INFO "%s unloaded\n", DRIVERNAME);
+}
+
+#else
+__initfunc(void
+eicon_setup(char *str, int *ints))
+{
+ int i, argc;
+
+ argc = ints[0];
+ i = 1;
+ if (argc) {
+ membase = irq = -1;
+ if (argc) {
+ membase = ints[i];
+ i++;
+ argc--;
+ }
+ if (argc) {
+ irq = ints[i];
+ i++;
+ argc--;
+ }
+ if (strlen(str)) {
+ strcpy(id, str);
+ } else {
+ strcpy(id, "eicon");
+ }
+ /* eicon_addcard(0, membase, irq, id); */
+ printk(KERN_INFO "eicon: membase=0x%x irq=%d id=%s\n", membase, irq, id);
+ }
+}
+#endif
diff --git a/drivers/isdn/eicon/eicon_pci.c b/drivers/isdn/eicon/eicon_pci.c
new file mode 100644
index 000000000..3d07167ce
--- /dev/null
+++ b/drivers/isdn/eicon/eicon_pci.c
@@ -0,0 +1,952 @@
+/* $Id: eicon_pci.c,v 1.6 1999/04/01 12:48:37 armin Exp $
+ *
+ * ISDN low-level module for Eicon.Diehl active ISDN-Cards.
+ * Hardware-specific code for PCI cards.
+ *
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * Thanks to Eicon Technology Diehl GmbH & Co. oHG for
+ * documents, informations and hardware.
+ *
+ * Deutsche Telekom AG for S2M support.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon_pci.c,v $
+ * Revision 1.6 1999/04/01 12:48:37 armin
+ * Changed some log outputs.
+ *
+ * Revision 1.5 1999/03/29 11:19:49 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.4 1999/03/02 12:37:48 armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.3 1999/01/24 20:14:24 armin
+ * Changed and added debug stuff.
+ * Better data sending. (still problems with tty's flip buffer)
+ *
+ * Revision 1.2 1999/01/10 18:46:06 armin
+ * Bug with wrong values in HLC fixed.
+ * Bytes to send are counted and limited now.
+ *
+ * Revision 1.1 1999/01/01 18:09:45 armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/pci.h>
+
+#include "eicon.h"
+#include "eicon_pci.h"
+
+
+char *eicon_pci_revision = "$Revision: 1.6 $";
+
+#if CONFIG_PCI /* intire stuff is only for PCI */
+
+#undef EICON_PCI_DEBUG
+
+int eicon_pci_find_card(char *ID)
+{
+ if (pci_present()) {
+ struct pci_dev *pdev = NULL;
+ int pci_nextindex=0, pci_cards=0, pci_akt=0;
+ int pci_type = PCI_MAESTRA;
+ int NoMorePCICards = FALSE;
+ char *ram, *reg, *cfg;
+ unsigned int pram=0, preg=0, pcfg=0;
+ char did[12];
+ eicon_pci_card *aparms;
+
+ if (!(aparms = (eicon_pci_card *) kmalloc(sizeof(eicon_pci_card), GFP_KERNEL))) {
+ printk(KERN_WARNING
+ "eicon_pci: Could not allocate card-struct.\n");
+ return 0;
+ }
+
+ for (pci_cards = 0; pci_cards < 0x0f; pci_cards++)
+ {
+ do {
+ if ((pdev = pci_find_device(PCI_VENDOR_EICON,
+ pci_type,
+ pdev)))
+ {
+ pci_nextindex++;
+ break;
+ }
+ else {
+ pci_nextindex = 0;
+ switch (pci_type) /* switch to next card type */
+ {
+ case PCI_MAESTRA:
+ pci_type = PCI_MAESTRAQ; break;
+ case PCI_MAESTRAQ:
+ pci_type = PCI_MAESTRAQ_U; break;
+ case PCI_MAESTRAQ_U:
+ pci_type = PCI_MAESTRAP; break;
+ default:
+ case PCI_MAESTRAP:
+ NoMorePCICards = TRUE;
+ }
+ }
+ }
+ while (!NoMorePCICards);
+ if (NoMorePCICards)
+ {
+ if (pci_cards < 1) {
+ printk(KERN_INFO "Eicon: No supported PCI cards found.\n");
+ kfree(aparms);
+ return 0;
+ }
+ else
+ {
+ printk(KERN_INFO "Eicon: %d PCI card%s registered.\n",
+ pci_cards, (pci_cards > 1) ? "s":"");
+ kfree(aparms);
+ return (pci_cards);
+ }
+ }
+
+ pci_akt = 0;
+ switch(pci_type)
+ {
+ case PCI_MAESTRA:
+ printk(KERN_INFO "Eicon: DIVA Server BRI/PCI detected !\n");
+ aparms->type = EICON_CTYPE_MAESTRA;
+
+ aparms->irq = pdev->irq;
+ preg = pdev->base_address[2] & 0xfffffffc;
+ pcfg = pdev->base_address[1] & 0xffffff80;
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: irq=%d\n", aparms->irq);
+ printk(KERN_DEBUG "eicon_pci: reg=0x%x\n", preg);
+ printk(KERN_DEBUG "eicon_pci: cfg=0x%x\n", pcfg);
+#endif
+ pci_akt = 1;
+ break;
+
+ case PCI_MAESTRAQ:
+ case PCI_MAESTRAQ_U:
+ printk(KERN_ERR "Eicon: DIVA Server 4BRI/PCI detected but not supported !\n");
+ pci_cards--;
+ pci_akt = 0;
+ break;
+
+ case PCI_MAESTRAP:
+ printk(KERN_INFO "Eicon: DIVA Server PRI/PCI detected !\n");
+ aparms->type = EICON_CTYPE_MAESTRAP; /*includes 9M,30M*/
+ aparms->irq = pdev->irq;
+ pram = pdev->base_address[0] & 0xfffff000;
+ preg = pdev->base_address[2] & 0xfffff000;
+ pcfg = pdev->base_address[4] & 0xfffff000;
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: irq=%d\n", aparms->irq);
+ printk(KERN_DEBUG "eicon_pci: ram=0x%x\n",
+ (pram));
+ printk(KERN_DEBUG "eicon_pci: reg=0x%x\n",
+ (preg));
+ printk(KERN_DEBUG "eicon_pci: cfg=0x%x\n",
+ (pcfg));
+#endif
+ pci_akt = 1;
+ break;
+ default:
+ printk(KERN_ERR "eicon_pci: Unknown PCI card detected !\n");
+ pci_cards--;
+ pci_akt = 0;
+ break;
+ }
+
+ if (pci_akt) {
+ /* remapping memory */
+ switch(pci_type)
+ {
+ case PCI_MAESTRA:
+ aparms->PCIreg = (unsigned int) preg;
+ aparms->PCIcfg = (unsigned int) pcfg;
+ if (check_region((aparms->PCIreg), 0x20)) {
+ printk(KERN_WARNING "eicon_pci: reg port already in use !\n");
+ aparms->PCIreg = 0;
+ break;
+ } else {
+ request_region(aparms->PCIreg, 0x20, "eicon reg");
+ }
+ if (check_region((aparms->PCIcfg), 0x100)) {
+ printk(KERN_WARNING "eicon_pci: cfg port already in use !\n");
+ aparms->PCIcfg = 0;
+ break;
+ } else {
+ request_region(aparms->PCIcfg, 0x100, "eicon cfg");
+ }
+ break;
+ case PCI_MAESTRAQ:
+ case PCI_MAESTRAQ_U:
+ case PCI_MAESTRAP:
+ aparms->shmem = (eicon_pci_shmem *) ioremap(pram, 0x10000);
+ ram = (u8 *) ((u32)aparms->shmem + MP_SHARED_RAM_OFFSET);
+ reg = ioremap(preg, 0x4000);
+ cfg = ioremap(pcfg, 0x1000);
+ aparms->PCIram = (unsigned int) ram;
+ aparms->PCIreg = (unsigned int) reg;
+ aparms->PCIcfg = (unsigned int) cfg;
+ break;
+ }
+ if ((!aparms->PCIreg) || (!aparms->PCIcfg)) {
+ printk(KERN_ERR "eicon_pci: Card could not be added !\n");
+ pci_cards--;
+ } else {
+ aparms->mvalid = 1;
+
+ sprintf(did, "%s%d", (strlen(ID) < 1) ? "eicon":ID, pci_cards);
+
+ printk(KERN_INFO "%s: DriverID: '%s'\n",eicon_ctype_name[aparms->type] , did);
+
+ if (!(eicon_addcard(aparms->type, (int) aparms, aparms->irq, did))) {
+ printk(KERN_ERR "eicon_pci: Card could not be added !\n");
+ pci_cards--;
+ }
+ }
+ }
+
+ }
+ } else
+ printk(KERN_ERR "eicon_pci: Kernel compiled with PCI but no PCI-bios found !\n");
+ return 0;
+}
+
+/*
+ * Checks protocol file id for "F#xxxx" string fragment to
+ * extract the features, supported by this protocol version.
+ * binary representation of the feature string value is returned
+ * in *value. The function returns 0 if feature string was not
+ * found or has a wrong format, else 1.
+ */
+static int GetProtFeatureValue(char *sw_id, int *value)
+{
+ __u8 i, offset;
+
+ while (*sw_id)
+ {
+ if ((sw_id[0] == 'F') && (sw_id[1] == '#'))
+ {
+ sw_id = &sw_id[2];
+ for (i=0, *value=0; i<4; i++, sw_id++)
+ {
+ if ((*sw_id >= '0') && (*sw_id <= '9'))
+ {
+ offset = '0';
+ }
+ else if ((*sw_id >= 'A') && (*sw_id <= 'F'))
+ {
+ offset = 'A' + 10;
+ }
+ else if ((*sw_id >= 'a') && (*sw_id <= 'f'))
+ {
+ offset = 'a' + 10;
+ }
+ else
+ {
+ return 0;
+ }
+ *value |= (*sw_id - offset) << (4*(3-i));
+ }
+ return 1;
+ }
+ else
+ {
+ sw_id++;
+ }
+ }
+ return 0;
+}
+
+
+void
+eicon_pci_printpar(eicon_pci_card *card) {
+ switch (card->type) {
+ case EICON_CTYPE_MAESTRA:
+ printk(KERN_INFO "%s at 0x%x / 0x%x, irq %d\n",
+ eicon_ctype_name[card->type],
+ (unsigned int)card->PCIreg,
+ (unsigned int)card->PCIcfg,
+ card->irq);
+ break;
+ case EICON_CTYPE_MAESTRAQ:
+ case EICON_CTYPE_MAESTRAQ_U:
+ case EICON_CTYPE_MAESTRAP:
+ printk(KERN_INFO "%s at 0x%x, irq %d\n",
+ eicon_ctype_name[card->type],
+ (unsigned int)card->shmem,
+ card->irq);
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_INFO "eicon_pci: remapped ram= 0x%x\n",(unsigned int)card->PCIram);
+ printk(KERN_INFO "eicon_pci: remapped reg= 0x%x\n",(unsigned int)card->PCIreg);
+ printk(KERN_INFO "eicon_pci: remapped cfg= 0x%x\n",(unsigned int)card->PCIcfg);
+#endif
+ break;
+ }
+}
+
+
+static void
+eicon_pci_release_shmem(eicon_pci_card *card) {
+ if (!card->master)
+ return;
+ if (card->mvalid) {
+ switch (card->type) {
+ case EICON_CTYPE_MAESTRA:
+ /* reset board */
+ outb(0, card->PCIcfg + 0x4c); /* disable interrupts from PLX */
+ outb(0, card->PCIreg + M_RESET);
+ SLEEP(20);
+ outb(0, card->PCIreg + M_ADDRH);
+ outw(0, card->PCIreg + M_ADDR);
+ outw(0, card->PCIreg + M_DATA);
+
+ release_region(card->PCIreg, 0x20);
+ release_region(card->PCIcfg, 0x100);
+ break;
+ case EICON_CTYPE_MAESTRAQ:
+ case EICON_CTYPE_MAESTRAQ_U:
+ case EICON_CTYPE_MAESTRAP:
+ /* reset board */
+ writeb(_MP_RISC_RESET | _MP_LED1 | _MP_LED2, card->PCIreg + MP_RESET);
+ SLEEP(20);
+ writeb(0, card->PCIreg + MP_RESET);
+ SLEEP(20);
+
+ iounmap((void *)card->shmem);
+ iounmap((void *)card->PCIreg);
+ iounmap((void *)card->PCIcfg);
+ break;
+ }
+ }
+ card->mvalid = 0;
+}
+
+static void
+eicon_pci_release_irq(eicon_pci_card *card) {
+ if (!card->master)
+ return;
+ if (card->ivalid)
+ free_irq(card->irq, card);
+ card->ivalid = 0;
+}
+
+void
+eicon_pci_release(eicon_pci_card *card) {
+ eicon_pci_release_irq(card);
+ eicon_pci_release_shmem(card);
+}
+
+/*
+ * Upload buffer content to adapters shared memory
+ * on verify error, 1 is returned and a message is printed on screen
+ * else 0 is returned
+ * Can serve IO-Type and Memory type adapters
+ */
+int eicon_upload(t_dsp_download_space *p_para,
+ __u16 length, /* byte count */
+ __u8 *buffer,
+ int verify)
+{
+ __u32 i, dwdata = 0, val = 0, timeout;
+ __u16 data;
+ eicon_pci_boot *boot = 0;
+
+ switch (p_para->type) /* actions depend on type of union */
+ {
+ case DL_PARA_IO_TYPE:
+ for (i=0; i<length; i+=2)
+ {
+ outb ((u8) ((p_para->dat.io.r3addr + i) >> 16), p_para->dat.io.ioADDRH);
+ outw ((u16) (p_para->dat.io.r3addr + i), p_para->dat.io.ioADDR);
+ /* outw (((u16 *)code)[i >> 1], p_para->dat.io.ioDATA); */
+ outw (*(u16 *)&buffer[i], p_para->dat.io.ioDATA);
+ }
+ if (verify) /* check written block */
+ {
+ for (i=0; i<length; i+=2)
+ {
+ outb ((u8) ((p_para->dat.io.r3addr + i) >> 16), p_para->dat.io.ioADDRH);
+ outw ((u16) (p_para->dat.io.r3addr + i), p_para->dat.io.ioADDR);
+ data = inw(p_para->dat.io.ioDATA);
+ if (data != *(u16 *)&buffer[i])
+ {
+ p_para->dat.io.r3addr += i;
+ p_para->dat.io.BadData = data;
+ p_para->dat.io.GoodData = *(u16 *)&buffer[i];
+ return 1;
+ }
+ }
+ }
+ break;
+
+ case DL_PARA_MEM_TYPE:
+ boot = p_para->dat.mem.boot;
+ writel(p_para->dat.mem.r3addr, &boot->addr);
+ for (i=0; i<length; i+=4)
+ {
+ writel(((u32 *)buffer)[i >> 2], &boot->data[i]);
+ }
+ if (verify) /* check written block */
+ {
+ for (i=0; i<length; i+=4)
+ {
+ dwdata = readl(&boot->data[i]);
+ if (((u32 *)buffer)[i >> 2] != dwdata)
+ {
+ p_para->dat.mem.r3addr += i;
+ p_para->dat.mem.BadData = dwdata;
+ p_para->dat.mem.GoodData = ((u32 *)buffer)[i >> 2];
+ return 1;
+ }
+ }
+ }
+ writel(((length + 3) / 4), &boot->len); /* len in dwords */
+ writel(2, &boot->cmd);
+
+ timeout = jiffies + 20;
+ while (timeout > jiffies) {
+ val = readl(&boot->cmd);
+ if (!val) break;
+ SLEEP(2);
+ }
+ if (val)
+ {
+ p_para->dat.mem.timeout = 1;
+ return 1;
+ }
+ break;
+ }
+ return 0;
+}
+
+
+/* show header information of code file */
+static
+int eicon_pci_print_hdr(unsigned char *code, int offset)
+{
+ unsigned char hdr[80];
+ int i, fvalue = 0;
+
+ i = 0;
+ while ((i < (sizeof(hdr) -1))
+ && (code[offset + i] != '\0')
+ && (code[offset + i] != '\r')
+ && (code[offset + i] != '\n'))
+ {
+ hdr[i] = code[offset + i];
+ i++;
+ }
+ hdr[i] = '\0';
+ printk(KERN_DEBUG "Eicon: loading %s\n", hdr);
+ if (GetProtFeatureValue(hdr, &fvalue)) return(fvalue);
+ else return(0);
+}
+
+
+/*
+ * Configure a card, download code into BRI card,
+ * check if we get interrupts and return 0 on succes.
+ * Return -ERRNO on failure.
+ */
+int
+eicon_pci_load_bri(eicon_pci_card *card, eicon_pci_codebuf *cb) {
+ int i,j;
+ int timeout;
+ unsigned int offset, offp=0, size, length;
+ int signature = 0;
+ int FeatureValue = 0;
+ eicon_pci_codebuf cbuf;
+ t_dsp_download_space dl_para;
+ t_dsp_download_desc dsp_download_table;
+ unsigned char *code;
+ unsigned int reg;
+ unsigned int cfg;
+
+ if (copy_from_user(&cbuf, cb, sizeof(eicon_pci_codebuf)))
+ return -EFAULT;
+
+ reg = card->PCIreg;
+ cfg = card->PCIcfg;
+
+ /* reset board */
+ outb(0, reg + M_RESET);
+ SLEEP(10);
+ outb(0, reg + M_ADDRH);
+ outw(0, reg + M_ADDR);
+ outw(0, reg + M_DATA);
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: reset card\n");
+#endif
+
+ /* clear shared memory */
+ outb(0xff, reg + M_ADDRH);
+ outw(0, reg + M_ADDR);
+ for(i = 0; i < 0xffff; i++) outw(0, reg + M_DATA);
+ SLEEP(10);
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: clear shared memory\n");
+#endif
+
+ /* download protocol and dsp file */
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: downloading firmware...\n");
+#endif
+
+ /* Allocate code-buffer */
+ if (!(code = kmalloc(400, GFP_KERNEL))) {
+ printk(KERN_WARNING "eicon_pci_boot: Couldn't allocate code buffer\n");
+ return -ENOMEM;
+ }
+
+ /* prepare protocol upload */
+ dl_para.type = DL_PARA_IO_TYPE;
+ dl_para.dat.io.ioADDR = reg + M_ADDR;
+ dl_para.dat.io.ioADDRH = reg + M_ADDRH;
+ dl_para.dat.io.ioDATA = reg + M_DATA;
+
+ for (j = 0; j <= cbuf.dsp_code_num; j++)
+ {
+ if (j == 0) size = cbuf.protocol_len;
+ else size = cbuf.dsp_code_len[j];
+
+ offset = 0;
+
+ if (j == 0) dl_para.dat.io.r3addr = 0;
+ if (j == 1) dl_para.dat.io.r3addr = M_DSP_CODE_BASE +
+ ((sizeof(__u32) + (sizeof(dsp_download_table) * 35) + 3) &0xfffffffc);
+ if (j == 2) dl_para.dat.io.r3addr = M_DSP_CODE_BASE;
+ if (j == 3) dl_para.dat.io.r3addr = M_DSP_CODE_BASE + sizeof(__u32);
+
+ do /* download block of up to 400 bytes */
+ {
+ length = ((size - offset) >= 400) ? 400 : (size - offset);
+
+ if (copy_from_user(code, (&cb->code) + offp + offset, length)) {
+ kfree(code);
+ return -EFAULT;
+ }
+
+ if ((offset == 0) && (j < 2)) {
+ FeatureValue = eicon_pci_print_hdr(code, j ? 0x00 : 0x80);
+#ifdef EICON_PCI_DEBUG
+ if (FeatureValue) printk(KERN_DEBUG "eicon_pci: Feature Value : 0x%04x.\n", FeatureValue);
+#endif
+ if ((j==0) && (!(FeatureValue & PROTCAP_TELINDUS))) {
+ printk(KERN_ERR "eicon_pci: Protocol Code cannot handle Telindus\n");
+ kfree(code);
+ return -EFAULT;
+ }
+ ((eicon_card *)card->card)->Feature = FeatureValue;
+ }
+
+ if (eicon_upload(&dl_para, length, code, 1))
+ {
+ printk(KERN_ERR "eicon_pci: code block check failed at 0x%x !\n",dl_para.dat.io.r3addr);
+ kfree(code);
+ return -EIO;
+ }
+ /* move onto next block */
+ offset += length;
+ dl_para.dat.io.r3addr += length;
+ } while (offset < size);
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "Eicon: %d bytes loaded.\n", offset);
+#endif
+ offp += size;
+ }
+ kfree(code);
+
+ /* clear signature */
+ outb(0xff, reg + M_ADDRH);
+ outw(0x1e, reg + M_ADDR);
+ outw(0, reg + M_DATA);
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: copy configuration data into shared memory...\n");
+#endif
+ /* copy configuration data into shared memory */
+ outw(8, reg + M_ADDR); outb(cbuf.tei, reg + M_DATA);
+ outw(9, reg + M_ADDR); outb(cbuf.nt2, reg + M_DATA);
+ outw(10,reg + M_ADDR); outb(0, reg + M_DATA);
+ outw(11,reg + M_ADDR); outb(cbuf.WatchDog, reg + M_DATA);
+ outw(12,reg + M_ADDR); outb(cbuf.Permanent, reg + M_DATA);
+ outw(13,reg + M_ADDR); outb(0, reg + M_DATA); /* XInterface */
+ outw(14,reg + M_ADDR); outb(cbuf.StableL2, reg + M_DATA);
+ outw(15,reg + M_ADDR); outb(cbuf.NoOrderCheck, reg + M_DATA);
+ outw(16,reg + M_ADDR); outb(0, reg + M_DATA); /* HandsetType */
+ outw(17,reg + M_ADDR); outb(0, reg + M_DATA); /* SigFlags */
+ outw(18,reg + M_ADDR); outb(cbuf.LowChannel, reg + M_DATA);
+ outw(19,reg + M_ADDR); outb(cbuf.ProtVersion, reg + M_DATA);
+ outw(20,reg + M_ADDR); outb(cbuf.Crc4, reg + M_DATA);
+ outw(21,reg + M_ADDR); outb((cbuf.Loopback) ? 2:0, reg + M_DATA);
+
+ for (i=0;i<32;i++)
+ {
+ outw( 32+i, reg + M_ADDR); outb(cbuf.l[0].oad[i], reg + M_DATA);
+ outw( 64+i, reg + M_ADDR); outb(cbuf.l[0].osa[i], reg + M_DATA);
+ outw( 96+i, reg + M_ADDR); outb(cbuf.l[0].spid[i], reg + M_DATA);
+ outw(128+i, reg + M_ADDR); outb(cbuf.l[1].oad[i], reg + M_DATA);
+ outw(160+i, reg + M_ADDR); outb(cbuf.l[1].osa[i], reg + M_DATA);
+ outw(192+i, reg + M_ADDR); outb(cbuf.l[1].spid[i], reg + M_DATA);
+ }
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_ERR "eicon_pci: starting CPU...\n");
+#endif
+ /* let the CPU run */
+ outw(0x08, reg + M_RESET);
+
+ timeout = jiffies + (5*HZ);
+ while (timeout > jiffies) {
+ outw(0x1e, reg + M_ADDR);
+ signature = inw(reg + M_DATA);
+ if (signature == DIVAS_SIGNATURE) break;
+ SLEEP(2);
+ }
+ if (signature != DIVAS_SIGNATURE)
+ {
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_ERR "eicon_pci: signature 0x%x expected 0x%x\n",signature,DIVAS_SIGNATURE);
+#endif
+ printk(KERN_ERR "eicon_pci: Timeout, protocol code not running !\n");
+ return -EIO;
+ }
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: Protocol code running, signature OK\n");
+#endif
+
+ /* get serial number and number of channels supported by card */
+ outb(0xff, reg + M_ADDRH);
+ outw(0x3f6, reg + M_ADDR);
+ card->channels = inw(reg + M_DATA);
+ card->serial = (u32)inw(cfg + 0x22) << 16 | (u32)inw(cfg + 0x26);
+ printk(KERN_INFO "Eicon: Supported channels : %d\n", card->channels);
+ printk(KERN_INFO "Eicon: Card serial no. = %lu\n", card->serial);
+
+ /* test interrupt */
+ card->irqprobe = 1;
+
+ if (!card->ivalid) {
+ if (request_irq(card->irq, &eicon_irq, 0, "Eicon PCI ISDN", card->card))
+ {
+ printk(KERN_ERR "eicon_pci: Couldn't request irq %d\n", card->irq);
+ return -EIO;
+ }
+ }
+ card->ivalid = 1;
+
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: testing interrupt\n");
+#endif
+ /* Trigger an interrupt and check if it is delivered */
+ outb(0x41, cfg + 0x4c); /* enable PLX for interrupts */
+ outb(0x89, reg + M_RESET); /* place int request */
+
+ timeout = jiffies + 20;
+ while (timeout > jiffies) {
+ if (card->irqprobe != 1) break;
+ SLEEP(5);
+ }
+ if (card->irqprobe == 1) {
+ free_irq(card->irq, card);
+ card->ivalid = 0;
+ printk(KERN_ERR "eicon_pci: Getting no interrupts !\n");
+ return -EIO;
+ }
+
+ /* initializing some variables */
+ ((eicon_card *)card->card)->ReadyInt = 0;
+ for(j=0; j<256; j++) ((eicon_card *)card->card)->IdTable[j] = NULL;
+ for(j=0; j< (card->channels + 1); j++) {
+ ((eicon_card *)card->card)->bch[j].e.busy = 0;
+ ((eicon_card *)card->card)->bch[j].e.D3Id = 0;
+ ((eicon_card *)card->card)->bch[j].e.B2Id = 0;
+ ((eicon_card *)card->card)->bch[j].e.ref = 0;
+ ((eicon_card *)card->card)->bch[j].e.Req = 0;
+ ((eicon_card *)card->card)->bch[j].e.complete = 1;
+ ((eicon_card *)card->card)->bch[j].fsm_state = EICON_STATE_NULL;
+ }
+
+ printk(KERN_INFO "Eicon: Card successfully started\n");
+
+ return 0;
+}
+
+
+/*
+ * Configure a card, download code into PRI card,
+ * check if we get interrupts and return 0 on succes.
+ * Return -ERRNO on failure.
+ */
+int
+eicon_pci_load_pri(eicon_pci_card *card, eicon_pci_codebuf *cb) {
+ eicon_pci_boot *boot;
+ eicon_pr_ram *prram;
+ int i,j;
+ int timeout;
+ int FeatureValue = 0;
+ unsigned int offset, offp=0, size, length;
+ unsigned long int signature = 0;
+ t_dsp_download_space dl_para;
+ t_dsp_download_desc dsp_download_table;
+ eicon_pci_codebuf cbuf;
+ unsigned char *code;
+ unsigned char req_int;
+ char *ram, *reg, *cfg;
+
+ if (copy_from_user(&cbuf, cb, sizeof(eicon_pci_codebuf)))
+ return -EFAULT;
+
+ boot = &card->shmem->boot;
+ ram = (char *)card->PCIram;
+ reg = (char *)card->PCIreg;
+ cfg = (char *)card->PCIcfg;
+ prram = (eicon_pr_ram *)ram;
+
+ /* reset board */
+ writeb(_MP_RISC_RESET | _MP_LED1 | _MP_LED2, card->PCIreg + MP_RESET);
+ SLEEP(20);
+ writeb(0, card->PCIreg + MP_RESET);
+ SLEEP(20);
+
+ /* set command count to 0 */
+ writel(0, &boot->reserved);
+
+ /* check if CPU increments the life word */
+ i = readw(&boot->live);
+ SLEEP(20);
+ if (i == readw(&boot->live)) {
+ printk(KERN_ERR "eicon_pci: card is reset, but CPU not running !\n");
+ return -EIO;
+ }
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: reset card OK (CPU running)\n");
+#endif
+
+ /* download firmware : DSP and Protocol */
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: downloading firmware...\n");
+#endif
+
+ /* Allocate code-buffer */
+ if (!(code = kmalloc(400, GFP_KERNEL))) {
+ printk(KERN_WARNING "eicon_pci_boot: Couldn't allocate code buffer\n");
+ return -ENOMEM;
+ }
+
+ /* prepare protocol upload */
+ dl_para.type = DL_PARA_MEM_TYPE;
+ dl_para.dat.mem.boot = boot;
+
+ for (j = 0; j <= cbuf.dsp_code_num; j++)
+ {
+ if (j==0) size = cbuf.protocol_len;
+ else size = cbuf.dsp_code_len[j];
+
+ if (j==1) writel(MP_DSP_ADDR, &boot->addr); /* DSP code entry point */
+
+ if (j == 0) dl_para.dat.io.r3addr = MP_PROTOCOL_ADDR;
+ if (j == 1) dl_para.dat.io.r3addr = MP_DSP_CODE_BASE +
+ ((sizeof(__u32) + (sizeof(dsp_download_table) * 35) + 3) &0xfffffffc);
+ if (j == 2) dl_para.dat.io.r3addr = MP_DSP_CODE_BASE;
+ if (j == 3) dl_para.dat.io.r3addr = MP_DSP_CODE_BASE + sizeof(__u32);
+
+ offset = 0;
+ do /* download block of up to 400 bytes */
+ {
+ length = ((size - offset) >= 400) ? 400 : (size - offset);
+
+ if (copy_from_user(code, (&cb->code) + offp + offset, length)) {
+ kfree(code);
+ return -EFAULT;
+ }
+
+ if ((offset == 0) && (j < 2)) {
+ FeatureValue = eicon_pci_print_hdr(code, j ? 0x00 : 0x80);
+#ifdef EICON_PCI_DEBUG
+ if (FeatureValue) printk(KERN_DEBUG "eicon_pci: Feature Value : 0x%x.\n", FeatureValue);
+#endif
+ if ((j==0) && (!(FeatureValue & PROTCAP_TELINDUS))) {
+ printk(KERN_ERR "eicon_pci: Protocol Code cannot handle Telindus\n");
+ kfree(code);
+ return -EFAULT;
+ }
+ ((eicon_card *)card->card)->Feature = FeatureValue;
+ }
+
+ if (eicon_upload(&dl_para, length, code, 1))
+ {
+ if (dl_para.dat.mem.timeout == 0)
+ printk(KERN_ERR "eicon_pci: code block check failed at 0x%x !\n",dl_para.dat.io.r3addr);
+ else
+ printk(KERN_ERR "eicon_pci: timeout, no ACK to load !\n");
+ kfree(code);
+ return -EIO;
+ }
+
+ /* move onto next block */
+ offset += length;
+ dl_para.dat.mem.r3addr += length;
+ } while (offset < size);
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: %d bytes loaded.\n", offset);
+#endif
+ offp += size;
+ }
+ kfree(code);
+
+ /* initialize the adapter data structure */
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: copy configuration data into shared memory...\n");
+#endif
+ /* clear out config space */
+ for (i = 0; i < 256; i++) writeb(0, &ram[i]);
+
+ /* copy configuration down to the card */
+ writeb(cbuf.tei, &ram[8]);
+ writeb(cbuf.nt2, &ram[9]);
+ writeb(0, &ram[10]);
+ writeb(cbuf.WatchDog, &ram[11]);
+ writeb(cbuf.Permanent, &ram[12]);
+ writeb(cbuf.XInterface, &ram[13]);
+ writeb(cbuf.StableL2, &ram[14]);
+ writeb(cbuf.NoOrderCheck, &ram[15]);
+ writeb(cbuf.HandsetType, &ram[16]);
+ writeb(0, &ram[17]);
+ writeb(cbuf.LowChannel, &ram[18]);
+ writeb(cbuf.ProtVersion, &ram[19]);
+ writeb(cbuf.Crc4, &ram[20]);
+ for (i = 0; i < 32; i++)
+ {
+ writeb(cbuf.l[0].oad[i], &ram[32 + i]);
+ writeb(cbuf.l[0].osa[i], &ram[64 + i]);
+ writeb(cbuf.l[0].spid[i], &ram[96 + i]);
+ writeb(cbuf.l[1].oad[i], &ram[128 + i]);
+ writeb(cbuf.l[1].osa[i], &ram[160 + i]);
+ writeb(cbuf.l[1].spid[i], &ram[192 + i]);
+ }
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: configured card OK\n");
+#endif
+
+ /* start adapter */
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: tell card to start...\n");
+#endif
+ writel(MP_PROTOCOL_ADDR, &boot->addr); /* RISC code entry point */
+ writel(3, &boot->cmd); /* DIVAS_START_CMD */
+
+ /* wait till card ACKs */
+ timeout = jiffies + (5*HZ);
+ while (timeout > jiffies) {
+ signature = readl(&boot->signature);
+ if ((signature >> 16) == DIVAS_SIGNATURE) break;
+ SLEEP(2);
+ }
+ if ((signature >> 16) != DIVAS_SIGNATURE)
+ {
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_ERR "eicon_pci: signature 0x%lx expected 0x%x\n",(signature >> 16),DIVAS_SIGNATURE);
+#endif
+ printk(KERN_ERR "eicon_pci: timeout, protocol code not running !\n");
+ return -EIO;
+ }
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: Protocol code running, signature OK\n");
+#endif
+
+ /* get serial number and number of channels supported by card */
+ card->channels = readb(&ram[0x3f6]);
+ card->serial = readl(&ram[0x3f0]);
+ printk(KERN_INFO "Eicon: Supported channels : %d\n", card->channels);
+ printk(KERN_INFO "Eicon: Card serial no. = %lu\n", card->serial);
+
+ /* test interrupt */
+ readb(&ram[0x3fe]);
+ writeb(0, &ram[0x3fe]); /* reset any pending interrupt */
+ readb(&ram[0x3fe]);
+
+ writew(MP_IRQ_RESET_VAL, &cfg[MP_IRQ_RESET]);
+ writew(0, &cfg[MP_IRQ_RESET + 2]);
+
+ card->irqprobe = 1;
+
+ if (!card->ivalid) {
+ if (request_irq(card->irq, &eicon_irq, 0, "Eicon PCI ISDN", card->card))
+ {
+ printk(KERN_ERR "eicon_pci: Couldn't request irq %d\n", card->irq);
+ return -EIO;
+ }
+ }
+ card->ivalid = 1;
+
+ req_int = readb(&prram->ReadyInt);
+#ifdef EICON_PCI_DEBUG
+ printk(KERN_DEBUG "eicon_pci: testing interrupt\n");
+#endif
+ req_int++;
+ /* Trigger an interrupt and check if it is delivered */
+ writeb(req_int, &prram->ReadyInt);
+
+ timeout = jiffies + 20;
+ while (timeout > jiffies) {
+ if (card->irqprobe != 1) break;
+ SLEEP(2);
+ }
+ if (card->irqprobe == 1) {
+ free_irq(card->irq, card);
+ card->ivalid = 0;
+ printk(KERN_ERR "eicon_pci: Getting no interrupts !\n");
+ return -EIO;
+ }
+
+ /* initializing some variables */
+ ((eicon_card *)card->card)->ReadyInt = 0;
+ for(j=0; j<256; j++) ((eicon_card *)card->card)->IdTable[j] = NULL;
+ for(j=0; j< (card->channels + 1); j++) {
+ ((eicon_card *)card->card)->bch[j].e.busy = 0;
+ ((eicon_card *)card->card)->bch[j].e.D3Id = 0;
+ ((eicon_card *)card->card)->bch[j].e.B2Id = 0;
+ ((eicon_card *)card->card)->bch[j].e.ref = 0;
+ ((eicon_card *)card->card)->bch[j].e.Req = 0;
+ ((eicon_card *)card->card)->bch[j].e.complete = 1;
+ ((eicon_card *)card->card)->bch[j].fsm_state = EICON_STATE_NULL;
+ }
+
+ printk(KERN_INFO "Eicon: Card successfully started\n");
+
+ return 0;
+}
+
+#endif /* CONFIG_PCI */
+
diff --git a/drivers/isdn/eicon/eicon_pci.h b/drivers/isdn/eicon/eicon_pci.h
new file mode 100644
index 000000000..a23faade2
--- /dev/null
+++ b/drivers/isdn/eicon/eicon_pci.h
@@ -0,0 +1,188 @@
+/* $Id: eicon_pci.h,v 1.3 1999/03/29 11:19:51 armin Exp $
+ *
+ * ISDN low-level module for Eicon.Diehl active ISDN-Cards (PCI part).
+ *
+ * Copyright 1998,99 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 Cytronics & Melware (info@melware.de)
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: eicon_pci.h,v $
+ * Revision 1.3 1999/03/29 11:19:51 armin
+ * I/O stuff now in seperate file (eicon_io.c)
+ * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented.
+ *
+ * Revision 1.2 1999/03/02 12:37:50 armin
+ * Added some important checks.
+ * Analog Modem with DSP.
+ * Channels will be added to Link-Level after loading firmware.
+ *
+ * Revision 1.1 1999/01/01 18:09:46 armin
+ * First checkin of new eicon driver.
+ * DIVA-Server BRI/PCI and PRI/PCI are supported.
+ * Old diehl code is obsolete.
+ *
+ *
+ */
+
+#ifndef eicon_pci_h
+#define eicon_pci_h
+
+#ifdef __KERNEL__
+
+
+#define PCI_VENDOR_EICON 0x1133
+#define PCI_DIVA_PRO20 0xe001 /* Not supported */
+#define PCI_DIVA20 0xe002 /* Not supported */
+#define PCI_DIVA_PRO20_U 0xe003 /* Not supported */
+#define PCI_DIVA20_U 0xe004 /* Not supported */
+#define PCI_MAESTRA 0xe010
+#define PCI_MAESTRAQ 0xe012
+#define PCI_MAESTRAQ_U 0xe013
+#define PCI_MAESTRAP 0xe014
+
+#define DIVA_PRO20 1
+#define DIVA20 2
+#define DIVA_PRO20_U 3
+#define DIVA20_U 4
+#define MAESTRA 5
+#define MAESTRAQ 6
+#define MAESTRAQ_U 7
+#define MAESTRAP 8
+
+#define TRUE 1
+#define FALSE 0
+
+#define DIVAS_SIGNATURE 0x4447
+
+
+/* MAESTRA BRI PCI */
+
+#define M_RESET 0x10 /* offset of reset register */
+#define M_DATA 0x00 /* offset of data register */
+#define M_ADDR 0x04 /* offset of address register */
+#define M_ADDRH 0x0c /* offset of high address register */
+
+#define M_DSP_CODE_LEN 0xbf7d0000
+#define M_DSP_CODE 0xbf7d0004 /* max 128K DSP-Code */
+#define M_DSP_CODE_BASE 0xbf7a0000
+#define M_MAX_DSP_CODE_SIZE 0x00050000 /* max 320K DSP-Code (Telindus) */
+
+
+
+/* MAESTRA PRI PCI */
+
+#define MP_SHARED_RAM_OFFSET 0x1000 /* offset of shared RAM base in the DRAM memory bar */
+
+#define MP_IRQ_RESET 0xc18 /* offset of interrupt status register in the CONFIG memory bar */
+#define MP_IRQ_RESET_VAL 0xfe /* value to clear an interrupt */
+
+#define MP_PROTOCOL_ADDR 0xa0011000 /* load address of protocol code */
+#define MP_DSP_ADDR 0xa03c0000 /* load address of DSP code */
+#define MP_MAX_PROTOCOL_CODE_SIZE 0x000a0000 /* max 640K Protocol-Code */
+#define MP_DSP_CODE_BASE 0xa03a0000
+#define MP_MAX_DSP_CODE_SIZE 0x00060000 /* max 384K DSP-Code */
+
+#define MP_RESET 0x20 /* offset of RESET register in the DEVICES memory bar */
+
+/* RESET register bits */
+#define _MP_S2M_RESET 0x10 /* active lo */
+#define _MP_LED2 0x08 /* 1 = on */
+#define _MP_LED1 0x04 /* 1 = on */
+#define _MP_DSP_RESET 0x02 /* active lo */
+#define _MP_RISC_RESET 0x81 /* active hi, bit 7 for compatibility with old boards */
+
+/* boot interface structure */
+typedef struct {
+ __u32 cmd __attribute__ ((packed));
+ __u32 addr __attribute__ ((packed));
+ __u32 len __attribute__ ((packed));
+ __u32 err __attribute__ ((packed));
+ __u32 live __attribute__ ((packed));
+ __u32 reserved[(0x1020>>2)-6] __attribute__ ((packed));
+ __u32 signature __attribute__ ((packed));
+ __u8 data[1]; /* real interface description */
+} eicon_pci_boot;
+
+
+#define DL_PARA_IO_TYPE 0
+#define DL_PARA_MEM_TYPE 1
+
+typedef struct tag_dsp_download_space
+{
+ __u16 type; /* see definitions above to differ union elements */
+ union
+ {
+ struct
+ {
+ __u32 r3addr;
+ __u16 ioADDR;
+ __u16 ioADDRH;
+ __u16 ioDATA;
+ __u16 BadData; /* in case of verify error */
+ __u16 GoodData;
+ } io; /* for io based adapters */
+ struct
+ {
+ __u32 r3addr;
+ eicon_pci_boot *boot;
+ __u32 BadData; /* in case of verify error */
+ __u32 GoodData;
+ __u16 timeout;
+ } mem; /* for memory based adapters */
+ } dat;
+} t_dsp_download_space;
+
+
+/* Shared memory */
+typedef union {
+ eicon_pci_boot boot;
+} eicon_pci_shmem;
+
+/*
+ * card's description
+ */
+typedef struct {
+ int ramsize;
+ int irq; /* IRQ */
+ unsigned int PCIram;
+ unsigned int PCIreg;
+ unsigned int PCIcfg;
+ long int serial; /* Serial No. */
+ int channels; /* No. of supported channels */
+ void* card;
+ eicon_pci_shmem* shmem; /* Shared-memory area */
+ unsigned char* intack; /* Int-Acknowledge */
+ unsigned char* stopcpu; /* Writing here stops CPU */
+ unsigned char* startcpu; /* Writing here starts CPU */
+ unsigned char type; /* card type */
+ unsigned char irqprobe; /* Flag: IRQ-probing */
+ unsigned char mvalid; /* Flag: Memory is valid */
+ unsigned char ivalid; /* Flag: IRQ is valid */
+ unsigned char master; /* Flag: Card is Quadro 1/4 */
+ void* generic; /* Ptr to generic card struct */
+} eicon_pci_card;
+
+
+
+extern int eicon_pci_load_pri(eicon_pci_card *card, eicon_pci_codebuf *cb);
+extern int eicon_pci_load_bri(eicon_pci_card *card, eicon_pci_codebuf *cb);
+extern void eicon_pci_release(eicon_pci_card *card);
+extern void eicon_pci_printpar(eicon_pci_card *card);
+extern int eicon_pci_find_card(char *ID);
+
+#endif /* __KERNEL__ */
+
+#endif /* eicon_pci_h */
diff --git a/drivers/isdn/hisax/avm_a1p.c b/drivers/isdn/hisax/avm_a1p.c
new file mode 100644
index 000000000..c11ac41e4
--- /dev/null
+++ b/drivers/isdn/hisax/avm_a1p.c
@@ -0,0 +1,334 @@
+/* $Id: avm_a1p.c,v 2.3 1998/11/15 23:54:22 keil Exp $
+ *
+ * avm_a1p.c low level stuff for the following AVM cards:
+ * A1 PCMCIA
+ * FRITZ!Card PCMCIA
+ * FRITZ!Card PCMCIA 2.0
+ *
+ * Author Carsten Paeth (calle@calle.in-berlin.de)
+ *
+ * $Log: avm_a1p.c,v $
+ * Revision 2.3 1998/11/15 23:54:22 keil
+ * changes from 2.0
+ *
+ * Revision 2.2 1998/08/13 23:36:13 keil
+ * HiSax 3.1 - don't work stable with current LinkLevel
+ *
+ * Revision 2.1 1998/07/15 15:01:23 calle
+ * Support for AVM passive PCMCIA cards:
+ * A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
+ *
+ * Revision 1.1.2.1 1998/07/15 14:43:26 calle
+ * Support for AVM passive PCMCIA cards:
+ * A1 PCMCIA, FRITZ!Card PCMCIA and FRITZ!Card PCMCIA 2.0
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+/* register offsets */
+#define ADDRREG_OFFSET 0x02
+#define DATAREG_OFFSET 0x03
+#define ASL0_OFFSET 0x04
+#define ASL1_OFFSET 0x05
+#define MODREG_OFFSET 0x06
+#define VERREG_OFFSET 0x07
+
+/* address offsets */
+#define ISAC_FIFO_OFFSET 0x00
+#define ISAC_REG_OFFSET 0x20
+#define HSCX_CH_DIFF 0x40
+#define HSCX_FIFO_OFFSET 0x80
+#define HSCX_REG_OFFSET 0xa0
+
+/* read bits ASL0 */
+#define ASL0_R_TIMER 0x10 /* active low */
+#define ASL0_R_ISAC 0x20 /* active low */
+#define ASL0_R_HSCX 0x40 /* active low */
+#define ASL0_R_TESTBIT 0x80
+#define ASL0_R_IRQPENDING (ASL0_R_ISAC|ASL0_R_HSCX|ASL0_R_TIMER)
+
+/* write bits ASL0 */
+#define ASL0_W_RESET 0x01
+#define ASL0_W_TDISABLE 0x02
+#define ASL0_W_TRESET 0x04
+#define ASL0_W_IRQENABLE 0x08
+#define ASL0_W_TESTBIT 0x80
+
+/* write bits ASL1 */
+#define ASL1_W_LED0 0x10
+#define ASL1_W_LED1 0x20
+#define ASL1_W_ENABLE_S0 0xC0
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static const char *avm_revision = "$Revision: 2.3 $";
+
+static inline u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ long flags;
+ u_char ret;
+
+ offset -= 0x20;
+ save_flags(flags);
+ cli();
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_REG_OFFSET+offset);
+ ret = bytein(cs->hw.avm.cfg_reg+DATAREG_OFFSET);
+ restore_flags(flags);
+ return ret;
+}
+
+static inline void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ long flags;
+
+ offset -= 0x20;
+
+ save_flags(flags);
+ cli();
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_REG_OFFSET+offset);
+ byteout(cs->hw.avm.cfg_reg+DATAREG_OFFSET, value);
+ restore_flags(flags);
+}
+
+static inline void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ long flags;
+
+ save_flags(flags);
+ cli();
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_FIFO_OFFSET);
+ insb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+ restore_flags(flags);
+}
+
+static inline void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ long flags;
+
+ save_flags(flags);
+ cli();
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_FIFO_OFFSET);
+ outsb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+ restore_flags(flags);
+}
+
+static inline u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ u_char ret;
+ long flags;
+
+ offset -= 0x20;
+
+ save_flags(flags);
+ cli();
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+ HSCX_REG_OFFSET+hscx*HSCX_CH_DIFF+offset);
+ ret = bytein(cs->hw.avm.cfg_reg+DATAREG_OFFSET);
+ restore_flags(flags);
+ return ret;
+}
+
+static inline void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ long flags;
+
+ offset -= 0x20;
+
+ save_flags(flags);
+ cli();
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+ HSCX_REG_OFFSET+hscx*HSCX_CH_DIFF+offset);
+ byteout(cs->hw.avm.cfg_reg+DATAREG_OFFSET, value);
+ restore_flags(flags);
+}
+
+static inline void
+ReadHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+ long flags;
+
+ save_flags(flags);
+ cli();
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+ HSCX_FIFO_OFFSET+hscx*HSCX_CH_DIFF);
+ insb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+ restore_flags(flags);
+}
+
+static inline void
+WriteHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+ long flags;
+
+ save_flags(flags);
+ cli();
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+ HSCX_FIFO_OFFSET+hscx*HSCX_CH_DIFF);
+ outsb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+ restore_flags(flags);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) ReadHSCX(cs, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) WriteHSCX(cs, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) ReadHSCXfifo(cs, nr, ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) WriteHSCXfifo(cs, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+avm_a1p_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val, sval, stat = 0;
+
+ if (!cs) {
+ printk(KERN_WARNING "AVM A1 PCMCIA: Spurious interrupt!\n");
+ return;
+ }
+ while ((sval = (~bytein(cs->hw.avm.cfg_reg+ASL0_OFFSET) & ASL0_R_IRQPENDING))) {
+ if (cs->debug & L1_DEB_INTSTAT)
+ debugl1(cs, "avm IntStatus %x", sval);
+ if (sval & ASL0_R_HSCX) {
+ val = ReadHSCX(cs, 1, HSCX_ISTA);
+ if (val) {
+ hscx_int_main(cs, val);
+ stat |= 1;
+ }
+ }
+ if (sval & ASL0_R_ISAC) {
+ val = ReadISAC(cs, ISAC_ISTA);
+ if (val) {
+ isac_interrupt(cs, val);
+ stat |= 2;
+ }
+ }
+ }
+ if (stat & 1) {
+ WriteHSCX(cs, 0, HSCX_MASK, 0xff);
+ WriteHSCX(cs, 1, HSCX_MASK, 0xff);
+ WriteHSCX(cs, 0, HSCX_MASK, 0x00);
+ WriteHSCX(cs, 1, HSCX_MASK, 0x00);
+ }
+ if (stat & 2) {
+ WriteISAC(cs, ISAC_MASK, 0xff);
+ WriteISAC(cs, ISAC_MASK, 0x00);
+ }
+}
+
+static int
+AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ int ret;
+ switch (mt) {
+ case CARD_RESET:
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+ HZDELAY(HZ / 5 + 1);
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_RESET);
+ HZDELAY(HZ / 5 + 1);
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+ return 0;
+
+ case CARD_RELEASE:
+ /* free_irq is done in HiSax_closecard(). */
+ /* free_irq(cs->irq, cs); */
+ return 0;
+
+ case CARD_SETIRQ:
+ ret = request_irq(cs->irq, &avm_a1p_interrupt,
+ I4L_IRQ_FLAG, "HiSax", cs);
+ if (ret)
+ return ret;
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,
+ ASL0_W_TDISABLE|ASL0_W_TRESET|ASL0_W_IRQENABLE);
+ return 0;
+
+ case CARD_INIT:
+ clear_pending_isac_ints(cs);
+ clear_pending_hscx_ints(cs);
+ inithscxisac(cs, 1);
+ inithscxisac(cs, 2);
+ return 0;
+
+ case CARD_TEST:
+ /* we really don't need it for the PCMCIA Version */
+ return 0;
+
+ default:
+ /* all card drivers ignore others, so we do the same */
+ return 0;
+ }
+ return 0;
+}
+
+__initfunc(int
+setup_avm_a1_pcmcia(struct IsdnCard *card))
+{
+ u_char model, vers;
+ struct IsdnCardState *cs = card->cs;
+ long flags;
+ char tmp[64];
+
+
+ strcpy(tmp, avm_revision);
+ printk(KERN_INFO "HiSax: AVM A1 PCMCIA driver Rev. %s\n",
+ HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_A1_PCMCIA)
+ return (0);
+
+ cs->hw.avm.cfg_reg = card->para[1];
+ cs->irq = card->para[0];
+
+
+ save_flags(flags);
+ outb(cs->hw.avm.cfg_reg+ASL1_OFFSET, ASL1_W_ENABLE_S0);
+ sti();
+
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+ HZDELAY(HZ / 5 + 1);
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_RESET);
+ HZDELAY(HZ / 5 + 1);
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET, ASL0_W_TDISABLE|ASL0_W_TRESET);
+
+ restore_flags(flags);
+
+ model = bytein(cs->hw.avm.cfg_reg+MODREG_OFFSET);
+ vers = bytein(cs->hw.avm.cfg_reg+VERREG_OFFSET);
+
+ printk(KERN_INFO "AVM A1 PCMCIA: io 0x%x irq %d model %d version %d\n",
+ cs->hw.avm.cfg_reg, cs->irq, model, vers);
+
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &AVM_card_msg;
+
+ ISACVersion(cs, "AVM A1 PCMCIA:");
+ if (HscxVersion(cs, "AVM A1 PCMCIA:")) {
+ printk(KERN_WARNING
+ "AVM A1 PCMCIA: wrong HSCX versions check IO address\n");
+ return (0);
+ }
+ return (1);
+}
diff --git a/drivers/isdn/hisax/avm_pci.c b/drivers/isdn/hisax/avm_pci.c
new file mode 100644
index 000000000..c0f04f91c
--- /dev/null
+++ b/drivers/isdn/hisax/avm_pci.c
@@ -0,0 +1,865 @@
+/* $Id: avm_pci.c,v 1.7 1999/02/22 18:26:30 keil Exp $
+
+ * avm_pci.c low level stuff for AVM Fritz!PCI and ISA PnP isdn cards
+ * Thanks to AVM, Berlin for informations
+ *
+ * Author Karsten Keil (keil@isdn4linux.de)
+ *
+ *
+ * $Log: avm_pci.c,v $
+ * Revision 1.7 1999/02/22 18:26:30 keil
+ * Argh ! ISAC address was only set with PCI
+ *
+ * Revision 1.6 1998/11/27 19:59:28 keil
+ * set subtype for Fritz!PCI
+ *
+ * Revision 1.5 1998/11/27 12:56:45 keil
+ * forgot to update setup function name
+ *
+ * Revision 1.4 1998/11/15 23:53:19 keil
+ * Fritz!PnP; changes from 2.0
+ *
+ * Revision 1.3 1998/09/27 23:53:39 keil
+ * Fix error handling
+ *
+ * Revision 1.2 1998/09/27 12:54:55 keil
+ * bcs assign was lost in setstack, very bad results
+ *
+ * Revision 1.1 1998/08/20 13:47:30 keil
+ * first version
+ *
+ *
+ *
+ */
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "hisax.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+extern const char *CardType[];
+static const char *avm_pci_rev = "$Revision: 1.7 $";
+
+#define AVM_FRITZ_PCI 1
+#define AVM_FRITZ_PNP 2
+
+#define PCI_VENDOR_AVM 0x1244
+#define PCI_FRITZPCI_ID 0xa00
+
+#define HDLC_FIFO 0x0
+#define HDLC_STATUS 0x4
+
+#define AVM_HDLC_1 0x00
+#define AVM_HDLC_2 0x01
+#define AVM_ISAC_FIFO 0x02
+#define AVM_ISAC_REG_LOW 0x04
+#define AVM_ISAC_REG_HIGH 0x06
+
+#define AVM_STATUS0_IRQ_ISAC 0x01
+#define AVM_STATUS0_IRQ_HDLC 0x02
+#define AVM_STATUS0_IRQ_TIMER 0x04
+#define AVM_STATUS0_IRQ_MASK 0x07
+
+#define AVM_STATUS0_RESET 0x01
+#define AVM_STATUS0_DIS_TIMER 0x02
+#define AVM_STATUS0_RES_TIMER 0x04
+#define AVM_STATUS0_ENA_IRQ 0x08
+#define AVM_STATUS0_TESTBIT 0x10
+
+#define AVM_STATUS1_INT_SEL 0x0f
+#define AVM_STATUS1_ENA_IOM 0x80
+
+#define HDLC_MODE_ITF_FLG 0x01
+#define HDLC_MODE_TRANS 0x02
+#define HDLC_MODE_CCR_7 0x04
+#define HDLC_MODE_CCR_16 0x08
+#define HDLC_MODE_TESTLOOP 0x80
+
+#define HDLC_INT_XPR 0x80
+#define HDLC_INT_XDU 0x40
+#define HDLC_INT_RPR 0x20
+#define HDLC_INT_MASK 0xE0
+
+#define HDLC_STAT_RME 0x01
+#define HDLC_STAT_RDO 0x10
+#define HDLC_STAT_CRCVFRRAB 0x0E
+#define HDLC_STAT_CRCVFR 0x06
+#define HDLC_STAT_RML_MASK 0x3f00
+
+#define HDLC_CMD_XRS 0x80
+#define HDLC_CMD_XME 0x01
+#define HDLC_CMD_RRS 0x20
+#define HDLC_CMD_XML_MASK 0x3f00
+
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+ register u_char val;
+ register long flags;
+
+ save_flags(flags);
+ cli();
+ outb(idx, cs->hw.avm.cfg_reg + 4);
+ val = inb(cs->hw.avm.isac + (offset & 0xf));
+ restore_flags(flags);
+ return (val);
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+ register long flags;
+
+ save_flags(flags);
+ cli();
+ outb(idx, cs->hw.avm.cfg_reg + 4);
+ outb(value, cs->hw.avm.isac + (offset & 0xf));
+ restore_flags(flags);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4);
+ insb(cs->hw.avm.isac, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4);
+ outsb(cs->hw.avm.isac, data, size);
+}
+
+static inline u_int
+ReadHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset)
+{
+ register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+ register u_int val;
+ register long flags;
+
+ save_flags(flags);
+ cli();
+ outl(idx, cs->hw.avm.cfg_reg + 4);
+ val = inl(cs->hw.avm.isac + offset);
+ restore_flags(flags);
+ return (val);
+}
+
+static inline void
+WriteHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset, u_int value)
+{
+ register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+ register long flags;
+
+ save_flags(flags);
+ cli();
+ outl(idx, cs->hw.avm.cfg_reg + 4);
+ outl(value, cs->hw.avm.isac + offset);
+ restore_flags(flags);
+}
+
+static inline u_char
+ReadHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset)
+{
+ register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+ register u_char val;
+ register long flags;
+
+ save_flags(flags);
+ cli();
+ outb(idx, cs->hw.avm.cfg_reg + 4);
+ val = inb(cs->hw.avm.isac + offset);
+ restore_flags(flags);
+ return (val);
+}
+
+static inline void
+WriteHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset, u_char value)
+{
+ register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+ register long flags;
+
+ save_flags(flags);
+ cli();
+ outb(idx, cs->hw.avm.cfg_reg + 4);
+ outb(value, cs->hw.avm.isac + offset);
+ restore_flags(flags);
+}
+
+static u_char
+ReadHDLC_s(struct IsdnCardState *cs, int chan, u_char offset)
+{
+ return(0xff & ReadHDLCPCI(cs, chan, offset));
+}
+
+static void
+WriteHDLC_s(struct IsdnCardState *cs, int chan, u_char offset, u_char value)
+{
+ WriteHDLCPCI(cs, chan, offset, value);
+}
+
+static inline
+struct BCState *Sel_BCS(struct IsdnCardState *cs, int channel)
+{
+ if (cs->bcs[0].mode && (cs->bcs[0].channel == channel))
+ return(&cs->bcs[0]);
+ else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel))
+ return(&cs->bcs[1]);
+ else
+ return(NULL);
+}
+
+void inline
+hdlc_sched_event(struct BCState *bcs, int event)
+{
+ bcs->event |= 1 << event;
+ queue_task(&bcs->tqueue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+void
+write_ctrl(struct BCState *bcs, int which) {
+
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs, "hdlc %c wr%x ctrl %x",
+ 'A' + bcs->channel, which, bcs->hw.hdlc.ctrl.ctrl);
+ if (bcs->cs->subtyp == AVM_FRITZ_PCI) {
+ WriteHDLCPCI(bcs->cs, bcs->channel, HDLC_STATUS, bcs->hw.hdlc.ctrl.ctrl);
+ } else {
+ if (which & 4)
+ WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 2,
+ bcs->hw.hdlc.ctrl.sr.mode);
+ if (which & 2)
+ WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 1,
+ bcs->hw.hdlc.ctrl.sr.xml);
+ if (which & 1)
+ WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS,
+ bcs->hw.hdlc.ctrl.sr.cmd);
+ }
+}
+
+void
+modehdlc(struct BCState *bcs, int mode, int bc)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int hdlc = bcs->channel;
+
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hdlc %c mode %d ichan %d",
+ 'A' + hdlc, mode, bc);
+ bcs->mode = mode;
+ bcs->channel = bc;
+ bcs->hw.hdlc.ctrl.ctrl = 0;
+ switch (mode) {
+ case (L1_MODE_NULL):
+ bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS;
+ bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS;
+ write_ctrl(bcs, 5);
+ break;
+ case (L1_MODE_TRANS):
+ bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS;
+ bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS;
+ write_ctrl(bcs, 5);
+ bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS;
+ write_ctrl(bcs, 1);
+ bcs->hw.hdlc.ctrl.sr.cmd = 0;
+ hdlc_sched_event(bcs, B_XMTBUFREADY);
+ break;
+ case (L1_MODE_HDLC):
+ bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS;
+ bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_ITF_FLG;
+ write_ctrl(bcs, 5);
+ bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS;
+ write_ctrl(bcs, 1);
+ bcs->hw.hdlc.ctrl.sr.cmd = 0;
+ hdlc_sched_event(bcs, B_XMTBUFREADY);
+ break;
+ }
+}
+
+static inline void
+hdlc_empty_fifo(struct BCState *bcs, int count)
+{
+ register u_int *ptr;
+ u_char *p;
+ u_char idx = bcs->channel ? AVM_HDLC_2 : AVM_HDLC_1;
+ int cnt=0;
+ struct IsdnCardState *cs = bcs->cs;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "hdlc_empty_fifo %d", count);
+ if (bcs->hw.hdlc.rcvidx + count > HSCX_BUFMAX) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "hdlc_empty_fifo: incoming packet too large");
+ return;
+ }
+ ptr = (u_int *) p = bcs->hw.hdlc.rcvbuf + bcs->hw.hdlc.rcvidx;
+ bcs->hw.hdlc.rcvidx += count;
+ if (cs->subtyp == AVM_FRITZ_PCI) {
+ outl(idx, cs->hw.avm.cfg_reg + 4);
+ while (cnt < count) {
+ *ptr++ = inl(cs->hw.avm.isac);
+ cnt += 4;
+ }
+ } else {
+ outb(idx, cs->hw.avm.cfg_reg + 4);
+ while (cnt < count) {
+ *p++ = inb(cs->hw.avm.isac);
+ cnt++;
+ }
+ }
+ if (cs->debug & L1_DEB_HSCX_FIFO) {
+ char *t = bcs->blog;
+
+ if (cs->subtyp == AVM_FRITZ_PNP)
+ p = (u_char *) ptr;
+ t += sprintf(t, "hdlc_empty_fifo %c cnt %d",
+ bcs->channel ? 'B' : 'A', count);
+ QuickHex(t, p, count);
+ debugl1(cs, bcs->blog);
+ }
+}
+
+static inline void
+hdlc_fill_fifo(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int count, cnt =0;
+ int fifo_size = 32;
+ u_char *p;
+ u_int *ptr;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "hdlc_fill_fifo");
+ if (!bcs->tx_skb)
+ return;
+ if (bcs->tx_skb->len <= 0)
+ return;
+
+ bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_XME;
+ if (bcs->tx_skb->len > fifo_size) {
+ count = fifo_size;
+ } else {
+ count = bcs->tx_skb->len;
+ if (bcs->mode != L1_MODE_TRANS)
+ bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_XME;
+ }
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "hdlc_fill_fifo %d/%ld", count, bcs->tx_skb->len);
+ ptr = (u_int *) p = bcs->tx_skb->data;
+ skb_pull(bcs->tx_skb, count);
+ bcs->tx_cnt -= count;
+ bcs->hw.hdlc.count += count;
+ bcs->hw.hdlc.ctrl.sr.xml = ((count == fifo_size) ? 0 : count);
+ write_ctrl(bcs, 3); /* sets the correct index too */
+ if (cs->subtyp == AVM_FRITZ_PCI) {
+ while (cnt<count) {
+ outl(*ptr++, cs->hw.avm.isac);
+ cnt += 4;
+ }
+ } else {
+ while (cnt<count) {
+ outb(*p++, cs->hw.avm.isac);
+ cnt++;
+ }
+ }
+ if (cs->debug & L1_DEB_HSCX_FIFO) {
+ char *t = bcs->blog;
+
+ if (cs->subtyp == AVM_FRITZ_PNP)
+ p = (u_char *) ptr;
+ t += sprintf(t, "hdlc_fill_fifo %c cnt %d",
+ bcs->channel ? 'B' : 'A', count);
+ QuickHex(t, p, count);
+ debugl1(cs, bcs->blog);
+ }
+}
+
+static void
+fill_hdlc(struct BCState *bcs)
+{
+ long flags;
+ save_flags(flags);
+ cli();
+ hdlc_fill_fifo(bcs);
+ restore_flags(flags);
+}
+
+static inline void
+HDLC_irq(struct BCState *bcs, u_int stat) {
+ int len;
+ struct sk_buff *skb;
+
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat);
+ if (stat & HDLC_INT_RPR) {
+ if (stat & HDLC_STAT_RDO) {
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs, "RDO");
+ else
+ debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat);
+ bcs->hw.hdlc.ctrl.sr.xml = 0;
+ bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_RRS;
+ write_ctrl(bcs, 1);
+ bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_RRS;
+ write_ctrl(bcs, 1);
+ bcs->hw.hdlc.rcvidx = 0;
+ } else {
+ if (!(len = (stat & HDLC_STAT_RML_MASK)>>8))
+ len = 32;
+ hdlc_empty_fifo(bcs, len);
+ if ((stat & HDLC_STAT_RME) || (bcs->mode == L1_MODE_TRANS)) {
+ if (((stat & HDLC_STAT_CRCVFRRAB)==HDLC_STAT_CRCVFR) ||
+ (bcs->mode == L1_MODE_TRANS)) {
+ if (!(skb = dev_alloc_skb(bcs->hw.hdlc.rcvidx)))
+ printk(KERN_WARNING "HDLC: receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, bcs->hw.hdlc.rcvidx),
+ bcs->hw.hdlc.rcvbuf, bcs->hw.hdlc.rcvidx);
+ skb_queue_tail(&bcs->rqueue, skb);
+ }
+ bcs->hw.hdlc.rcvidx = 0;
+ hdlc_sched_event(bcs, B_RCVBUFREADY);
+ } else {
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs, "invalid frame");
+ else
+ debugl1(bcs->cs, "ch%d invalid frame %#x", bcs->channel, stat);
+ bcs->hw.hdlc.rcvidx = 0;
+ }
+ }
+ }
+ }
+ if (stat & HDLC_INT_XDU) {
+ /* Here we lost an TX interrupt, so
+ * restart transmitting the whole frame.
+ */
+ if (bcs->tx_skb) {
+ skb_push(bcs->tx_skb, bcs->hw.hdlc.count);
+ bcs->tx_cnt += bcs->hw.hdlc.count;
+ bcs->hw.hdlc.count = 0;
+// hdlc_sched_event(bcs, B_XMTBUFREADY);
+ if (bcs->cs->debug & L1_DEB_WARN)
+ debugl1(bcs->cs, "ch%d XDU", bcs->channel);
+ } else if (bcs->cs->debug & L1_DEB_WARN)
+ debugl1(bcs->cs, "ch%d XDU without skb", bcs->channel);
+ bcs->hw.hdlc.ctrl.sr.xml = 0;
+ bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_XRS;
+ write_ctrl(bcs, 1);
+ bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_XRS;
+ write_ctrl(bcs, 1);
+ hdlc_fill_fifo(bcs);
+ } else if (stat & HDLC_INT_XPR) {
+ if (bcs->tx_skb) {
+ if (bcs->tx_skb->len) {
+ hdlc_fill_fifo(bcs);
+ return;
+ } else {
+ if (bcs->st->lli.l1writewakeup &&
+ (PACKET_NOACK != bcs->tx_skb->pkt_type))
+ bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hdlc.count);
+ dev_kfree_skb(bcs->tx_skb);
+ bcs->hw.hdlc.count = 0;
+ bcs->tx_skb = NULL;
+ }
+ }
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ bcs->hw.hdlc.count = 0;
+ test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ hdlc_fill_fifo(bcs);
+ } else {
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ hdlc_sched_event(bcs, B_XMTBUFREADY);
+ }
+ }
+}
+
+inline void
+HDLC_irq_main(struct IsdnCardState *cs)
+{
+ u_int stat;
+ long flags;
+ struct BCState *bcs;
+
+ save_flags(flags);
+ cli();
+ if (cs->subtyp == AVM_FRITZ_PCI) {
+ stat = ReadHDLCPCI(cs, 0, HDLC_STATUS);
+ } else {
+ stat = ReadHDLCPnP(cs, 0, HDLC_STATUS);
+ if (stat & HDLC_INT_RPR)
+ stat |= (ReadHDLCPnP(cs, 0, HDLC_STATUS+1))<<8;
+ }
+ if (stat & HDLC_INT_MASK) {
+ if (!(bcs = Sel_BCS(cs, 0))) {
+ if (cs->debug)
+ debugl1(cs, "hdlc spurious channel 0 IRQ");
+ } else
+ HDLC_irq(bcs, stat);
+ }
+ if (cs->subtyp == AVM_FRITZ_PCI) {
+ stat = ReadHDLCPCI(cs, 1, HDLC_STATUS);
+ } else {
+ stat = ReadHDLCPnP(cs, 1, HDLC_STATUS);
+ if (stat & HDLC_INT_RPR)
+ stat |= (ReadHDLCPnP(cs, 1, HDLC_STATUS+1))<<8;
+ }
+ if (stat & HDLC_INT_MASK) {
+ if (!(bcs = Sel_BCS(cs, 1))) {
+ if (cs->debug)
+ debugl1(cs, "hdlc spurious channel 1 IRQ");
+ } else
+ HDLC_irq(bcs, stat);
+ }
+ restore_flags(flags);
+}
+
+void
+hdlc_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ long flags;
+
+ switch (pr) {
+ case (PH_DATA | REQUEST):
+ save_flags(flags);
+ cli();
+ if (st->l1.bcs->tx_skb) {
+ skb_queue_tail(&st->l1.bcs->squeue, skb);
+ restore_flags(flags);
+ } else {
+ st->l1.bcs->tx_skb = skb;
+ test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ st->l1.bcs->hw.hdlc.count = 0;
+ restore_flags(flags);
+ st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+ }
+ break;
+ case (PH_PULL | INDICATION):
+ if (st->l1.bcs->tx_skb) {
+ printk(KERN_WARNING "hdlc_l2l1: this shouldn't happen\n");
+ break;
+ }
+ test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ st->l1.bcs->tx_skb = skb;
+ st->l1.bcs->hw.hdlc.count = 0;
+ st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+ break;
+ case (PH_PULL | REQUEST):
+ if (!st->l1.bcs->tx_skb) {
+ test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+ st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ } else
+ test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+ break;
+ case (PH_ACTIVATE | REQUEST):
+ test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ modehdlc(st->l1.bcs, st->l1.mode, st->l1.bc);
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | REQUEST):
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | CONFIRM):
+ test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ modehdlc(st->l1.bcs, 0, st->l1.bc);
+ st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ break;
+ }
+}
+
+void
+close_hdlcstate(struct BCState *bcs)
+{
+ modehdlc(bcs, 0, 0);
+ if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+ if (bcs->hw.hdlc.rcvbuf) {
+ kfree(bcs->hw.hdlc.rcvbuf);
+ bcs->hw.hdlc.rcvbuf = NULL;
+ }
+ if (bcs->blog) {
+ kfree(bcs->blog);
+ bcs->blog = NULL;
+ }
+ discard_queue(&bcs->rqueue);
+ discard_queue(&bcs->squeue);
+ if (bcs->tx_skb) {
+ dev_kfree_skb(bcs->tx_skb);
+ bcs->tx_skb = NULL;
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ }
+ }
+}
+
+int
+open_hdlcstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+ if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+ if (!(bcs->hw.hdlc.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for hdlc.rcvbuf\n");
+ return (1);
+ }
+ if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for bcs->blog\n");
+ test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+ kfree(bcs->hw.hdlc.rcvbuf);
+ bcs->hw.hdlc.rcvbuf = NULL;
+ return (2);
+ }
+ skb_queue_head_init(&bcs->rqueue);
+ skb_queue_head_init(&bcs->squeue);
+ }
+ bcs->tx_skb = NULL;
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ bcs->event = 0;
+ bcs->hw.hdlc.rcvidx = 0;
+ bcs->tx_cnt = 0;
+ return (0);
+}
+
+int
+setstack_hdlc(struct PStack *st, struct BCState *bcs)
+{
+ bcs->channel = st->l1.bc;
+ if (open_hdlcstate(st->l1.hardware, bcs))
+ return (-1);
+ st->l1.bcs = bcs;
+ st->l2.l2l1 = hdlc_l2l1;
+ setstack_manager(st);
+ bcs->st = st;
+ setstack_l1_B(st);
+ return (0);
+}
+
+HISAX_INITFUNC(void
+clear_pending_hdlc_ints(struct IsdnCardState *cs))
+{
+ u_int val;
+
+ if (cs->subtyp == AVM_FRITZ_PCI) {
+ val = ReadHDLCPCI(cs, 0, HDLC_STATUS);
+ debugl1(cs, "HDLC 1 STA %x", val);
+ val = ReadHDLCPCI(cs, 1, HDLC_STATUS);
+ debugl1(cs, "HDLC 2 STA %x", val);
+ } else {
+ val = ReadHDLCPnP(cs, 0, HDLC_STATUS);
+ debugl1(cs, "HDLC 1 STA %x", val);
+ val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 1);
+ debugl1(cs, "HDLC 1 RML %x", val);
+ val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 2);
+ debugl1(cs, "HDLC 1 MODE %x", val);
+ val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 3);
+ debugl1(cs, "HDLC 1 VIN %x", val);
+ val = ReadHDLCPnP(cs, 1, HDLC_STATUS);
+ debugl1(cs, "HDLC 2 STA %x", val);
+ val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 1);
+ debugl1(cs, "HDLC 2 RML %x", val);
+ val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 2);
+ debugl1(cs, "HDLC 2 MODE %x", val);
+ val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 3);
+ debugl1(cs, "HDLC 2 VIN %x", val);
+ }
+}
+
+HISAX_INITFUNC(void
+inithdlc(struct IsdnCardState *cs))
+{
+ cs->bcs[0].BC_SetStack = setstack_hdlc;
+ cs->bcs[1].BC_SetStack = setstack_hdlc;
+ cs->bcs[0].BC_Close = close_hdlcstate;
+ cs->bcs[1].BC_Close = close_hdlcstate;
+ modehdlc(cs->bcs, 0, 0);
+ modehdlc(cs->bcs + 1, 0, 0);
+}
+
+static void
+avm_pcipnp_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val, stat = 0;
+ u_char sval;
+
+ if (!cs) {
+ printk(KERN_WARNING "AVM PCI: Spurious interrupt!\n");
+ return;
+ }
+ sval = inb(cs->hw.avm.cfg_reg + 2);
+ if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK)
+ /* possible a shared IRQ reqest */
+ return;
+ if (!(sval & AVM_STATUS0_IRQ_ISAC)) {
+ val = ReadISAC(cs, ISAC_ISTA);
+ isac_interrupt(cs, val);
+ stat |= 2;
+ }
+ if (!(sval & AVM_STATUS0_IRQ_HDLC)) {
+ HDLC_irq_main(cs);
+ }
+ if (stat & 2) {
+ WriteISAC(cs, ISAC_MASK, 0xFF);
+ WriteISAC(cs, ISAC_MASK, 0x0);
+ }
+}
+
+static void
+reset_avmpcipnp(struct IsdnCardState *cs)
+{
+ long flags;
+
+ printk(KERN_INFO "AVM PCI/PnP: reset\n");
+ save_flags(flags);
+ sti();
+ outb(AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER, cs->hw.avm.cfg_reg + 2);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
+ outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2);
+ outb(AVM_STATUS1_ENA_IOM | cs->irq, cs->hw.avm.cfg_reg + 3);
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout((10*HZ)/1000); /* Timeout 10ms */
+ printk(KERN_INFO "AVM PCI/PnP: S1 %x\n", inb(cs->hw.avm.cfg_reg + 3));
+}
+
+static int
+AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_int irq_flag;
+
+ switch (mt) {
+ case CARD_RESET:
+ reset_avmpcipnp(cs);
+ return(0);
+ case CARD_RELEASE:
+ outb(0, cs->hw.avm.cfg_reg + 2);
+ release_region(cs->hw.avm.cfg_reg, 32);
+ return(0);
+ case CARD_SETIRQ:
+ if (cs->subtyp == AVM_FRITZ_PCI)
+ irq_flag = I4L_IRQ_FLAG | SA_SHIRQ;
+ else
+ irq_flag = I4L_IRQ_FLAG;
+ return(request_irq(cs->irq, &avm_pcipnp_interrupt,
+ irq_flag, "HiSax", cs));
+ case CARD_INIT:
+ clear_pending_isac_ints(cs);
+ initisac(cs);
+ clear_pending_hdlc_ints(cs);
+ inithdlc(cs);
+ outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER,
+ cs->hw.avm.cfg_reg + 2);
+ WriteISAC(cs, ISAC_MASK, 0);
+ outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER |
+ AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2);
+ /* RESET Receiver and Transmitter */
+ WriteISAC(cs, ISAC_CMDR, 0x41);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ }
+ return(0);
+}
+
+static struct pci_dev *dev_avm __initdata = NULL;
+
+__initfunc(int
+setup_avm_pcipnp(struct IsdnCard *card))
+{
+ u_int val, ver;
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, avm_pci_rev);
+ printk(KERN_INFO "HiSax: AVM PCI driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_FRITZPCI)
+ return (0);
+ if (card->para[1]) {
+ cs->hw.avm.cfg_reg = card->para[1];
+ cs->irq = card->para[0];
+ cs->subtyp = AVM_FRITZ_PNP;
+ } else {
+#if CONFIG_PCI
+ if (!pci_present()) {
+ printk(KERN_ERR "FritzPCI: no PCI bus present\n");
+ return(0);
+ }
+ if ((dev_avm = pci_find_device(PCI_VENDOR_AVM,
+ PCI_FRITZPCI_ID, dev_avm))) {
+ cs->irq = dev_avm->irq;
+ if (!cs->irq) {
+ printk(KERN_WARNING "FritzPCI: No IRQ for PCI card found\n");
+ return(0);
+ }
+ cs->hw.avm.cfg_reg = dev_avm->base_address[1] &
+ PCI_BASE_ADDRESS_IO_MASK;
+ if (!cs->hw.avm.cfg_reg) {
+ printk(KERN_WARNING "FritzPCI: No IO-Adr for PCI card found\n");
+ return(0);
+ }
+ cs->subtyp = AVM_FRITZ_PCI;
+ } else {
+ printk(KERN_WARNING "FritzPCI: No PCI card found\n");
+ return(0);
+ }
+#else
+ printk(KERN_WARNING "FritzPCI: NO_PCI_BIOS\n");
+ return (0);
+#endif /* CONFIG_PCI */
+ }
+ cs->hw.avm.isac = cs->hw.avm.cfg_reg + 0x10;
+ if (check_region((cs->hw.avm.cfg_reg), 32)) {
+ printk(KERN_WARNING
+ "HiSax: %s config port %x-%x already in use\n",
+ CardType[card->typ],
+ cs->hw.avm.cfg_reg,
+ cs->hw.avm.cfg_reg + 31);
+ return (0);
+ } else {
+ request_region(cs->hw.avm.cfg_reg, 32,
+ (cs->subtyp == AVM_FRITZ_PCI) ? "avm PCI" : "avm PnP");
+ }
+ switch (cs->subtyp) {
+ case AVM_FRITZ_PCI:
+ val = inl(cs->hw.avm.cfg_reg);
+ printk(KERN_INFO "AVM PCI: stat %#x\n", val);
+ printk(KERN_INFO "AVM PCI: Class %X Rev %d\n",
+ val & 0xff, (val>>8) & 0xff);
+ cs->BC_Read_Reg = &ReadHDLC_s;
+ cs->BC_Write_Reg = &WriteHDLC_s;
+ break;
+ case AVM_FRITZ_PNP:
+ val = inb(cs->hw.avm.cfg_reg);
+ ver = inb(cs->hw.avm.cfg_reg + 1);
+ printk(KERN_INFO "AVM PnP: Class %X Rev %d\n", val, ver);
+ reset_avmpcipnp(cs);
+ cs->BC_Read_Reg = &ReadHDLCPnP;
+ cs->BC_Write_Reg = &WriteHDLCPnP;
+ break;
+ default:
+ printk(KERN_WARNING "AVM unknown subtype %d\n", cs->subtyp);
+ outb(0, cs->hw.avm.cfg_reg + 2);
+ release_region(cs->hw.avm.cfg_reg, 32);
+ return(0);
+ }
+ printk(KERN_INFO "HiSax: %s config irq:%d base:0x%X\n",
+ (cs->subtyp == AVM_FRITZ_PCI) ? "AVM Fritz!PCI" : "AVM Fritz!PnP",
+ cs->irq, cs->hw.avm.cfg_reg);
+
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Send_Data = &fill_hdlc;
+ cs->cardmsg = &AVM_card_msg;
+ ISACVersion(cs, (cs->subtyp == AVM_FRITZ_PCI) ? "AVM PCI:" : "AVM PnP:");
+ return (1);
+}
diff --git a/drivers/isdn/hisax/cert.c b/drivers/isdn/hisax/cert.c
new file mode 100644
index 000000000..a76736b60
--- /dev/null
+++ b/drivers/isdn/hisax/cert.c
@@ -0,0 +1,55 @@
+/* $Id: cert.c,v 2.1 1998/11/15 23:51:15 keil Exp $
+
+ * Author Karsten Keil (keil@isdn4linux.de)
+ *
+ * This file is (c) under GNU PUBLIC LICENSE
+ * For changes and modifications please read
+ * ../../../Documentation/isdn/HiSax.cert
+ *
+ * $Log: cert.c,v $
+ * Revision 2.1 1998/11/15 23:51:15 keil
+ * certification stuff
+ *
+ * Revision 1.2.2.1 1998/11/03 21:46:37 keil
+ * first version
+ *
+ *
+ */
+
+#include <linux/kernel.h>
+
+int
+certification_check(int output) {
+
+#ifdef CERTIFICATION
+#if CERTIFICATION == 0
+ if (output) {
+ printk(KERN_INFO "HiSax: Approval certification valid\n");
+ printk(KERN_INFO "HiSax: Approved with ELSA Quickstep series cards\n");
+ printk(KERN_INFO "HiSax: Approval registration numbers:\n");
+ printk(KERN_INFO "HiSax: German D133361J CETECOM ICT Services GmbH\n");
+ printk(KERN_INFO "HiSax: EU (D133362J) CETECOM ICT Services GmbH\n");
+ }
+ return(0);
+#endif
+#if CERTIFICATION == 1
+ if (output) {
+ printk(KERN_INFO "HiSax: Approval certification failed because of\n");
+ printk(KERN_INFO "HiSax: unauthorized source code changes\n");
+ }
+ return(1);
+#endif
+#if CERTIFICATION == 127
+ if (output) {
+ printk(KERN_INFO "HiSax: Approval certification not possible\n");
+ printk(KERN_INFO "HiSax: because \"md5sum\" is not available\n");
+ }
+ return(2);
+#endif
+#else
+ if (output) {
+ printk(KERN_INFO "HiSax: Certification not verified\n");
+ }
+ return(3);
+#endif
+}
diff --git a/drivers/isdn/hisax/elsa_ser.c b/drivers/isdn/hisax/elsa_ser.c
new file mode 100644
index 000000000..f5ec29839
--- /dev/null
+++ b/drivers/isdn/hisax/elsa_ser.c
@@ -0,0 +1,749 @@
+#include <linux/config.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+
+#define MAX_MODEM_BUF 256
+#define WAKEUP_CHARS (MAX_MODEM_BUF/2)
+#define RS_ISR_PASS_LIMIT 256
+#define BASE_BAUD ( 1843200 / 16 )
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+//#define SERIAL_DEBUG_OPEN 1
+//#define SERIAL_DEBUG_INTR 1
+//#define SERIAL_DEBUG_FLOW 1
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_REG
+//#define SERIAL_DEBUG_REG 1
+
+#ifdef SERIAL_DEBUG_REG
+static u_char deb[32];
+const char *ModemIn[] = {"RBR","IER","IIR","LCR","MCR","LSR","MSR","SCR"};
+const char *ModemOut[] = {"THR","IER","FCR","LCR","MCR","LSR","MSR","SCR"};
+#endif
+
+static char *MInit_1 = "AT&F&C1E0&D2\r\0";
+static char *MInit_2 = "ATL2M1S64=13\r\0";
+static char *MInit_3 = "AT+FCLASS=0\r\0";
+static char *MInit_4 = "ATV1S2=128X1\r\0";
+static char *MInit_5 = "AT\\V8\\N3\r\0";
+static char *MInit_6 = "ATL0M0&G0%E1\r\0";
+static char *MInit_7 = "AT%L1%M0%C3\r\0";
+
+static char *MInit_speed28800 = "AT%G0%B28800\r\0";
+
+static char *MInit_dialout = "ATs7=60 x1 d\r\0";
+static char *MInit_dialin = "ATs7=60 x1 a\r\0";
+
+
+static inline unsigned int serial_in(struct IsdnCardState *cs, int offset)
+{
+#ifdef SERIAL_DEBUG_REG
+ u_int val = inb(cs->hw.elsa.base + 8 + offset);
+ debugl1(cs,"in %s %02x",ModemIn[offset], val);
+ return(val);
+#else
+ return inb(cs->hw.elsa.base + 8 + offset);
+#endif
+}
+
+static inline unsigned int serial_inp(struct IsdnCardState *cs, int offset)
+{
+#ifdef SERIAL_DEBUG_REG
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+ u_int val = inb(cs->hw.elsa.base + 8 + offset);
+ debugl1(cs,"inp %s %02x",ModemIn[offset], val);
+#else
+ u_int val = inb_p(cs->hw.elsa.base + 8 + offset);
+ debugl1(cs,"inP %s %02x",ModemIn[offset], val);
+#endif
+ return(val);
+#else
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+ return inb(cs->hw.elsa.base + 8 + offset);
+#else
+ return inb_p(cs->hw.elsa.base + 8 + offset);
+#endif
+#endif
+}
+
+static inline void serial_out(struct IsdnCardState *cs, int offset, int value)
+{
+#ifdef SERIAL_DEBUG_REG
+ debugl1(cs,"out %s %02x",ModemOut[offset], value);
+#endif
+ outb(value, cs->hw.elsa.base + 8 + offset);
+}
+
+static inline void serial_outp(struct IsdnCardState *cs, int offset,
+ int value)
+{
+#ifdef SERIAL_DEBUG_REG
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+ debugl1(cs,"outp %s %02x",ModemOut[offset], value);
+#else
+ debugl1(cs,"outP %s %02x",ModemOut[offset], value);
+#endif
+#endif
+#ifdef CONFIG_SERIAL_NOPAUSE_IO
+ outb(value, cs->hw.elsa.base + 8 + offset);
+#else
+ outb_p(value, cs->hw.elsa.base + 8 + offset);
+#endif
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct IsdnCardState *cs, int baud)
+{
+ int quot = 0, baud_base;
+ unsigned cval, fcr = 0;
+ int bits;
+ unsigned long flags;
+
+
+ /* byte size and parity */
+ cval = 0x03; bits = 10;
+ /* Determine divisor based on baud rate */
+ baud_base = BASE_BAUD;
+ quot = baud_base / baud;
+ /* If the quotient is ever zero, default to 9600 bps */
+ if (!quot)
+ quot = baud_base / 9600;
+
+ /* Set up FIFO's */
+ if ((baud_base / quot) < 2400)
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+ else
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
+ serial_outp(cs, UART_FCR, fcr);
+ /* CTS flow control flag and modem status interrupts */
+ cs->hw.elsa.IER &= ~UART_IER_MSI;
+ cs->hw.elsa.IER |= UART_IER_MSI;
+ serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+
+ debugl1(cs,"modem quot=0x%x", quot);
+ save_flags(flags);
+ cli();
+ serial_outp(cs, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
+ serial_outp(cs, UART_DLL, quot & 0xff); /* LS of divisor */
+ serial_outp(cs, UART_DLM, quot >> 8); /* MS of divisor */
+ serial_outp(cs, UART_LCR, cval); /* reset DLAB */
+ serial_inp(cs, UART_RX);
+ restore_flags(flags);
+}
+
+static int mstartup(struct IsdnCardState *cs)
+{
+ unsigned long flags;
+ int retval=0;
+
+
+ save_flags(flags); cli();
+
+ /*
+ * Clear the FIFO buffers and disable them
+ * (they will be reenabled in change_speed())
+ */
+ serial_outp(cs, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT));
+
+ /*
+ * At this point there's no way the LSR could still be 0xFF;
+ * if it is, then bail out, because there's likely no UART
+ * here.
+ */
+ if (serial_inp(cs, UART_LSR) == 0xff) {
+ retval = -ENODEV;
+ goto errout;
+ }
+
+ /*
+ * Clear the interrupt registers.
+ */
+ (void) serial_inp(cs, UART_RX);
+ (void) serial_inp(cs, UART_IIR);
+ (void) serial_inp(cs, UART_MSR);
+
+ /*
+ * Now, initialize the UART
+ */
+ serial_outp(cs, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */
+
+ cs->hw.elsa.MCR = 0;
+ cs->hw.elsa.MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
+ serial_outp(cs, UART_MCR, cs->hw.elsa.MCR);
+
+ /*
+ * Finally, enable interrupts
+ */
+ cs->hw.elsa.IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
+ serial_outp(cs, UART_IER, cs->hw.elsa.IER); /* enable interrupts */
+
+ /*
+ * And clear the interrupt registers again for luck.
+ */
+ (void)serial_inp(cs, UART_LSR);
+ (void)serial_inp(cs, UART_RX);
+ (void)serial_inp(cs, UART_IIR);
+ (void)serial_inp(cs, UART_MSR);
+
+ cs->hw.elsa.transcnt = cs->hw.elsa.transp = 0;
+ cs->hw.elsa.rcvcnt = cs->hw.elsa.rcvp =0;
+
+ /*
+ * and set the speed of the serial port
+ */
+ change_speed(cs, BASE_BAUD);
+ cs->hw.elsa.MFlag = 1;
+errout:
+ restore_flags(flags);
+ return retval;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void mshutdown(struct IsdnCardState *cs)
+{
+ unsigned long flags;
+
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk(KERN_DEBUG"Shutting down serial ....");
+#endif
+
+ save_flags(flags); cli(); /* Disable interrupts */
+
+ /*
+ * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+ * here so the queue might never be waken up
+ */
+
+ cs->hw.elsa.IER = 0;
+ serial_outp(cs, UART_IER, 0x00); /* disable all intrs */
+ cs->hw.elsa.MCR &= ~UART_MCR_OUT2;
+
+ /* disable break condition */
+ serial_outp(cs, UART_LCR, serial_inp(cs, UART_LCR) & ~UART_LCR_SBC);
+
+ cs->hw.elsa.MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
+ serial_outp(cs, UART_MCR, cs->hw.elsa.MCR);
+
+ /* disable FIFO's */
+ serial_outp(cs, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT));
+ serial_inp(cs, UART_RX); /* read data port to reset things */
+
+ restore_flags(flags);
+#ifdef SERIAL_DEBUG_OPEN
+ printk(" done\n");
+#endif
+}
+
+inline int
+write_modem(struct BCState *bcs) {
+ int ret=0;
+ struct IsdnCardState *cs = bcs->cs;
+ int count, len, fp, buflen;
+ long flags;
+
+ if (!bcs->tx_skb)
+ return 0;
+ if (bcs->tx_skb->len <= 0)
+ return 0;
+ save_flags(flags);
+ cli();
+ buflen = MAX_MODEM_BUF - cs->hw.elsa.transcnt;
+ len = MIN(buflen, bcs->tx_skb->len);
+ fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp;
+ fp &= (MAX_MODEM_BUF -1);
+ count = MIN(len, MAX_MODEM_BUF - fp);
+ if (count < len) {
+ memcpy(cs->hw.elsa.transbuf + fp, bcs->tx_skb->data, count);
+ skb_pull(bcs->tx_skb, count);
+ cs->hw.elsa.transcnt += count;
+ ret = count;
+ count = len - count;
+ fp = 0;
+ }
+ memcpy((cs->hw.elsa.transbuf + fp), bcs->tx_skb->data, count);
+ skb_pull(bcs->tx_skb, count);
+ cs->hw.elsa.transcnt += count;
+ ret += count;
+
+ if (cs->hw.elsa.transcnt &&
+ !(cs->hw.elsa.IER & UART_IER_THRI)) {
+ cs->hw.elsa.IER |= UART_IER_THRI;
+ serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+ }
+ restore_flags(flags);
+ return(ret);
+}
+
+inline void
+modem_fill(struct BCState *bcs) {
+
+ if (bcs->tx_skb) {
+ if (bcs->tx_skb->len) {
+ write_modem(bcs);
+ return;
+ } else {
+ if (bcs->st->lli.l1writewakeup &&
+ (PACKET_NOACK != bcs->tx_skb->pkt_type))
+ bcs->st->lli.l1writewakeup(bcs->st,
+ bcs->hw.hscx.count);
+ dev_kfree_skb(bcs->tx_skb);
+ bcs->tx_skb = NULL;
+ }
+ }
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ bcs->hw.hscx.count = 0;
+ test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ write_modem(bcs);
+ } else {
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ hscx_sched_event(bcs, B_XMTBUFREADY);
+ }
+}
+
+static inline void receive_chars(struct IsdnCardState *cs,
+ int *status)
+{
+ unsigned char ch;
+ struct sk_buff *skb;
+
+ do {
+ ch = serial_in(cs, UART_RX);
+ if (cs->hw.elsa.rcvcnt >= MAX_MODEM_BUF)
+ break;
+ cs->hw.elsa.rcvbuf[cs->hw.elsa.rcvcnt++] = ch;
+#ifdef SERIAL_DEBUG_INTR
+ printk("DR%02x:%02x...", ch, *status);
+#endif
+ if (*status & (UART_LSR_BI | UART_LSR_PE |
+ UART_LSR_FE | UART_LSR_OE)) {
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("handling exept....");
+#endif
+ }
+ *status = serial_inp(cs, UART_LSR);
+ } while (*status & UART_LSR_DR);
+ if (cs->hw.elsa.MFlag == 2) {
+ if (!(skb = dev_alloc_skb(cs->hw.elsa.rcvcnt)))
+ printk(KERN_WARNING "ElsaSER: receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, cs->hw.elsa.rcvcnt), cs->hw.elsa.rcvbuf,
+ cs->hw.elsa.rcvcnt);
+ skb_queue_tail(& cs->hw.elsa.bcs->rqueue, skb);
+ }
+ hscx_sched_event(cs->hw.elsa.bcs, B_RCVBUFREADY);
+ } else {
+ char tmp[128];
+ char *t = tmp;
+
+ t += sprintf(t, "modem read cnt %d", cs->hw.elsa.rcvcnt);
+ QuickHex(t, cs->hw.elsa.rcvbuf, cs->hw.elsa.rcvcnt);
+ debugl1(cs, tmp);
+ }
+ cs->hw.elsa.rcvcnt = 0;
+}
+
+static inline void transmit_chars(struct IsdnCardState *cs, int *intr_done)
+{
+ int count;
+
+ debugl1(cs, "transmit_chars: p(%x) cnt(%x)", cs->hw.elsa.transp,
+ cs->hw.elsa.transcnt);
+
+ if (cs->hw.elsa.transcnt <= 0) {
+ cs->hw.elsa.IER &= ~UART_IER_THRI;
+ serial_out(cs, UART_IER, cs->hw.elsa.IER);
+ return;
+ }
+ count = 16;
+ do {
+ serial_outp(cs, UART_TX, cs->hw.elsa.transbuf[cs->hw.elsa.transp++]);
+ if (cs->hw.elsa.transp >= MAX_MODEM_BUF)
+ cs->hw.elsa.transp=0;
+ if (--cs->hw.elsa.transcnt <= 0)
+ break;
+ } while (--count > 0);
+ if ((cs->hw.elsa.transcnt < WAKEUP_CHARS) && (cs->hw.elsa.MFlag==2))
+ modem_fill(cs->hw.elsa.bcs);
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("THRE...");
+#endif
+ if (intr_done)
+ *intr_done = 0;
+ if (cs->hw.elsa.transcnt <= 0) {
+ cs->hw.elsa.IER &= ~UART_IER_THRI;
+ serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+ }
+}
+
+#if 0
+static inline void check_modem_status(struct IsdnCardState *cs)
+{
+ int status;
+ struct async_struct *info = cs->hw.elsa.info;
+ struct async_icount *icount;
+
+ status = serial_inp(info, UART_MSR);
+
+ if (status & UART_MSR_ANY_DELTA) {
+ icount = &info->state->icount;
+ /* update input line counters */
+ if (status & UART_MSR_TERI)
+ icount->rng++;
+ if (status & UART_MSR_DDSR)
+ icount->dsr++;
+ if (status & UART_MSR_DDCD) {
+ icount->dcd++;
+ }
+ if (status & UART_MSR_DCTS)
+ icount->cts++;
+// wake_up_interruptible(&info->delta_msr_wait);
+ }
+
+ if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
+#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
+ printk("ttys%d CD now %s...", info->line,
+ (status & UART_MSR_DCD) ? "on" : "off");
+#endif
+ if (status & UART_MSR_DCD)
+// wake_up_interruptible(&info->open_wait);
+;
+ else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ASYNC_CALLOUT_NOHUP))) {
+#ifdef SERIAL_DEBUG_OPEN
+ printk("doing serial hangup...");
+#endif
+ if (info->tty)
+ tty_hangup(info->tty);
+ }
+ }
+#if 0
+ if (info->flags & ASYNC_CTS_FLOW) {
+ if (info->tty->hw_stopped) {
+ if (status & UART_MSR_CTS) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+ printk("CTS tx start...");
+#endif
+ info->tty->hw_stopped = 0;
+ info->IER |= UART_IER_THRI;
+ serial_outp(info, UART_IER, info->IER);
+// rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+ return;
+ }
+ } else {
+ if (!(status & UART_MSR_CTS)) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+ printk("CTS tx stop...");
+#endif
+ info->tty->hw_stopped = 1;
+ info->IER &= ~UART_IER_THRI;
+ serial_outp(info, UART_IER, info->IER);
+ }
+ }
+ }
+#endif 0
+}
+#endif
+
+static void rs_interrupt_elsa(int irq, struct IsdnCardState *cs)
+{
+ int status, iir, msr;
+ int pass_counter = 0;
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("rs_interrupt_single(%d)...", irq);
+#endif
+
+ do {
+ status = serial_inp(cs, UART_LSR);
+ debugl1(cs,"rs LSR %02x", status);
+#ifdef SERIAL_DEBUG_INTR
+ printk("status = %x...", status);
+#endif
+ if (status & UART_LSR_DR)
+ receive_chars(cs, &status);
+ if (status & UART_LSR_THRE)
+ transmit_chars(cs, 0);
+ if (pass_counter++ > RS_ISR_PASS_LIMIT) {
+ printk("rs_single loop break.\n");
+ break;
+ }
+ iir = serial_inp(cs, UART_IIR);
+ debugl1(cs,"rs IIR %02x", iir);
+ if ((iir & 0xf) == 0) {
+ msr = serial_inp(cs, UART_MSR);
+ debugl1(cs,"rs MSR %02x", msr);
+ }
+ } while (!(iir & UART_IIR_NO_INT));
+#ifdef SERIAL_DEBUG_INTR
+ printk("end.\n");
+#endif
+}
+
+extern int open_hscxstate(struct IsdnCardState *cs, struct BCState *bcs);
+extern void modehscx(struct BCState *bcs, int mode, int bc);
+extern void hscx_l2l1(struct PStack *st, int pr, void *arg);
+
+void
+close_elsastate(struct BCState *bcs)
+{
+ struct sk_buff *skb;
+
+ modehscx(bcs, 0, bcs->channel);
+ if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+ if (bcs->hw.hscx.rcvbuf) {
+ if (bcs->mode != L1_MODE_MODEM)
+ kfree(bcs->hw.hscx.rcvbuf);
+ bcs->hw.hscx.rcvbuf = NULL;
+ }
+ while ((skb = skb_dequeue(&bcs->rqueue))) {
+ dev_kfree_skb(skb);
+ }
+ while ((skb = skb_dequeue(&bcs->squeue))) {
+ dev_kfree_skb(skb);
+ }
+ if (bcs->tx_skb) {
+ dev_kfree_skb(bcs->tx_skb);
+ bcs->tx_skb = NULL;
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ }
+ }
+}
+
+void
+modem_write_cmd(struct IsdnCardState *cs, u_char *buf, int len) {
+ int count, fp;
+ u_char *msg = buf;
+ long flags;
+
+ if (!len)
+ return;
+ save_flags(flags);
+ cli();
+ if (len > (MAX_MODEM_BUF - cs->hw.elsa.transcnt)) {
+ restore_flags(flags);
+ return;
+ }
+ fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp;
+ fp &= (MAX_MODEM_BUF -1);
+ count = MIN(len, MAX_MODEM_BUF - fp);
+ if (count < len) {
+ memcpy(cs->hw.elsa.transbuf + fp, msg, count);
+ cs->hw.elsa.transcnt += count;
+ msg += count;
+ count = len - count;
+ fp = 0;
+ }
+ memcpy(cs->hw.elsa.transbuf + fp, msg, count);
+ cs->hw.elsa.transcnt += count;
+ if (cs->hw.elsa.transcnt &&
+ !(cs->hw.elsa.IER & UART_IER_THRI)) {
+ cs->hw.elsa.IER |= UART_IER_THRI;
+ serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+ }
+ restore_flags(flags);
+}
+
+void
+modem_set_init(struct IsdnCardState *cs) {
+ long flags;
+ int timeout;
+
+#define RCV_DELAY 20000
+ save_flags(flags);
+ sti();
+ modem_write_cmd(cs, MInit_1, strlen(MInit_1));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ modem_write_cmd(cs, MInit_2, strlen(MInit_2));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ modem_write_cmd(cs, MInit_3, strlen(MInit_3));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ modem_write_cmd(cs, MInit_4, strlen(MInit_4));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY );
+ modem_write_cmd(cs, MInit_5, strlen(MInit_5));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ modem_write_cmd(cs, MInit_6, strlen(MInit_6));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ modem_write_cmd(cs, MInit_7, strlen(MInit_7));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ restore_flags(flags);
+}
+
+void
+modem_set_dial(struct IsdnCardState *cs, int outgoing) {
+ long flags;
+ int timeout;
+#define RCV_DELAY 20000
+
+ save_flags(flags);
+ sti();
+ modem_write_cmd(cs, MInit_speed28800, strlen(MInit_speed28800));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ if (outgoing)
+ modem_write_cmd(cs, MInit_dialout, strlen(MInit_dialout));
+ else
+ modem_write_cmd(cs, MInit_dialin, strlen(MInit_dialin));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ restore_flags(flags);
+}
+
+void
+modem_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ long flags;
+
+ if (pr == (PH_DATA | REQUEST)) {
+ save_flags(flags);
+ cli();
+ if (st->l1.bcs->tx_skb) {
+ skb_queue_tail(&st->l1.bcs->squeue, skb);
+ restore_flags(flags);
+ } else {
+ st->l1.bcs->tx_skb = skb;
+ test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ st->l1.bcs->hw.hscx.count = 0;
+ restore_flags(flags);
+ write_modem(st->l1.bcs);
+ }
+ } else if (pr == (PH_ACTIVATE | REQUEST)) {
+ test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+ set_arcofi(st->l1.bcs->cs, st->l1.bc);
+ mstartup(st->l1.bcs->cs);
+ modem_set_dial(st->l1.bcs->cs, test_bit(FLG_ORIG, &st->l2.flag));
+ st->l1.bcs->cs->hw.elsa.MFlag=2;
+ } else if (pr == (PH_DEACTIVATE | REQUEST)) {
+ test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ send_arcofi(st->l1.bcs->cs, ARCOFI_XOP_0, st->l1.bc, 0);
+ st->l1.bcs->cs->hw.elsa.MFlag=1;
+ } else {
+ printk(KERN_WARNING"ElsaSer: unknown pr %x\n", pr);
+ }
+}
+
+int
+setstack_elsa(struct PStack *st, struct BCState *bcs)
+{
+
+ bcs->channel = st->l1.bc;
+ switch (st->l1.mode) {
+ case L1_MODE_HDLC:
+ case L1_MODE_TRANS:
+ if (open_hscxstate(st->l1.hardware, bcs))
+ return (-1);
+ st->l2.l2l1 = hscx_l2l1;
+ break;
+ case L1_MODE_MODEM:
+ bcs->mode = L1_MODE_MODEM;
+ if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+ bcs->hw.hscx.rcvbuf = bcs->cs->hw.elsa.rcvbuf;
+ skb_queue_head_init(&bcs->rqueue);
+ skb_queue_head_init(&bcs->squeue);
+ }
+ bcs->tx_skb = NULL;
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ bcs->event = 0;
+ bcs->hw.hscx.rcvidx = 0;
+ bcs->tx_cnt = 0;
+ bcs->cs->hw.elsa.bcs = bcs;
+ st->l2.l2l1 = modem_l2l1;
+ break;
+ }
+ st->l1.bcs = bcs;
+ setstack_manager(st);
+ bcs->st = st;
+ setstack_l1_B(st);
+ return (0);
+}
+
+void
+init_modem(struct IsdnCardState *cs) {
+
+ cs->bcs[0].BC_SetStack = setstack_elsa;
+ cs->bcs[1].BC_SetStack = setstack_elsa;
+ cs->bcs[0].BC_Close = close_elsastate;
+ cs->bcs[1].BC_Close = close_elsastate;
+ if (!(cs->hw.elsa.rcvbuf = kmalloc(MAX_MODEM_BUF,
+ GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "Elsa: No modem mem hw.elsa.rcvbuf\n");
+ return;
+ }
+ if (!(cs->hw.elsa.transbuf = kmalloc(MAX_MODEM_BUF,
+ GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "Elsa: No modem mem hw.elsa.transbuf\n");
+ kfree(cs->hw.elsa.rcvbuf);
+ cs->hw.elsa.rcvbuf = NULL;
+ return;
+ }
+ if (mstartup(cs)) {
+ printk(KERN_WARNING "Elsa: problem startup modem\n");
+ }
+ modem_set_init(cs);
+}
+
+void
+release_modem(struct IsdnCardState *cs) {
+
+ cs->hw.elsa.MFlag = 0;
+ if (cs->hw.elsa.transbuf) {
+ if (cs->hw.elsa.rcvbuf) {
+ mshutdown(cs);
+ kfree(cs->hw.elsa.rcvbuf);
+ cs->hw.elsa.rcvbuf = NULL;
+ }
+ kfree(cs->hw.elsa.transbuf);
+ cs->hw.elsa.transbuf = NULL;
+ }
+}
diff --git a/drivers/isdn/hisax/isar.c b/drivers/isdn/hisax/isar.c
new file mode 100644
index 000000000..ba9247a9f
--- /dev/null
+++ b/drivers/isdn/hisax/isar.c
@@ -0,0 +1,937 @@
+/* $Id: isar.c,v 1.2 1998/11/15 23:54:53 keil Exp $
+
+ * isar.c ISAR (Siemens PSB 7110) specific routines
+ *
+ * Author Karsten Keil (keil@isdn4linux.de)
+ *
+ *
+ * $Log: isar.c,v $
+ * Revision 1.2 1998/11/15 23:54:53 keil
+ * changes from 2.0
+ *
+ * Revision 1.1 1998/08/13 23:33:47 keil
+ * First version, only init
+ *
+ *
+ */
+
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isar.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+#define DBG_LOADFIRM 0
+#define DUMP_MBOXFRAME 2
+
+#define MIN(a,b) ((a<b)?a:b)
+
+void isar_setup(struct IsdnCardState *cs);
+
+static inline int
+waitforHIA(struct IsdnCardState *cs, int timeout)
+{
+
+ while ((cs->BC_Read_Reg(cs, 0, ISAR_HIA) & 1) && timeout) {
+ udelay(1);
+ timeout--;
+ }
+ if (!timeout)
+ printk(KERN_WARNING "HiSax: ISAR waitforHIA timeout\n");
+ return(timeout);
+}
+
+
+int
+sendmsg(struct IsdnCardState *cs, u_char his, u_char creg, u_char len,
+ u_char *msg)
+{
+ long flags;
+ int i;
+
+ if (!waitforHIA(cs, 4000))
+ return(0);
+#if DUMP_MBOXFRAME
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "sendmsg(%02x,%02x,%d)", his, creg, len);
+#endif
+ save_flags(flags);
+ cli();
+ cs->BC_Write_Reg(cs, 0, ISAR_CTRL_H, creg);
+ cs->BC_Write_Reg(cs, 0, ISAR_CTRL_L, len);
+ cs->BC_Write_Reg(cs, 0, ISAR_WADR, 0);
+ if (msg && len) {
+ cs->BC_Write_Reg(cs, 1, ISAR_MBOX, msg[0]);
+ for (i=1; i<len; i++)
+ cs->BC_Write_Reg(cs, 2, ISAR_MBOX, msg[i]);
+#if DUMP_MBOXFRAME>1
+ if (cs->debug & L1_DEB_HSCX_FIFO) {
+ char tmp[256], *t;
+
+ i = len;
+ while (i>0) {
+ t = tmp;
+ t += sprintf(t, "sendmbox cnt %d", len);
+ QuickHex(t, &msg[len-i], (i>64) ? 64:i);
+ debugl1(cs, tmp);
+ i -= 64;
+ }
+ }
+#endif
+ }
+ cs->BC_Write_Reg(cs, 1, ISAR_HIS, his);
+ restore_flags(flags);
+ waitforHIA(cs, 10000);
+ return(1);
+}
+
+/* Call only with IRQ disabled !!! */
+inline void
+rcv_mbox(struct IsdnCardState *cs, struct isar_reg *ireg, u_char *msg)
+{
+ int i;
+
+ cs->BC_Write_Reg(cs, 1, ISAR_RADR, 0);
+ if (msg && ireg->clsb) {
+ msg[0] = cs->BC_Read_Reg(cs, 1, ISAR_MBOX);
+ for (i=1; i < ireg->clsb; i++)
+ msg[i] = cs->BC_Read_Reg(cs, 2, ISAR_MBOX);
+#if DUMP_MBOXFRAME>1
+ if (cs->debug & L1_DEB_HSCX_FIFO) {
+ char tmp[256], *t;
+
+ i = ireg->clsb;
+ while (i>0) {
+ t = tmp;
+ t += sprintf(t, "rcv_mbox cnt %d", ireg->clsb);
+ QuickHex(t, &msg[ireg->clsb-i], (i>64) ? 64:i);
+ debugl1(cs, tmp);
+ i -= 64;
+ }
+ }
+#endif
+ }
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+}
+
+/* Call only with IRQ disabled !!! */
+inline void
+get_irq_infos(struct IsdnCardState *cs, struct isar_reg *ireg)
+{
+ ireg->iis = cs->BC_Read_Reg(cs, 1, ISAR_IIS);
+ ireg->cmsb = cs->BC_Read_Reg(cs, 1, ISAR_CTRL_H);
+ ireg->clsb = cs->BC_Read_Reg(cs, 1, ISAR_CTRL_L);
+#if DUMP_MBOXFRAME
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "rcv_mbox(%02x,%02x,%d)", ireg->iis, ireg->cmsb,
+ ireg->clsb);
+#endif
+}
+
+int
+waitrecmsg(struct IsdnCardState *cs, u_char *len,
+ u_char *msg, int maxdelay)
+{
+ int timeout = 0;
+ long flags;
+ struct isar_reg *ir = cs->bcs[0].hw.isar.reg;
+
+
+ while((!(cs->BC_Read_Reg(cs, 0, ISAR_IRQBIT) & ISAR_IRQSTA)) &&
+ (timeout++ < maxdelay))
+ udelay(1);
+ if (timeout >= maxdelay) {
+ printk(KERN_WARNING"isar recmsg IRQSTA timeout\n");
+ return(0);
+ }
+ save_flags(flags);
+ cli();
+ get_irq_infos(cs, ir);
+ rcv_mbox(cs, ir, msg);
+ *len = ir->clsb;
+ restore_flags(flags);
+ return(1);
+}
+
+int
+ISARVersion(struct IsdnCardState *cs, char *s)
+{
+ int ver;
+ u_char msg[] = ISAR_MSG_HWVER;
+ u_char tmp[64];
+ u_char len;
+ int debug;
+
+ cs->cardmsg(cs, CARD_RESET, NULL);
+ /* disable ISAR IRQ */
+ cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+ debug = cs->debug;
+ cs->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO);
+ if (!sendmsg(cs, ISAR_HIS_VNR, 0, 3, msg))
+ return(-1);
+ if (!waitrecmsg(cs, &len, tmp, 100000))
+ return(-2);
+ cs->debug = debug;
+ if (cs->bcs[0].hw.isar.reg->iis == ISAR_IIS_VNR) {
+ if (len == 1) {
+ ver = tmp[0] & 0xf;
+ printk(KERN_INFO "%s ISAR version %d\n", s, ver);
+ return(ver);
+ }
+ return(-3);
+ }
+ return(-4);
+}
+
+int
+isar_load_firmware(struct IsdnCardState *cs, u_char *buf)
+{
+ int ret, size, cnt, debug;
+ u_char len, nom, noc;
+ u_short sadr, left, *sp;
+ u_char *p = buf;
+ u_char *msg, *tmpmsg, *mp, tmp[64];
+ long flags;
+ struct isar_reg *ireg = cs->bcs[0].hw.isar.reg;
+
+ struct {u_short sadr;
+ u_short len;
+ u_short d_key;
+ } blk_head;
+
+#define BLK_HEAD_SIZE 6
+ if (1 != (ret = ISARVersion(cs, "Testing"))) {
+ printk(KERN_ERR"isar_load_firmware wrong isar version %d\n", ret);
+ return(1);
+ }
+ debug = cs->debug;
+#if DBG_LOADFIRM<2
+ cs->debug &= ~(L1_DEB_HSCX | L1_DEB_HSCX_FIFO);
+#endif
+ printk(KERN_DEBUG"isar_load_firmware buf %#lx\n", (u_long)buf);
+ if ((ret = verify_area(VERIFY_READ, (void *) p, sizeof(int)))) {
+ printk(KERN_ERR"isar_load_firmware verify_area ret %d\n", ret);
+ return ret;
+ }
+ if ((ret = copy_from_user(&size, p, sizeof(int)))) {
+ printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
+ return ret;
+ }
+ p += sizeof(int);
+ printk(KERN_DEBUG"isar_load_firmware size: %d\n", size);
+ if ((ret = verify_area(VERIFY_READ, (void *) p, size))) {
+ printk(KERN_ERR"isar_load_firmware verify_area ret %d\n", ret);
+ return ret;
+ }
+ cnt = 0;
+ /* disable ISAR IRQ */
+ cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+ if (!(msg = kmalloc(256, GFP_KERNEL))) {
+ printk(KERN_ERR"isar_load_firmware no buffer\n");
+ return (1);
+ }
+ if (!(tmpmsg = kmalloc(256, GFP_KERNEL))) {
+ printk(KERN_ERR"isar_load_firmware no tmp buffer\n");
+ kfree(msg);
+ return (1);
+ }
+ while (cnt < size) {
+ if ((ret = copy_from_user(&blk_head, p, BLK_HEAD_SIZE))) {
+ printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
+ goto reterror;
+ }
+ cnt += BLK_HEAD_SIZE;
+ p += BLK_HEAD_SIZE;
+ printk(KERN_DEBUG"isar firmware block (%#x,%5d,%#x)\n",
+ blk_head.sadr, blk_head.len, blk_head.d_key & 0xff);
+ sadr = blk_head.sadr;
+ left = blk_head.len;
+ if (!sendmsg(cs, ISAR_HIS_DKEY, blk_head.d_key & 0xff, 0, NULL)) {
+ printk(KERN_ERR"isar sendmsg dkey failed\n");
+ ret = 1;goto reterror;
+ }
+ if (!waitrecmsg(cs, &len, tmp, 100000)) {
+ printk(KERN_ERR"isar waitrecmsg dkey failed\n");
+ ret = 1;goto reterror;
+ }
+ if ((ireg->iis != ISAR_IIS_DKEY) || ireg->cmsb || len) {
+ printk(KERN_ERR"isar wrong dkey response (%x,%x,%x)\n",
+ ireg->iis, ireg->cmsb, len);
+ ret = 1;goto reterror;
+ }
+ while (left>0) {
+ noc = MIN(126, left);
+ nom = 2*noc;
+ mp = msg;
+ *mp++ = sadr / 256;
+ *mp++ = sadr % 256;
+ left -= noc;
+ *mp++ = noc;
+ if ((ret = copy_from_user(tmpmsg, p, nom))) {
+ printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
+ goto reterror;
+ }
+ p += nom;
+ cnt += nom;
+ nom += 3;
+ sp = (u_short *)tmpmsg;
+#if DBG_LOADFIRM
+ printk(KERN_DEBUG"isar: load %3d words at %04x\n",
+ noc, sadr);
+#endif
+ sadr += noc;
+ while(noc) {
+ *mp++ = *sp / 256;
+ *mp++ = *sp % 256;
+ sp++;
+ noc--;
+ }
+ if (!sendmsg(cs, ISAR_HIS_FIRM, 0, nom, msg)) {
+ printk(KERN_ERR"isar sendmsg prog failed\n");
+ ret = 1;goto reterror;
+ }
+ if (!waitrecmsg(cs, &len, tmp, 100000)) {
+ printk(KERN_ERR"isar waitrecmsg prog failed\n");
+ ret = 1;goto reterror;
+ }
+ if ((ireg->iis != ISAR_IIS_FIRM) || ireg->cmsb || len) {
+ printk(KERN_ERR"isar wrong prog response (%x,%x,%x)\n",
+ ireg->iis, ireg->cmsb, len);
+ ret = 1;goto reterror;
+ }
+ }
+ printk(KERN_DEBUG"isar firmware block %5d words loaded\n",
+ blk_head.len);
+ }
+ msg[0] = 0xff;
+ msg[1] = 0xfe;
+ ireg->bstat = 0;
+ if (!sendmsg(cs, ISAR_HIS_STDSP, 0, 2, msg)) {
+ printk(KERN_ERR"isar sendmsg start dsp failed\n");
+ ret = 1;goto reterror;
+ }
+ if (!waitrecmsg(cs, &len, tmp, 100000)) {
+ printk(KERN_ERR"isar waitrecmsg start dsp failed\n");
+ ret = 1;goto reterror;
+ }
+ if ((ireg->iis != ISAR_IIS_STDSP) || ireg->cmsb || len) {
+ printk(KERN_ERR"isar wrong start dsp response (%x,%x,%x)\n",
+ ireg->iis, ireg->cmsb, len);
+ ret = 1;goto reterror;
+ } else
+ printk(KERN_DEBUG"isar start dsp success\n");
+ /* NORMAL mode entered */
+ /* Enable IRQs of ISAR */
+ cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, ISAR_IRQSTA);
+ save_flags(flags);
+ sti();
+ cnt = 1000; /* max 1s */
+ while ((!ireg->bstat) && cnt) {
+ udelay(1000);
+ cnt--;
+ }
+ if (!cnt) {
+ printk(KERN_ERR"isar no general status event received\n");
+ ret = 1;goto reterrflg;
+ } else {
+ printk(KERN_DEBUG"isar general status event %x\n",
+ ireg->bstat);
+ }
+ ireg->iis = 0;
+ if (!sendmsg(cs, ISAR_HIS_DIAG, ISAR_CTRL_STST, 0, NULL)) {
+ printk(KERN_ERR"isar sendmsg self tst failed\n");
+ ret = 1;goto reterrflg;
+ }
+ cnt = 1000; /* max 10 ms */
+ while ((ireg->iis != ISAR_IIS_DIAG) && cnt) {
+ udelay(10);
+ cnt--;
+ }
+ if (!cnt) {
+ printk(KERN_ERR"isar no self tst response\n");
+ ret = 1;goto reterrflg;
+ } else if ((ireg->cmsb == ISAR_CTRL_STST) && (ireg->clsb == 1)
+ && (ireg->par[0] == 0)) {
+ printk(KERN_DEBUG"isar selftest OK\n");
+ } else {
+ printk(KERN_DEBUG"isar selftest not OK %x/%x/%x\n",
+ ireg->cmsb, ireg->clsb, ireg->par[0]);
+ ret = 1;goto reterror;
+ }
+ ireg->iis = 0;
+ if (!sendmsg(cs, ISAR_HIS_DIAG, ISAR_CTRL_SWVER, 0, NULL)) {
+ printk(KERN_ERR"isar RQST SVN failed\n");
+ ret = 1;goto reterror;
+ }
+ cnt = 10000; /* max 100 ms */
+ while ((ireg->iis != ISAR_IIS_DIAG) && cnt) {
+ udelay(10);
+ cnt--;
+ }
+ if (!cnt) {
+ printk(KERN_ERR"isar no SVN response\n");
+ ret = 1;goto reterrflg;
+ } else {
+ if ((ireg->cmsb == ISAR_CTRL_SWVER) && (ireg->clsb == 1))
+ printk(KERN_DEBUG"isar software version %#x\n",
+ ireg->par[0]);
+ else {
+ printk(KERN_ERR"isar wrong swver response (%x,%x) cnt(%d)\n",
+ ireg->cmsb, ireg->clsb, cnt);
+ ret = 1;goto reterrflg;
+ }
+ }
+ cs->debug = debug;
+ isar_setup(cs);
+ ret = 0;
+reterrflg:
+ restore_flags(flags);
+reterror:
+ cs->debug = debug;
+ if (ret)
+ /* disable ISAR IRQ */
+ cs->BC_Write_Reg(cs, 0, ISAR_IRQBIT, 0);
+ kfree(msg);
+ kfree(tmpmsg);
+ return(ret);
+}
+
+void
+isar_sched_event(struct BCState *bcs, int event)
+{
+ bcs->event |= 1 << event;
+ queue_task(&bcs->tqueue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+}
+
+static inline void
+isar_rcv_frame(struct IsdnCardState *cs, struct BCState *bcs)
+{
+ u_char *ptr;
+ struct sk_buff *skb;
+ struct isar_reg *ireg = bcs->hw.isar.reg;
+
+ if (!ireg->clsb) {
+ debugl1(cs, "isar zero len frame");
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+ return;
+ }
+ switch (bcs->mode) {
+ case L1_MODE_NULL:
+ debugl1(cs, "isar mode 0 spurious IIS_RDATA %x/%x/%x",
+ ireg->iis, ireg->cmsb, ireg->clsb);
+ printk(KERN_WARNING"isar mode 0 spurious IIS_RDATA %x/%x/%x\n",
+ ireg->iis, ireg->cmsb, ireg->clsb);
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+ break;
+ case L1_MODE_TRANS:
+ if ((skb = dev_alloc_skb(ireg->clsb))) {
+ rcv_mbox(cs, ireg, (u_char *)skb_put(skb, ireg->clsb));
+ skb_queue_tail(&bcs->rqueue, skb);
+ isar_sched_event(bcs, B_RCVBUFREADY);
+ } else {
+ printk(KERN_WARNING "HiSax: skb out of memory\n");
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+ }
+ break;
+ case L1_MODE_HDLC:
+ if ((bcs->hw.isar.rcvidx + ireg->clsb) > HSCX_BUFMAX) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "isar_rcv_frame: incoming packet too large");
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+ bcs->hw.isar.rcvidx = 0;
+ } else if (ireg->cmsb & HDLC_ERROR) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "isar frame error %x len %d",
+ ireg->cmsb, ireg->clsb);
+ bcs->hw.isar.rcvidx = 0;
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+ } else {
+ if (ireg->cmsb & HDLC_FSD)
+ bcs->hw.isar.rcvidx = 0;
+ ptr = bcs->hw.isar.rcvbuf + bcs->hw.isar.rcvidx;
+ bcs->hw.isar.rcvidx += ireg->clsb;
+ rcv_mbox(cs, ireg, ptr);
+ if (ireg->cmsb & HDLC_FED) {
+ if (bcs->hw.isar.rcvidx < 3) { /* last 2 bytes are the FCS */
+ printk(KERN_WARNING "ISAR: HDLC frame too short(%d)\n",
+ bcs->hw.isar.rcvidx);
+ } else if (!(skb = dev_alloc_skb(bcs->hw.isar.rcvidx-2)))
+ printk(KERN_WARNING "ISAR: receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, bcs->hw.isar.rcvidx-2),
+ bcs->hw.isar.rcvbuf, bcs->hw.isar.rcvidx-2);
+ skb_queue_tail(&bcs->rqueue, skb);
+ isar_sched_event(bcs, B_RCVBUFREADY);
+ }
+ }
+ }
+ break;
+ default:
+ printk(KERN_ERR"isar_rcv_frame mode (%x)error\n", bcs->mode);
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+ break;
+ }
+}
+
+void
+isar_fill_fifo(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int count;
+ u_char msb;
+ u_char *ptr;
+ long flags;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "isar_fill_fifo");
+ if (!bcs->tx_skb)
+ return;
+ if (bcs->tx_skb->len <= 0)
+ return;
+ if (!(bcs->hw.isar.reg->bstat &
+ (bcs->hw.isar.dpath == 1 ? BSTAT_RDM1 : BSTAT_RDM2)))
+ return;
+ if (bcs->tx_skb->len > bcs->hw.isar.mml) {
+ msb = 0;
+ count = bcs->hw.isar.mml;
+ } else {
+ count = bcs->tx_skb->len;
+ msb = HDLC_FED;
+ }
+ if (!bcs->hw.isar.txcnt)
+ msb |= HDLC_FST;
+ save_flags(flags);
+ cli();
+ ptr = bcs->tx_skb->data;
+ skb_pull(bcs->tx_skb, count);
+ bcs->tx_cnt -= count;
+ bcs->hw.isar.txcnt += count;
+ switch (bcs->mode) {
+ case L1_MODE_NULL:
+ printk(KERN_ERR"isar_fill_fifo wrong mode 0\n");
+ break;
+ case L1_MODE_TRANS:
+ if (!sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+ 0, count, ptr)) {
+ if (cs->debug)
+ debugl1(cs, "isar bin data send dp%d failed",
+ bcs->hw.isar.dpath);
+ }
+ break;
+ case L1_MODE_HDLC:
+ if (!sendmsg(cs, SET_DPS(bcs->hw.isar.dpath) | ISAR_HIS_SDATA,
+ msb, count, ptr)) {
+ if (cs->debug)
+ debugl1(cs, "isar hdlc data send dp%d failed",
+ bcs->hw.isar.dpath);
+ }
+ break;
+ default:
+ printk(KERN_ERR"isar_fill_fifo mode (%x)error\n", bcs->mode);
+ break;
+ }
+ restore_flags(flags);
+}
+
+inline
+struct BCState *sel_bcs_isar(struct IsdnCardState *cs, u_char dpath)
+{
+ if ((!dpath) || (dpath == 3))
+ return(NULL);
+ if (cs->bcs[0].hw.isar.dpath == dpath)
+ return(&cs->bcs[0]);
+ if (cs->bcs[1].hw.isar.dpath == dpath)
+ return(&cs->bcs[1]);
+ return(NULL);
+}
+
+inline void
+send_frames(struct BCState *bcs)
+{
+ if (bcs->tx_skb) {
+ if (bcs->tx_skb->len) {
+ isar_fill_fifo(bcs);
+ return;
+ } else {
+ if (bcs->st->lli.l1writewakeup &&
+ (PACKET_NOACK != bcs->tx_skb->pkt_type))
+ bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.isar.txcnt);
+ dev_kfree_skb(bcs->tx_skb);
+ bcs->hw.isar.txcnt = 0;
+ bcs->tx_skb = NULL;
+ }
+ }
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ bcs->hw.isar.txcnt = 0;
+ test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ isar_fill_fifo(bcs);
+ } else {
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ isar_sched_event(bcs, B_XMTBUFREADY);
+ }
+}
+
+inline void
+check_send(struct IsdnCardState *cs, u_char rdm)
+{
+ struct BCState *bcs;
+
+ if (rdm & BSTAT_RDM1) {
+ if ((bcs = sel_bcs_isar(cs, 1))) {
+ if (bcs->mode) {
+ send_frames(bcs);
+ }
+ }
+ }
+ if (rdm & BSTAT_RDM2) {
+ if ((bcs = sel_bcs_isar(cs, 2))) {
+ if (bcs->mode) {
+ send_frames(bcs);
+ }
+ }
+ }
+
+}
+
+static char debbuf[64];
+
+void
+isar_int_main(struct IsdnCardState *cs)
+{
+ long flags;
+ struct isar_reg *ireg = cs->bcs[0].hw.isar.reg;
+ struct BCState *bcs;
+
+ save_flags(flags);
+ cli();
+ get_irq_infos(cs, ireg);
+ switch (ireg->iis & ISAR_IIS_MSCMSD) {
+ case ISAR_IIS_RDATA:
+ if ((bcs = sel_bcs_isar(cs, ireg->iis >> 6))) {
+ isar_rcv_frame(cs, bcs);
+ } else {
+ debugl1(cs, "isar spurious IIS_RDATA %x/%x/%x",
+ ireg->iis, ireg->cmsb, ireg->clsb);
+ printk(KERN_WARNING"isar spurious IIS_RDATA %x/%x/%x\n",
+ ireg->iis, ireg->cmsb, ireg->clsb);
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+ }
+ break;
+ case ISAR_IIS_GSTEV:
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+ ireg->bstat |= ireg->cmsb;
+ check_send(cs, ireg->cmsb);
+ break;
+ case ISAR_IIS_BSTEV:
+ cs->BC_Write_Reg(cs, 1, ISAR_IIA, 0);
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "Buffer STEV dpath%d msb(%x)",
+ ireg->iis>>6, ireg->cmsb);
+ break;
+ case ISAR_IIS_DIAG:
+ case ISAR_IIS_PSTRSP:
+ case ISAR_IIS_PSTEV:
+ case ISAR_IIS_BSTRSP:
+ case ISAR_IIS_IOM2RSP:
+ rcv_mbox(cs, ireg, (u_char *)ireg->par);
+ if ((cs->debug & (L1_DEB_HSCX | L1_DEB_HSCX_FIFO))
+ == L1_DEB_HSCX) {
+ u_char *tp=debbuf;
+
+ tp += sprintf(debbuf, "msg iis(%x) msb(%x)",
+ ireg->iis, ireg->cmsb);
+ QuickHex(tp, (u_char *)ireg->par, ireg->clsb);
+ debugl1(cs, debbuf);
+ }
+ break;
+ default:
+ rcv_mbox(cs, ireg, debbuf);
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "unhandled msg iis(%x) ctrl(%x/%x)",
+ ireg->iis, ireg->cmsb, ireg->clsb);
+ break;
+ }
+ restore_flags(flags);
+}
+
+void
+setup_pump(struct BCState *bcs) {
+ struct IsdnCardState *cs = bcs->cs;
+ u_char dps = SET_DPS(bcs->hw.isar.dpath);
+
+ switch (bcs->mode) {
+ case L1_MODE_NULL:
+ case L1_MODE_TRANS:
+ case L1_MODE_HDLC:
+ if (!sendmsg(cs, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL)) {
+ if (cs->debug)
+ debugl1(cs, "isar pump bypass cfg dp%d failed",
+ bcs->hw.isar.dpath);
+ }
+ break;
+ }
+ if (!sendmsg(cs, dps | ISAR_HIS_PSTREQ, 0, 0, NULL)) {
+ if (cs->debug)
+ debugl1(cs, "isar pump status req dp%d failed",
+ bcs->hw.isar.dpath);
+ }
+}
+
+void
+setup_sart(struct BCState *bcs) {
+ struct IsdnCardState *cs = bcs->cs;
+ u_char dps = SET_DPS(bcs->hw.isar.dpath);
+
+ switch (bcs->mode) {
+ case L1_MODE_NULL:
+ if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE, 0, NULL)) {
+ if (cs->debug)
+ debugl1(cs, "isar sart disable dp%d failed",
+ bcs->hw.isar.dpath);
+ }
+ break;
+ case L1_MODE_TRANS:
+ if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_BINARY, 2, "\0\0")) {
+ if (cs->debug)
+ debugl1(cs, "isar sart binary dp%d failed",
+ bcs->hw.isar.dpath);
+ }
+ break;
+ case L1_MODE_HDLC:
+ if (!sendmsg(cs, dps | ISAR_HIS_SARTCFG, SMODE_HDLC, 1, "\0")) {
+ if (cs->debug)
+ debugl1(cs, "isar sart binary dp%d failed",
+ bcs->hw.isar.dpath);
+ }
+ break;
+ }
+ if (!sendmsg(cs, dps | ISAR_HIS_BSTREQ, 0, 0, NULL)) {
+ if (cs->debug)
+ debugl1(cs, "isar buf stat req dp%d failed",
+ bcs->hw.isar.dpath);
+ }
+}
+
+void
+setup_iom2(struct BCState *bcs) {
+ struct IsdnCardState *cs = bcs->cs;
+ u_char dps = SET_DPS(bcs->hw.isar.dpath);
+ u_char cmsb = 0, msg[5] = {0x10,0,0,0,0};
+
+ switch (bcs->mode) {
+ case L1_MODE_NULL:
+ /* dummy slot */
+ msg[1] = msg[3] = bcs->hw.isar.dpath + 2;
+ break;
+ case L1_MODE_TRANS:
+ case L1_MODE_HDLC:
+ cmsb = 0x80;
+ if (bcs->channel)
+ msg[1] = msg[3] = 1;
+ break;
+ }
+ if (!sendmsg(cs, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg)) {
+ if (cs->debug)
+ debugl1(cs, "isar iom2 dp%d failed", bcs->hw.isar.dpath);
+ }
+ if (!sendmsg(cs, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL)) {
+ if (cs->debug)
+ debugl1(cs, "isar IOM2 cfg req dp%d failed",
+ bcs->hw.isar.dpath);
+ }
+}
+
+int
+modeisar(struct BCState *bcs, int mode, int bc)
+{
+ struct IsdnCardState *cs = bcs->cs;
+
+ /* Here we are selecting the best datapath for requested mode */
+ if(bcs->mode == L1_MODE_NULL) { /* New Setup */
+ bcs->channel = bc;
+ switch (mode) {
+ case L1_MODE_NULL: /* init */
+ break;
+ case L1_MODE_TRANS:
+ case L1_MODE_HDLC:
+ /* best is datapath 2 */
+ if (!test_and_set_bit(ISAR_DP2_USE,
+ &bcs->hw.isar.reg->Flags))
+ bcs->hw.isar.dpath = 2;
+ else if (!test_and_set_bit(ISAR_DP1_USE,
+ &bcs->hw.isar.reg->Flags))
+ bcs->hw.isar.dpath = 1;
+ else {
+ printk(KERN_ERR"isar modeisar both pathes in use\n");
+ return(1);
+ }
+ break;
+ }
+ }
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "isar dp%d mode %d->%d ichan %d",
+ bcs->hw.isar.dpath, bcs->mode, mode, bc);
+ bcs->mode = mode;
+ setup_pump(bcs);
+ setup_sart(bcs);
+ setup_iom2(bcs);
+ if (bcs->mode == L1_MODE_NULL) {
+ /* Clear resources */
+ if (bcs->hw.isar.dpath == 1)
+ test_and_clear_bit(ISAR_DP1_USE, &bcs->hw.isar.reg->Flags);
+ else if (bcs->hw.isar.dpath == 2)
+ test_and_clear_bit(ISAR_DP2_USE, &bcs->hw.isar.reg->Flags);
+ bcs->hw.isar.dpath = 0;
+ }
+ return(0);
+}
+
+void
+isar_setup(struct IsdnCardState *cs)
+{
+ u_char msg;
+ int i;
+
+ /* Dpath 1, 2 */
+ msg = 61;
+ for (i=0; i<2; i++) {
+ /* Buffer Config */
+ if (!sendmsg(cs, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) |
+ ISAR_HIS_P12CFG, 4, 1, &msg)) {
+ if (cs->debug)
+ debugl1(cs, "isar P%dCFG failed", i+1);
+ }
+ cs->bcs[i].hw.isar.mml = msg;
+ cs->bcs[i].mode = 0;
+ cs->bcs[i].hw.isar.dpath = i + 1;
+ modeisar(&cs->bcs[i], 0, 0);
+ }
+}
+
+void
+isar_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ long flags;
+
+ switch (pr) {
+ case (PH_DATA | REQUEST):
+ save_flags(flags);
+ cli();
+ if (st->l1.bcs->tx_skb) {
+ skb_queue_tail(&st->l1.bcs->squeue, skb);
+ restore_flags(flags);
+ } else {
+ st->l1.bcs->tx_skb = skb;
+ test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ if (st->l1.bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(st->l1.bcs->cs, "DRQ set BC_FLG_BUSY");
+ st->l1.bcs->hw.isar.txcnt = 0;
+ restore_flags(flags);
+ st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+ }
+ break;
+ case (PH_PULL | INDICATION):
+ if (st->l1.bcs->tx_skb) {
+ printk(KERN_WARNING "isar_l2l1: this shouldn't happen\n");
+ break;
+ }
+ test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ if (st->l1.bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(st->l1.bcs->cs, "PUI set BC_FLG_BUSY");
+ st->l1.bcs->tx_skb = skb;
+ st->l1.bcs->hw.isar.txcnt = 0;
+ st->l1.bcs->cs->BC_Send_Data(st->l1.bcs);
+ break;
+ case (PH_PULL | REQUEST):
+ if (!st->l1.bcs->tx_skb) {
+ test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+ st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ } else
+ test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+ break;
+ case (PH_ACTIVATE | REQUEST):
+ test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ modeisar(st->l1.bcs, st->l1.mode, st->l1.bc);
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | REQUEST):
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | CONFIRM):
+ test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
+ test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
+ if (st->l1.bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(st->l1.bcs->cs, "PDAC clear BC_FLG_BUSY");
+ modeisar(st->l1.bcs, 0, st->l1.bc);
+ st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ break;
+ }
+}
+
+void
+close_isarstate(struct BCState *bcs)
+{
+ modeisar(bcs, 0, bcs->channel);
+ if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+ if (bcs->hw.isar.rcvbuf) {
+ kfree(bcs->hw.isar.rcvbuf);
+ bcs->hw.isar.rcvbuf = NULL;
+ }
+ discard_queue(&bcs->rqueue);
+ discard_queue(&bcs->squeue);
+ if (bcs->tx_skb) {
+ dev_kfree_skb(bcs->tx_skb);
+ bcs->tx_skb = NULL;
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs, "closeisar clear BC_FLG_BUSY");
+ }
+ }
+}
+
+int
+open_isarstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+ if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+ if (!(bcs->hw.isar.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for isar.rcvbuf\n");
+ return (1);
+ }
+ skb_queue_head_init(&bcs->rqueue);
+ skb_queue_head_init(&bcs->squeue);
+ }
+ bcs->tx_skb = NULL;
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "openisar clear BC_FLG_BUSY");
+ bcs->event = 0;
+ bcs->hw.isar.rcvidx = 0;
+ bcs->tx_cnt = 0;
+ return (0);
+}
+
+int
+setstack_isar(struct PStack *st, struct BCState *bcs)
+{
+ bcs->channel = st->l1.bc;
+ if (open_isarstate(st->l1.hardware, bcs))
+ return (-1);
+ st->l1.bcs = bcs;
+ st->l2.l2l1 = isar_l2l1;
+ setstack_manager(st);
+ bcs->st = st;
+ setstack_l1_B(st);
+ return (0);
+}
+
+HISAX_INITFUNC(void
+initisar(struct IsdnCardState *cs))
+{
+ cs->bcs[0].BC_SetStack = setstack_isar;
+ cs->bcs[1].BC_SetStack = setstack_isar;
+ cs->bcs[0].BC_Close = close_isarstate;
+ cs->bcs[1].BC_Close = close_isarstate;
+}
diff --git a/drivers/isdn/hisax/isar.h b/drivers/isdn/hisax/isar.h
new file mode 100644
index 000000000..de892f813
--- /dev/null
+++ b/drivers/isdn/hisax/isar.h
@@ -0,0 +1,91 @@
+/* $Id: isar.h,v 1.2 1998/11/15 23:54:54 keil Exp $
+ * isar.h ISAR (Siemens PSB 7110) specific defines
+ *
+ * Author Karsten Keil (keil@isdn4linux.de)
+ *
+ *
+ * $Log: isar.h,v $
+ * Revision 1.2 1998/11/15 23:54:54 keil
+ * changes from 2.0
+ *
+ * Revision 1.1 1998/08/13 23:33:48 keil
+ * First version, only init
+ *
+ *
+ */
+
+#define ISAR_IRQMSK 0x04
+#define ISAR_IRQSTA 0x04
+#define ISAR_IRQBIT 0x75
+#define ISAR_CTRL_H 0x61
+#define ISAR_CTRL_L 0x60
+#define ISAR_IIS 0x58
+#define ISAR_IIA 0x58
+#define ISAR_HIS 0x50
+#define ISAR_HIA 0x50
+#define ISAR_MBOX 0x4c
+#define ISAR_WADR 0x4a
+#define ISAR_RADR 0x48
+
+#define ISAR_HIS_VNR 0x14
+#define ISAR_HIS_DKEY 0x02
+#define ISAR_HIS_FIRM 0x1e
+#define ISAR_HIS_STDSP 0x08
+#define ISAR_HIS_DIAG 0x05
+#define ISAR_HIS_P0CFG 0x3c
+#define ISAR_HIS_P12CFG 0x24
+#define ISAR_HIS_SARTCFG 0x25
+#define ISAR_HIS_PUMPCFG 0x26
+#define ISAR_HIS_IOM2CFG 0x27
+#define ISAR_HIS_IOM2REQ 0x07
+#define ISAR_HIS_BSTREQ 0x0c
+#define ISAR_HIS_PSTREQ 0x0e
+#define ISAR_HIS_SDATA 0x20
+#define ISAR_HIS_DPS1 0x40
+#define ISAR_HIS_DPS2 0x80
+#define SET_DPS(x) ((x<<6) & 0xc0)
+
+#define ISAR_IIS_MSCMSD 0x3f
+#define ISAR_IIS_VNR 0x15
+#define ISAR_IIS_DKEY 0x03
+#define ISAR_IIS_FIRM 0x1f
+#define ISAR_IIS_STDSP 0x09
+#define ISAR_IIS_DIAG 0x25
+#define ISAR_IIS_GSTEV 0x0
+#define ISAR_IIS_BSTEV 0x28
+#define ISAR_IIS_BSTRSP 0x2c
+#define ISAR_IIS_PSTRSP 0x2e
+#define ISAR_IIS_PSTEV 0x2a
+#define ISAR_IIS_IOM2RSP 0x27
+
+#define ISAR_IIS_RDATA 0x20
+#define ISAR_CTRL_SWVER 0x10
+#define ISAR_CTRL_STST 0x40
+
+#define ISAR_MSG_HWVER {0x20, 0, 1}
+
+#define ISAR_DP1_USE 1
+#define ISAR_DP2_USE 2
+
+#define PMOD_BYPASS 7
+
+#define SMODE_DISABLE 0
+#define SMODE_HDLC 3
+#define SMODE_BINARY 4
+
+#define HDLC_FED 0x40
+#define HDLC_FSD 0x20
+#define HDLC_FST 0x20
+#define HDLC_ERROR 0x1c
+
+#define BSTAT_RDM0 0x1
+#define BSTAT_RDM1 0x2
+#define BSTAT_RDM2 0x4
+#define BSTAT_RDM3 0x8
+
+
+extern int ISARVersion(struct IsdnCardState *cs, char *s);
+extern int isar_load_firmware(struct IsdnCardState *cs, u_char *buf);
+extern void isar_int_main(struct IsdnCardState *cs);
+extern void initisar(struct IsdnCardState *cs);
+extern void isar_fill_fifo(struct BCState *bcs);
diff --git a/drivers/isdn/hisax/md5sums.asc b/drivers/isdn/hisax/md5sums.asc
new file mode 100644
index 000000000..49888c55d
--- /dev/null
+++ b/drivers/isdn/hisax/md5sums.asc
@@ -0,0 +1,29 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+
+# This are valid md5sums for certificated HiSax driver.
+# The certification is valid only if the md5sums of all files match.
+# The certification is valid only for ELSA QuickStep cards in the moment.
+# Read ../../../Documentation/isdn/HiSax.cert for more informations.
+#
+a273c532aec063574273ee519975cd9a isac.c
+27c5c5bfa2ceabf02e2e6d686b03abde isdnl1.c
+8c89ac659d3188ab997fb575da22b566 isdnl2.c
+d0fa912aa284b8fd19fed86b65999f6f isdnl3.c
+1bce120740b615006286ad9b2d7fcdcb tei.c
+8845f88dd17917d9b58badeff1605057 callc.c
+f3ec2a634f06074d16167aaba02b6dc1 cert.c
+71840ec8189f42b0db86fb38e5e5984c l3dss1.c
+1882de8bea921b9ccd98fbe77267aa04 l3_1tr6.c
+3bd7af3a11693d028300278744d0da09 elsa.c
+# end of md5sums
+
+-----BEGIN PGP SIGNATURE-----
+Version: 2.6.3i
+Charset: noconv
+
+iQCVAwUBNrl5JDpxHvX/mS9tAQHm8wP+Nk64UJ2abdDG/igXZSrwcYhX/Kp7cxt9
+ccYp+aaur+pALA0lxwY3xcLt9u36fCYuTLHAVmQoiC9Vbemj37yzM2rUpz9nkw/7
+D6gLqZs2jxVpAwVVJgp0JwDONKXaRX6Lt2EPD9PTW6vxRWEu0HqGhM5hrtd/o4rV
+mC1W7Wj13XM=
+=LdhT
+-----END PGP SIGNATURE-----
diff --git a/drivers/isdn/hisax/s0box.c b/drivers/isdn/hisax/s0box.c
new file mode 100644
index 000000000..479880bb3
--- /dev/null
+++ b/drivers/isdn/hisax/s0box.c
@@ -0,0 +1,277 @@
+/* $Id: s0box.c,v 2.1 1998/04/15 16:38:24 keil Exp $
+
+ * s0box.c low level stuff for Creatix S0BOX
+ *
+ * Author S0BOX specific stuff: Enrik Berkhan (enrik@starfleet.inka.de)
+ *
+ *
+ */
+#define __NO_VERSION__
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+const char *s0box_revision = "$Revision: 2.1 $";
+
+static inline void
+writereg(unsigned int padr, signed int addr, u_char off, u_char val) {
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(0x1c,padr+2);
+ outb_p(0x14,padr+2);
+ outb_p((addr+off)&0x7f,padr);
+ outb_p(0x16,padr+2);
+ outb_p(val,padr);
+ outb_p(0x17,padr+2);
+ outb_p(0x14,padr+2);
+ outb_p(0x1c,padr+2);
+ restore_flags(flags);
+}
+
+static u_char nibtab[] = { 1, 9, 5, 0xd, 3, 0xb, 7, 0xf,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 8, 4, 0xc, 2, 0xa, 6, 0xe } ;
+
+static inline u_char
+readreg(unsigned int padr, signed int addr, u_char off) {
+ register u_char n1, n2;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(0x1c,padr+2);
+ outb_p(0x14,padr+2);
+ outb_p((addr+off)|0x80,padr);
+ outb_p(0x16,padr+2);
+ outb_p(0x17,padr+2);
+ n1 = (inb_p(padr+1) >> 3) & 0x17;
+ outb_p(0x16,padr+2);
+ n2 = (inb_p(padr+1) >> 3) & 0x17;
+ outb_p(0x14,padr+2);
+ outb_p(0x1c,padr+2);
+ restore_flags(flags);
+ return nibtab[n1] | (nibtab[n2] << 4);
+}
+
+static inline void
+read_fifo(unsigned int padr, signed int adr, u_char * data, int size)
+{
+ int i;
+ register u_char n1, n2;
+
+ outb_p(0x1c, padr+2);
+ outb_p(0x14, padr+2);
+ outb_p(adr|0x80, padr);
+ outb_p(0x16, padr+2);
+ for (i=0; i<size; i++) {
+ outb_p(0x17, padr+2);
+ n1 = (inb_p(padr+1) >> 3) & 0x17;
+ outb_p(0x16,padr+2);
+ n2 = (inb_p(padr+1) >> 3) & 0x17;
+ *(data++)=nibtab[n1] | (nibtab[n2] << 4);
+ }
+ outb_p(0x14,padr+2);
+ outb_p(0x1c,padr+2);
+ return;
+}
+
+static inline void
+write_fifo(unsigned int padr, signed int adr, u_char * data, int size)
+{
+ int i;
+ outb_p(0x1c, padr+2);
+ outb_p(0x14, padr+2);
+ outb_p(adr&0x7f, padr);
+ for (i=0; i<size; i++) {
+ outb_p(0x16, padr+2);
+ outb_p(*(data++), padr);
+ outb_p(0x17, padr+2);
+ }
+ outb_p(0x14,padr+2);
+ outb_p(0x1c,padr+2);
+ return;
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ read_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.isacfifo, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ write_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.isacfifo, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return (readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[hscx], offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[hscx], offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[nr], reg)
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[nr], reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+s0box_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+#define MAXCOUNT 20
+ struct IsdnCardState *cs = dev_id;
+ u_char val, stat = 0;
+ int count = 0;
+
+ if (!cs) {
+ printk(KERN_WARNING "Teles: Spurious interrupt!\n");
+ return;
+ }
+ val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA);
+ Start_HSCX:
+ if (val) {
+ hscx_int_main(cs, val);
+ stat |= 1;
+ }
+ val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_ISTA);
+ Start_ISAC:
+ if (val) {
+ isac_interrupt(cs, val);
+ stat |= 2;
+ }
+ count++;
+ val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA);
+ if (val && count < MAXCOUNT) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX IntStat after IntRoutine");
+ goto Start_HSCX;
+ }
+ val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_ISTA);
+ if (val && count < MAXCOUNT) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC IntStat after IntRoutine");
+ goto Start_ISAC;
+ }
+ if (count >= MAXCOUNT)
+ printk(KERN_WARNING "S0Box: more than %d loops in s0box_interrupt\n", count);
+ if (stat & 1) {
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF);
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF);
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0x0);
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0x0);
+ }
+ if (stat & 2) {
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0xFF);
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0x0);
+ }
+}
+
+void
+release_io_s0box(struct IsdnCardState *cs)
+{
+ release_region(cs->hw.teles3.cfg_reg, 8);
+}
+
+static int
+S0Box_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ switch (mt) {
+ case CARD_RESET:
+ break;
+ case CARD_RELEASE:
+ release_io_s0box(cs);
+ break;
+ case CARD_SETIRQ:
+ return(request_irq(cs->irq, &s0box_interrupt,
+ I4L_IRQ_FLAG, "HiSax", cs));
+ case CARD_INIT:
+ inithscxisac(cs, 3);
+ break;
+ case CARD_TEST:
+ break;
+ }
+ return(0);
+}
+
+__initfunc(int
+setup_s0box(struct IsdnCard *card))
+{
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, s0box_revision);
+ printk(KERN_INFO "HiSax: S0Box IO driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_S0BOX)
+ return (0);
+
+ cs->hw.teles3.cfg_reg = card->para[1];
+ cs->hw.teles3.hscx[0] = -0x20;
+ cs->hw.teles3.hscx[1] = 0x0;
+ cs->hw.teles3.isac = 0x20;
+ cs->hw.teles3.isacfifo = cs->hw.teles3.isac + 0x3e;
+ cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e;
+ cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e;
+ cs->irq = card->para[0];
+ if (check_region(cs->hw.teles3.cfg_reg,8)) {
+ printk(KERN_WARNING
+ "HiSax: %s ports %x-%x already in use\n",
+ CardType[cs->typ],
+ cs->hw.teles3.cfg_reg,
+ cs->hw.teles3.cfg_reg + 7);
+ return 0;
+ } else
+ request_region(cs->hw.teles3.cfg_reg, 8, "S0Box parallel I/O");
+ printk(KERN_INFO
+ "HiSax: %s config irq:%d isac:0x%x cfg:0x%x\n",
+ CardType[cs->typ], cs->irq,
+ cs->hw.teles3.isac, cs->hw.teles3.cfg_reg);
+ printk(KERN_INFO
+ "HiSax: hscx A:0x%x hscx B:0x%x\n",
+ cs->hw.teles3.hscx[0], cs->hw.teles3.hscx[1]);
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &S0Box_card_msg;
+ ISACVersion(cs, "S0Box:");
+ if (HscxVersion(cs, "S0Box:")) {
+ printk(KERN_WARNING
+ "S0Box: wrong HSCX versions check IO address\n");
+ release_io_s0box(cs);
+ return (0);
+ }
+ return (1);
+}
diff --git a/drivers/isdn/hisax/telespci.c b/drivers/isdn/hisax/telespci.c
new file mode 100644
index 000000000..88592aa8d
--- /dev/null
+++ b/drivers/isdn/hisax/telespci.c
@@ -0,0 +1,372 @@
+/* $Id: telespci.c,v 2.5 1998/11/15 23:55:28 keil Exp $
+
+ * telespci.c low level stuff for Teles PCI isdn cards
+ *
+ * Author Ton van Rosmalen
+ * Karsten Keil (keil@temic-ech.spacenet.de)
+ *
+ *
+ * $Log: telespci.c,v $
+ * Revision 2.5 1998/11/15 23:55:28 keil
+ * changes from 2.0
+ *
+ * Revision 2.4 1998/10/05 09:38:08 keil
+ * Fix register addressing
+ *
+ * Revision 2.3 1998/05/25 12:58:26 keil
+ * HiSax golden code from certification, Don't use !!!
+ * No leased lines, no X75, but many changes.
+ *
+ * Revision 2.1 1998/04/15 16:38:23 keil
+ * Add S0Box and Teles PCI support
+ *
+ *
+ */
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/string.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+
+extern const char *CardType[];
+
+const char *telespci_revision = "$Revision: 2.5 $";
+
+#define ZORAN_PO_RQ_PEN 0x02000000
+#define ZORAN_PO_WR 0x00800000
+#define ZORAN_PO_GID0 0x00000000
+#define ZORAN_PO_GID1 0x00100000
+#define ZORAN_PO_GREG0 0x00000000
+#define ZORAN_PO_GREG1 0x00010000
+#define ZORAN_PO_DMASK 0xFF
+
+#define WRITE_ADDR_ISAC (ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG0)
+#define READ_DATA_ISAC (ZORAN_PO_GID0 | ZORAN_PO_GREG1)
+#define WRITE_DATA_ISAC (ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG1)
+#define WRITE_ADDR_HSCX (ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG0)
+#define READ_DATA_HSCX (ZORAN_PO_GID1 | ZORAN_PO_GREG1)
+#define WRITE_DATA_HSCX (ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG1)
+
+#define ZORAN_WAIT_NOBUSY do { \
+ portdata = readl(adr + 0x200); \
+ } while (portdata & ZORAN_PO_RQ_PEN)
+
+static inline u_char
+readisac(unsigned int adr, u_char off)
+{
+ register unsigned int portdata;
+
+ ZORAN_WAIT_NOBUSY;
+
+ /* set address for ISAC */
+ writel(WRITE_ADDR_ISAC | off, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+
+ /* read data from ISAC */
+ writel(READ_DATA_ISAC, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ return((u_char)(portdata & ZORAN_PO_DMASK));
+}
+
+static inline void
+writeisac(unsigned int adr, u_char off, u_char data)
+{
+ register unsigned int portdata;
+
+ ZORAN_WAIT_NOBUSY;
+
+ /* set address for ISAC */
+ writel(WRITE_ADDR_ISAC | off, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+
+ /* write data to ISAC */
+ writel(WRITE_DATA_ISAC | data, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+}
+
+static inline u_char
+readhscx(unsigned int adr, int hscx, u_char off)
+{
+ register unsigned int portdata;
+
+ ZORAN_WAIT_NOBUSY;
+ /* set address for HSCX */
+ writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+
+ /* read data from HSCX */
+ writel(READ_DATA_HSCX, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ return ((u_char)(portdata & ZORAN_PO_DMASK));
+}
+
+static inline void
+writehscx(unsigned int adr, int hscx, u_char off, u_char data)
+{
+ register unsigned int portdata;
+
+ ZORAN_WAIT_NOBUSY;
+ /* set address for HSCX */
+ writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+
+ /* write data to HSCX */
+ writel(WRITE_DATA_HSCX | data, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+}
+
+static inline void
+read_fifo_isac(unsigned int adr, u_char * data, int size)
+{
+ register unsigned int portdata;
+ register int i;
+
+ ZORAN_WAIT_NOBUSY;
+ /* read data from ISAC */
+ for (i = 0; i < size; i++) {
+ /* set address for ISAC fifo */
+ writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ writel(READ_DATA_ISAC, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ data[i] = (u_char)(portdata & ZORAN_PO_DMASK);
+ }
+}
+
+static void
+write_fifo_isac(unsigned int adr, u_char * data, int size)
+{
+ register unsigned int portdata;
+ register int i;
+
+ ZORAN_WAIT_NOBUSY;
+ /* write data to ISAC */
+ for (i = 0; i < size; i++) {
+ /* set address for ISAC fifo */
+ writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ writel(WRITE_DATA_ISAC | data[i], adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ }
+}
+
+static inline void
+read_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
+{
+ register unsigned int portdata;
+ register int i;
+
+ ZORAN_WAIT_NOBUSY;
+ /* read data from HSCX */
+ for (i = 0; i < size; i++) {
+ /* set address for HSCX fifo */
+ writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ writel(READ_DATA_HSCX, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ data[i] = (u_char) (portdata & ZORAN_PO_DMASK);
+ }
+}
+
+static inline void
+write_fifo_hscx(unsigned int adr, int hscx, u_char * data, int size)
+{
+ unsigned int portdata;
+ register int i;
+
+ ZORAN_WAIT_NOBUSY;
+ /* write data to HSCX */
+ for (i = 0; i < size; i++) {
+ /* set address for HSCX fifo */
+ writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ writel(WRITE_DATA_HSCX | data[i], adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ udelay(10);
+ }
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (readisac(cs->hw.teles0.membase, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writeisac(cs->hw.teles0.membase, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ read_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ write_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return (readhscx(cs->hw.teles0.membase, hscx, offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ writehscx(cs->hw.teles0.membase, hscx, offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static void
+telespci_interrupt(int intno, void *dev_id, struct pt_regs *regs)
+{
+#define MAXCOUNT 20
+ struct IsdnCardState *cs = dev_id;
+ u_char val, stat = 0;
+
+ if (!cs) {
+ printk(KERN_WARNING "TelesPCI: Spurious interrupt!\n");
+ return;
+ }
+ val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA);
+ if (val) {
+ hscx_int_main(cs, val);
+ stat |= 1;
+ }
+ val = readisac(cs->hw.teles0.membase, ISAC_ISTA);
+ if (val) {
+ isac_interrupt(cs, val);
+ stat |= 2;
+ }
+ /* Clear interrupt register for Zoran PCI controller */
+ writel(0x70000000, cs->hw.teles0.membase + 0x3C);
+
+ if (stat & 1) {
+ writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF);
+ writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF);
+ writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0);
+ writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0);
+ }
+ if (stat & 2) {
+ writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF);
+ writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0);
+ }
+}
+
+void
+release_io_telespci(struct IsdnCardState *cs)
+{
+ iounmap((void *)cs->hw.teles0.membase);
+}
+
+static int
+TelesPCI_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ switch (mt) {
+ case CARD_RESET:
+ return(0);
+ case CARD_RELEASE:
+ release_io_telespci(cs);
+ return(0);
+ case CARD_SETIRQ:
+ return(request_irq(cs->irq, &telespci_interrupt,
+ I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs));
+ case CARD_INIT:
+ inithscxisac(cs, 3);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ }
+ return(0);
+}
+
+static struct pci_dev *dev_tel __initdata = NULL;
+
+__initfunc(int
+setup_telespci(struct IsdnCard *card))
+{
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, telespci_revision);
+ printk(KERN_INFO "HiSax: Teles/PCI driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_TELESPCI)
+ return (0);
+
+#if CONFIG_PCI
+ if (!pci_present()) {
+ printk(KERN_ERR "TelesPCI: no PCI bus present\n");
+ return(0);
+ }
+ if ((dev_tel = pci_find_device (0x11DE, 0x6120, dev_tel))) {
+ cs->irq = dev_tel->irq;
+ if (!cs->irq) {
+ printk(KERN_WARNING "Teles: No IRQ for PCI card found\n");
+ return(0);
+ }
+ cs->hw.teles0.membase = (u_int) ioremap(dev_tel->base_address[0],
+ PAGE_SIZE);
+ printk(KERN_INFO "Found: Zoran, base-address: 0x%lx, irq: 0x%x\n",
+ dev_tel->base_address[0], dev_tel->irq);
+ } else {
+ printk(KERN_WARNING "TelesPCI: No PCI card found\n");
+ return(0);
+ }
+#else
+ printk(KERN_WARNING "HiSax: Teles/PCI and NO_PCI_BIOS\n");
+ printk(KERN_WARNING "HiSax: Teles/PCI unable to config\n");
+ return (0);
+#endif /* CONFIG_PCI */
+
+ /* Initialize Zoran PCI controller */
+ writel(0x00000000, cs->hw.teles0.membase + 0x28);
+ writel(0x01000000, cs->hw.teles0.membase + 0x28);
+ writel(0x01000000, cs->hw.teles0.membase + 0x28);
+ writel(0x7BFFFFFF, cs->hw.teles0.membase + 0x2C);
+ writel(0x70000000, cs->hw.teles0.membase + 0x3C);
+ writel(0x61000000, cs->hw.teles0.membase + 0x40);
+ /* writel(0x00800000, cs->hw.teles0.membase + 0x200); */
+
+ printk(KERN_INFO
+ "HiSax: %s config irq:%d mem:%x\n",
+ CardType[cs->typ], cs->irq,
+ cs->hw.teles0.membase);
+
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &TelesPCI_card_msg;
+ ISACVersion(cs, "TelesPCI:");
+ if (HscxVersion(cs, "TelesPCI:")) {
+ printk(KERN_WARNING
+ "TelesPCI: wrong HSCX versions check IO/MEM addresses\n");
+ release_io_telespci(cs);
+ return (0);
+ }
+ return (1);
+}
diff --git a/drivers/isdn/isdn_bsdcomp.c b/drivers/isdn/isdn_bsdcomp.c
new file mode 100644
index 000000000..92012c8e5
--- /dev/null
+++ b/drivers/isdn/isdn_bsdcomp.c
@@ -0,0 +1,934 @@
+/*
+ * BSD compression module
+ *
+ * Patched version for ISDN syncPPP written 1997/1998 by Michael Hipp
+ * The whole module is now SKB based.
+ *
+ * Compile with:
+ * gcc -O2 -I/usr/src/linux/include -D__KERNEL__ -DMODULE -c isdn_bsdcomp.c
+ */
+
+/*
+ * Original copyright notice:
+ *
+ * Copyright (c) 1985, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James A. Woods, derived from original work by Spencer Thomas
+ * and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef MODULE
+#error This file must be compiled as a module.
+#endif
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/sched.h> /* to get the struct task_struct */
+#include <linux/string.h> /* used in new tty drivers */
+#include <linux/signal.h> /* used in new tty drivers */
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/segment.h>
+#include <asm/byteorder.h>
+#include <asm/types.h>
+
+#include <linux/if.h>
+
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/inet.h>
+#include <linux/ioctl.h>
+
+#include <linux/ppp_defs.h>
+
+#include <linux/isdn.h>
+#include <linux/isdn_ppp.h>
+/* #include <linux/netprotocol.h> */
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/if_arp.h>
+#include <linux/ppp-comp.h>
+
+#include "isdn_ppp.h"
+
+#define BSD_VERSION(x) ((x) >> 5)
+#define BSD_NBITS(x) ((x) & 0x1F)
+
+#define BSD_CURRENT_VERSION 1
+
+#define DEBUG 1
+
+/*
+ * A dictionary for doing BSD compress.
+ */
+
+struct bsd_dict {
+ u32 fcode;
+ u16 codem1; /* output of hash table -1 */
+ u16 cptr; /* map code to hash table entry */
+};
+
+struct bsd_db {
+ int totlen; /* length of this structure */
+ unsigned int hsize; /* size of the hash table */
+ unsigned char hshift; /* used in hash function */
+ unsigned char n_bits; /* current bits/code */
+ unsigned char maxbits; /* maximum bits/code */
+ unsigned char debug; /* non-zero if debug desired */
+ unsigned char unit; /* ppp unit number */
+ u16 seqno; /* sequence # of next packet */
+ unsigned int mru; /* size of receive (decompress) bufr */
+ unsigned int maxmaxcode; /* largest valid code */
+ unsigned int max_ent; /* largest code in use */
+ unsigned int in_count; /* uncompressed bytes, aged */
+ unsigned int bytes_out; /* compressed bytes, aged */
+ unsigned int ratio; /* recent compression ratio */
+ unsigned int checkpoint; /* when to next check the ratio */
+ unsigned int clear_count; /* times dictionary cleared */
+ unsigned int incomp_count; /* incompressible packets */
+ unsigned int incomp_bytes; /* incompressible bytes */
+ unsigned int uncomp_count; /* uncompressed packets */
+ unsigned int uncomp_bytes; /* uncompressed bytes */
+ unsigned int comp_count; /* compressed packets */
+ unsigned int comp_bytes; /* compressed bytes */
+ unsigned short *lens; /* array of lengths of codes */
+ struct bsd_dict *dict; /* dictionary */
+ int xmit;
+};
+
+#define BSD_OVHD 2 /* BSD compress overhead/packet */
+#define MIN_BSD_BITS 9
+#define BSD_INIT_BITS MIN_BSD_BITS
+#define MAX_BSD_BITS 15
+
+/*
+ * the next two codes should not be changed lightly, as they must not
+ * lie within the contiguous general code space.
+ */
+#define CLEAR 256 /* table clear output code */
+#define FIRST 257 /* first free entry */
+#define LAST 255
+
+#define MAXCODE(b) ((1 << (b)) - 1)
+#define BADCODEM1 MAXCODE(MAX_BSD_BITS);
+
+#define BSD_HASH(prefix,suffix,hshift) ((((unsigned long)(suffix))<<(hshift)) \
+ ^ (unsigned long)(prefix))
+#define BSD_KEY(prefix,suffix) ((((unsigned long)(suffix)) << 16) \
+ + (unsigned long)(prefix))
+
+#define CHECK_GAP 10000 /* Ratio check interval */
+
+#define RATIO_SCALE_LOG 8
+#define RATIO_SCALE (1<<RATIO_SCALE_LOG)
+#define RATIO_MAX (0x7fffffff>>RATIO_SCALE_LOG)
+
+/*
+ * clear the dictionary
+ */
+
+static void bsd_clear(struct bsd_db *db)
+{
+ db->clear_count++;
+ db->max_ent = FIRST-1;
+ db->n_bits = BSD_INIT_BITS;
+ db->bytes_out = 0;
+ db->in_count = 0;
+ db->incomp_count = 0;
+ db->ratio = 0;
+ db->checkpoint = CHECK_GAP;
+}
+
+/*
+ * If the dictionary is full, then see if it is time to reset it.
+ *
+ * Compute the compression ratio using fixed-point arithmetic
+ * with 8 fractional bits.
+ *
+ * Since we have an infinite stream instead of a single file,
+ * watch only the local compression ratio.
+ *
+ * Since both peers must reset the dictionary at the same time even in
+ * the absence of CLEAR codes (while packets are incompressible), they
+ * must compute the same ratio.
+ */
+static int bsd_check (struct bsd_db *db) /* 1=output CLEAR */
+{
+ unsigned int new_ratio;
+
+ if (db->in_count >= db->checkpoint)
+ {
+ /* age the ratio by limiting the size of the counts */
+ if (db->in_count >= RATIO_MAX || db->bytes_out >= RATIO_MAX)
+ {
+ db->in_count -= (db->in_count >> 2);
+ db->bytes_out -= (db->bytes_out >> 2);
+ }
+
+ db->checkpoint = db->in_count + CHECK_GAP;
+
+ if (db->max_ent >= db->maxmaxcode)
+ {
+ /* Reset the dictionary only if the ratio is worse,
+ * or if it looks as if it has been poisoned
+ * by incompressible data.
+ *
+ * This does not overflow, because
+ * db->in_count <= RATIO_MAX.
+ */
+
+ new_ratio = db->in_count << RATIO_SCALE_LOG;
+ if (db->bytes_out != 0)
+ {
+ new_ratio /= db->bytes_out;
+ }
+
+ if (new_ratio < db->ratio || new_ratio < 1 * RATIO_SCALE)
+ {
+ bsd_clear (db);
+ return 1;
+ }
+ db->ratio = new_ratio;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Return statistics.
+ */
+
+static void bsd_stats (void *state, struct compstat *stats)
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+
+ stats->unc_bytes = db->uncomp_bytes;
+ stats->unc_packets = db->uncomp_count;
+ stats->comp_bytes = db->comp_bytes;
+ stats->comp_packets = db->comp_count;
+ stats->inc_bytes = db->incomp_bytes;
+ stats->inc_packets = db->incomp_count;
+ stats->in_count = db->in_count;
+ stats->bytes_out = db->bytes_out;
+}
+
+/*
+ * Reset state, as on a CCP ResetReq.
+ */
+static void bsd_reset (void *state,unsigned char code, unsigned char id,
+ unsigned char *data, unsigned len,
+ struct isdn_ppp_resetparams *rsparm)
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+
+ bsd_clear(db);
+ db->seqno = 0;
+ db->clear_count = 0;
+}
+
+/*
+ * Release the compression structure
+ */
+static void bsd_free (void *state)
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+
+ if (db) {
+ /*
+ * Release the dictionary
+ */
+ if (db->dict) {
+ vfree (db->dict);
+ db->dict = NULL;
+ }
+
+ /*
+ * Release the string buffer
+ */
+ if (db->lens) {
+ vfree (db->lens);
+ db->lens = NULL;
+ }
+
+ /*
+ * Finally release the structure itself.
+ */
+ kfree (db);
+ MOD_DEC_USE_COUNT;
+ }
+}
+
+
+/*
+ * Allocate space for a (de) compressor.
+ */
+static void *bsd_alloc (struct isdn_ppp_comp_data *data)
+{
+ int bits;
+ unsigned int hsize, hshift, maxmaxcode;
+ struct bsd_db *db;
+ int decomp;
+
+ static unsigned int htab[][2] = {
+ { 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } ,
+ { 9001 , 5 } , { 18013 , 6 } , { 35023 , 7 } , { 69001 , 8 }
+ };
+
+ if (data->optlen != 1 || data->num != CI_BSD_COMPRESS
+ || BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION)
+ return NULL;
+
+ bits = BSD_NBITS(data->options[0]);
+
+ if(bits < 9 || bits > 15)
+ return NULL;
+
+ hsize = htab[bits-9][0];
+ hshift = htab[bits-9][1];
+
+ /*
+ * Allocate the main control structure for this instance.
+ */
+ maxmaxcode = MAXCODE(bits);
+ db = (struct bsd_db *) kmalloc (sizeof (struct bsd_db),GFP_KERNEL);
+ if (!db)
+ return NULL;
+
+ memset (db, 0, sizeof(struct bsd_db));
+
+ db->xmit = data->flags & IPPP_COMP_FLAG_XMIT;
+ decomp = db->xmit ? 0 : 1;
+
+ /*
+ * Allocate space for the dictionary. This may be more than one page in
+ * length.
+ */
+ db->dict = (struct bsd_dict *) vmalloc (hsize * sizeof (struct bsd_dict));
+ if (!db->dict) {
+ bsd_free (db);
+ return NULL;
+ }
+
+ MOD_INC_USE_COUNT;
+
+ /*
+ * If this is the compression buffer then there is no length data.
+ * For decompression, the length information is needed as well.
+ */
+ if (!decomp)
+ db->lens = NULL;
+ else {
+ db->lens = (unsigned short *) vmalloc ((maxmaxcode + 1) *
+ sizeof (db->lens[0]));
+ if (!db->lens) {
+ bsd_free (db); /* calls MOD_DEC_USE_COUNT; */
+ return (NULL);
+ }
+ }
+
+ /*
+ * Initialize the data information for the compression code
+ */
+ db->totlen = sizeof (struct bsd_db) + (sizeof (struct bsd_dict) * hsize);
+ db->hsize = hsize;
+ db->hshift = hshift;
+ db->maxmaxcode = maxmaxcode;
+ db->maxbits = bits;
+
+ return (void *) db;
+}
+
+/*
+ * Initialize the database.
+ */
+static int bsd_init (void *state, struct isdn_ppp_comp_data *data, int unit, int debug)
+{
+ struct bsd_db *db = state;
+ int indx;
+ int decomp;
+
+ if(!state || !data) {
+ printk(KERN_ERR "isdn_bsd_init: [%d] ERR, state %lx data %lx\n",unit,(long)state,(long)data);
+ return 0;
+ }
+
+ decomp = db->xmit ? 0 : 1;
+
+ if (data->optlen != 1 || data->num != CI_BSD_COMPRESS
+ || (BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION)
+ || (BSD_NBITS(data->options[0]) != db->maxbits)
+ || (decomp && db->lens == NULL)) {
+ printk(KERN_ERR "isdn_bsd: %d %d %d %d %lx\n",data->optlen,data->num,data->options[0],decomp,(unsigned long)db->lens);
+ return 0;
+ }
+
+ if (decomp)
+ for(indx=LAST;indx>=0;indx--)
+ db->lens[indx] = 1;
+
+ indx = db->hsize;
+ while (indx-- != 0) {
+ db->dict[indx].codem1 = BADCODEM1;
+ db->dict[indx].cptr = 0;
+ }
+
+ db->unit = unit;
+ db->mru = 0;
+
+ db->debug = 1;
+
+ bsd_reset(db,0,0,NULL,0,NULL);
+
+ return 1;
+}
+
+/*
+ * Obtain pointers to the various structures in the compression tables
+ */
+
+#define dict_ptrx(p,idx) &(p->dict[idx])
+#define lens_ptrx(p,idx) &(p->lens[idx])
+
+#ifdef DEBUG
+static unsigned short *lens_ptr(struct bsd_db *db, int idx)
+{
+ if ((unsigned int) idx > (unsigned int) db->maxmaxcode) {
+ printk (KERN_DEBUG "<9>ppp: lens_ptr(%d) > max\n", idx);
+ idx = 0;
+ }
+ return lens_ptrx (db, idx);
+}
+
+static struct bsd_dict *dict_ptr(struct bsd_db *db, int idx)
+{
+ if ((unsigned int) idx >= (unsigned int) db->hsize) {
+ printk (KERN_DEBUG "<9>ppp: dict_ptr(%d) > max\n", idx);
+ idx = 0;
+ }
+ return dict_ptrx (db, idx);
+}
+
+#else
+#define lens_ptr(db,idx) lens_ptrx(db,idx)
+#define dict_ptr(db,idx) dict_ptrx(db,idx)
+#endif
+
+/*
+ * compress a packet
+ */
+static int bsd_compress (void *state, struct sk_buff *skb_in, struct sk_buff *skb_out,int proto)
+{
+ struct bsd_db *db;
+ int hshift;
+ unsigned int max_ent;
+ unsigned int n_bits;
+ unsigned int bitno;
+ unsigned long accm;
+ int ent;
+ unsigned long fcode;
+ struct bsd_dict *dictp;
+ unsigned char c;
+ int hval,disp,ilen,mxcode;
+ unsigned char *rptr = skb_in->data;
+ int isize = skb_in->len;
+
+#define OUTPUT(ent) \
+ { \
+ bitno -= n_bits; \
+ accm |= ((ent) << bitno); \
+ do { \
+ if(skb_out && skb_tailroom(skb_out) > 0) \
+ *(skb_put(skb_out,1)) = (unsigned char) (accm>>24); \
+ accm <<= 8; \
+ bitno += 8; \
+ } while (bitno <= 24); \
+ }
+
+ /*
+ * If the protocol is not in the range we're interested in,
+ * just return without compressing the packet. If it is,
+ * the protocol becomes the first byte to compress.
+ */
+ printk(KERN_DEBUG "bsd_compress called with %x\n",proto);
+
+ ent = proto;
+ if (proto < 0x21 || proto > 0xf9 || !(proto & 0x1) )
+ return 0;
+
+ db = (struct bsd_db *) state;
+ hshift = db->hshift;
+ max_ent = db->max_ent;
+ n_bits = db->n_bits;
+ bitno = 32;
+ accm = 0;
+ mxcode = MAXCODE (n_bits);
+
+ /* This is the PPP header information */
+ if(skb_out && skb_tailroom(skb_out) >= 2) {
+ char *v = skb_put(skb_out,2);
+ /* we only push our own data on the header,
+ AC,PC and protos is pushed by caller */
+ v[0] = db->seqno >> 8;
+ v[1] = db->seqno;
+ }
+
+ ilen = ++isize; /* This is off by one, but that is what is in draft! */
+
+ while (--ilen > 0) {
+ c = *rptr++;
+ fcode = BSD_KEY (ent, c);
+ hval = BSD_HASH (ent, c, hshift);
+ dictp = dict_ptr (db, hval);
+
+ /* Validate and then check the entry. */
+ if (dictp->codem1 >= max_ent)
+ goto nomatch;
+
+ if (dictp->fcode == fcode) {
+ ent = dictp->codem1 + 1;
+ continue; /* found (prefix,suffix) */
+ }
+
+ /* continue probing until a match or invalid entry */
+ disp = (hval == 0) ? 1 : hval;
+
+ do {
+ hval += disp;
+ if (hval >= db->hsize)
+ hval -= db->hsize;
+ dictp = dict_ptr (db, hval);
+ if (dictp->codem1 >= max_ent)
+ goto nomatch;
+ } while (dictp->fcode != fcode);
+
+ ent = dictp->codem1 + 1; /* finally found (prefix,suffix) */
+ continue;
+
+nomatch:
+ OUTPUT(ent); /* output the prefix */
+
+ /* code -> hashtable */
+ if (max_ent < db->maxmaxcode) {
+ struct bsd_dict *dictp2;
+ struct bsd_dict *dictp3;
+ int indx;
+
+ /* expand code size if needed */
+ if (max_ent >= mxcode) {
+ db->n_bits = ++n_bits;
+ mxcode = MAXCODE (n_bits);
+ }
+
+ /*
+ * Invalidate old hash table entry using
+ * this code, and then take it over.
+ */
+ dictp2 = dict_ptr (db, max_ent + 1);
+ indx = dictp2->cptr;
+ dictp3 = dict_ptr (db, indx);
+
+ if (dictp3->codem1 == max_ent)
+ dictp3->codem1 = BADCODEM1;
+
+ dictp2->cptr = hval;
+ dictp->codem1 = max_ent;
+ dictp->fcode = fcode;
+ db->max_ent = ++max_ent;
+
+ if (db->lens) {
+ unsigned short *len1 = lens_ptr (db, max_ent);
+ unsigned short *len2 = lens_ptr (db, ent);
+ *len1 = *len2 + 1;
+ }
+ }
+ ent = c;
+ }
+
+ OUTPUT(ent); /* output the last code */
+
+ if(skb_out)
+ db->bytes_out += skb_out->len; /* Do not count bytes from here */
+ db->uncomp_bytes += isize;
+ db->in_count += isize;
+ ++db->uncomp_count;
+ ++db->seqno;
+
+ if (bitno < 32)
+ ++db->bytes_out; /* must be set before calling bsd_check */
+
+ /*
+ * Generate the clear command if needed
+ */
+
+ if (bsd_check(db))
+ OUTPUT (CLEAR);
+
+ /*
+ * Pad dribble bits of last code with ones.
+ * Do not emit a completely useless byte of ones.
+ */
+ if (bitno < 32 && skb_out && skb_tailroom(skb_out) > 0)
+ *(skb_put(skb_out,1)) = (unsigned char) ((accm | (0xff << (bitno-8))) >> 24);
+
+ /*
+ * Increase code size if we would have without the packet
+ * boundary because the decompressor will do so.
+ */
+ if (max_ent >= mxcode && max_ent < db->maxmaxcode)
+ db->n_bits++;
+
+ /* If output length is too large then this is an incompressible frame. */
+ if (!skb_out || (skb_out && skb_out->len >= skb_in->len) ) {
+ ++db->incomp_count;
+ db->incomp_bytes += isize;
+ return 0;
+ }
+
+ /* Count the number of compressed frames */
+ ++db->comp_count;
+ db->comp_bytes += skb_out->len;
+ return skb_out->len;
+
+#undef OUTPUT
+}
+
+/*
+ * Update the "BSD Compress" dictionary on the receiver for
+ * incompressible data by pretending to compress the incoming data.
+ */
+static void bsd_incomp (void *state, struct sk_buff *skb_in,int proto)
+{
+ bsd_compress (state, skb_in, NULL, proto);
+}
+
+/*
+ * Decompress "BSD Compress".
+ */
+static int bsd_decompress (void *state, struct sk_buff *skb_in, struct sk_buff *skb_out,
+ struct isdn_ppp_resetparams *rsparm)
+{
+ struct bsd_db *db;
+ unsigned int max_ent;
+ unsigned long accm;
+ unsigned int bitno; /* 1st valid bit in accm */
+ unsigned int n_bits;
+ unsigned int tgtbitno; /* bitno when we have a code */
+ struct bsd_dict *dictp;
+ int seq;
+ unsigned int incode;
+ unsigned int oldcode;
+ unsigned int finchar;
+ unsigned char *p,*ibuf;
+ int ilen;
+ int codelen;
+ int extra;
+
+ db = (struct bsd_db *) state;
+ max_ent = db->max_ent;
+ accm = 0;
+ bitno = 32; /* 1st valid bit in accm */
+ n_bits = db->n_bits;
+ tgtbitno = 32 - n_bits; /* bitno when we have a code */
+
+ printk(KERN_DEBUG "bsd_decompress called\n");
+
+ if(!skb_in || !skb_out) {
+ printk(KERN_ERR "bsd_decompress called with NULL parameter\n");
+ return DECOMP_ERROR;
+ }
+
+ /*
+ * Get the sequence number.
+ */
+ if( (p = skb_pull(skb_in,2)) == NULL) {
+ return DECOMP_ERROR;
+ }
+ p-=2;
+ seq = (p[0] << 8) + p[1];
+ ilen = skb_in->len;
+ ibuf = skb_in->data;
+
+ /*
+ * Check the sequence number and give up if it differs from
+ * the value we're expecting.
+ */
+ if (seq != db->seqno) {
+ if (db->debug) {
+ printk(KERN_DEBUG "bsd_decomp%d: bad sequence # %d, expected %d\n",
+ db->unit, seq, db->seqno - 1);
+ }
+ return DECOMP_ERROR;
+ }
+
+ ++db->seqno;
+ db->bytes_out += ilen;
+
+ if(skb_tailroom(skb_out) > 0)
+ *(skb_put(skb_out,1)) = 0;
+ else
+ return DECOMP_ERR_NOMEM;
+
+ oldcode = CLEAR;
+
+ /*
+ * Keep the checkpoint correctly so that incompressible packets
+ * clear the dictionary at the proper times.
+ */
+
+ for (;;) {
+ if (ilen-- <= 0) {
+ db->in_count += (skb_out->len - 1); /* don't count the header */
+ break;
+ }
+
+ /*
+ * Accumulate bytes until we have a complete code.
+ * Then get the next code, relying on the 32-bit,
+ * unsigned accm to mask the result.
+ */
+
+ bitno -= 8;
+ accm |= *ibuf++ << bitno;
+ if (tgtbitno < bitno)
+ continue;
+
+ incode = accm >> tgtbitno;
+ accm <<= n_bits;
+ bitno += n_bits;
+
+ /*
+ * The dictionary must only be cleared at the end of a packet.
+ */
+
+ if (incode == CLEAR) {
+ if (ilen > 0) {
+ if (db->debug)
+ printk(KERN_DEBUG "bsd_decomp%d: bad CLEAR\n", db->unit);
+ return DECOMP_FATALERROR; /* probably a bug */
+ }
+ bsd_clear(db);
+ break;
+ }
+
+ if ((incode > max_ent + 2) || (incode > db->maxmaxcode)
+ || (incode > max_ent && oldcode == CLEAR)) {
+ if (db->debug) {
+ printk(KERN_DEBUG "bsd_decomp%d: bad code 0x%x oldcode=0x%x ",
+ db->unit, incode, oldcode);
+ printk(KERN_DEBUG "max_ent=0x%x skb->Len=%d seqno=%d\n",
+ max_ent, skb_out->len, db->seqno);
+ }
+ return DECOMP_FATALERROR; /* probably a bug */
+ }
+
+ /* Special case for KwKwK string. */
+ if (incode > max_ent) {
+ finchar = oldcode;
+ extra = 1;
+ } else {
+ finchar = incode;
+ extra = 0;
+ }
+
+ codelen = *(lens_ptr (db, finchar));
+ if( skb_tailroom(skb_out) < codelen + extra) {
+ if (db->debug) {
+ printk(KERN_DEBUG "bsd_decomp%d: ran out of mru\n", db->unit);
+#ifdef DEBUG
+ printk(KERN_DEBUG " len=%d, finchar=0x%x, codelen=%d,skblen=%d\n",
+ ilen, finchar, codelen, skb_out->len);
+#endif
+ }
+ return DECOMP_FATALERROR;
+ }
+
+ /*
+ * Decode this code and install it in the decompressed buffer.
+ */
+
+ p = skb_put(skb_out,codelen);
+ p += codelen;
+ while (finchar > LAST) {
+ struct bsd_dict *dictp2 = dict_ptr (db, finchar);
+
+ dictp = dict_ptr (db, dictp2->cptr);
+
+#ifdef DEBUG
+ if (--codelen <= 0 || dictp->codem1 != finchar-1) {
+ if (codelen <= 0) {
+ printk(KERN_ERR "bsd_decomp%d: fell off end of chain ", db->unit);
+ printk(KERN_ERR "0x%x at 0x%x by 0x%x, max_ent=0x%x\n", incode, finchar, dictp2->cptr, max_ent);
+ } else {
+ if (dictp->codem1 != finchar-1) {
+ printk(KERN_ERR "bsd_decomp%d: bad code chain 0x%x finchar=0x%x ",db->unit, incode, finchar);
+ printk(KERN_ERR "oldcode=0x%x cptr=0x%x codem1=0x%x\n", oldcode, dictp2->cptr, dictp->codem1);
+ }
+ }
+ return DECOMP_FATALERROR;
+ }
+#endif
+
+ {
+ u32 fcode = dictp->fcode;
+ *--p = (fcode >> 16) & 0xff;
+ finchar = fcode & 0xffff;
+ }
+ }
+ *--p = finchar;
+
+#ifdef DEBUG
+ if (--codelen != 0)
+ printk(KERN_ERR "bsd_decomp%d: short by %d after code 0x%x, max_ent=0x%x\n", db->unit, codelen, incode, max_ent);
+#endif
+
+ if (extra) /* the KwKwK case again */
+ *(skb_put(skb_out,1)) = finchar;
+
+ /*
+ * If not first code in a packet, and
+ * if not out of code space, then allocate a new code.
+ *
+ * Keep the hash table correct so it can be used
+ * with uncompressed packets.
+ */
+ if (oldcode != CLEAR && max_ent < db->maxmaxcode) {
+ struct bsd_dict *dictp2, *dictp3;
+ u16 *lens1, *lens2;
+ unsigned long fcode;
+ int hval, disp, indx;
+
+ fcode = BSD_KEY(oldcode,finchar);
+ hval = BSD_HASH(oldcode,finchar,db->hshift);
+ dictp = dict_ptr (db, hval);
+
+ /* look for a free hash table entry */
+ if (dictp->codem1 < max_ent) {
+ disp = (hval == 0) ? 1 : hval;
+ do {
+ hval += disp;
+ if (hval >= db->hsize)
+ hval -= db->hsize;
+ dictp = dict_ptr (db, hval);
+ } while (dictp->codem1 < max_ent);
+ }
+
+ /*
+ * Invalidate previous hash table entry
+ * assigned this code, and then take it over
+ */
+
+ dictp2 = dict_ptr (db, max_ent + 1);
+ indx = dictp2->cptr;
+ dictp3 = dict_ptr (db, indx);
+
+ if (dictp3->codem1 == max_ent)
+ dictp3->codem1 = BADCODEM1;
+
+ dictp2->cptr = hval;
+ dictp->codem1 = max_ent;
+ dictp->fcode = fcode;
+ db->max_ent = ++max_ent;
+
+ /* Update the length of this string. */
+ lens1 = lens_ptr (db, max_ent);
+ lens2 = lens_ptr (db, oldcode);
+ *lens1 = *lens2 + 1;
+
+ /* Expand code size if needed. */
+ if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode) {
+ db->n_bits = ++n_bits;
+ tgtbitno = 32-n_bits;
+ }
+ }
+ oldcode = incode;
+ }
+
+ ++db->comp_count;
+ ++db->uncomp_count;
+ db->comp_bytes += skb_in->len - BSD_OVHD;
+ db->uncomp_bytes += skb_out->len;
+
+ if (bsd_check(db)) {
+ if (db->debug)
+ printk(KERN_DEBUG "bsd_decomp%d: peer should have cleared dictionary on %d\n",
+ db->unit, db->seqno - 1);
+ }
+ return skb_out->len;
+}
+
+/*************************************************************
+ * Table of addresses for the BSD compression module
+ *************************************************************/
+
+static struct isdn_ppp_compressor ippp_bsd_compress = {
+ NULL,NULL, /* prev,next: overwritten by isdn_ppp */
+ CI_BSD_COMPRESS, /* compress_proto */
+ bsd_alloc, /* alloc */
+ bsd_free, /* free */
+ bsd_init, /* init */
+ bsd_reset, /* reset */
+ bsd_compress, /* compress */
+ bsd_decompress, /* decompress */
+ bsd_incomp, /* incomp */
+ bsd_stats /* comp_stat */
+};
+
+/*************************************************************
+ * Module support routines
+ *************************************************************/
+
+int init_module(void)
+{
+ int answer = isdn_ppp_register_compressor (&ippp_bsd_compress);
+ if (answer == 0)
+ printk (KERN_INFO "PPP BSD Compression module registered\n");
+ return answer;
+}
+
+void cleanup_module(void)
+{
+ isdn_ppp_unregister_compressor (&ippp_bsd_compress);
+}
diff --git a/drivers/isdn/isdn_budget.c b/drivers/isdn/isdn_budget.c
new file mode 100644
index 000000000..985c97ba8
--- /dev/null
+++ b/drivers/isdn/isdn_budget.c
@@ -0,0 +1,206 @@
+/* $Id: isdn_budget.c,v 1.3 1998/10/23 10:18:39 paul Exp $
+ *
+ * Linux ISDN subsystem, budget-accounting for network interfaces.
+ *
+ * Copyright 1997 by Christian Lademann <cal@zls.de>
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Log: isdn_budget.c,v $
+ * Revision 1.3 1998/10/23 10:18:39 paul
+ * Implementation of "dialmode" (successor of "status")
+ * You also need current isdnctrl for this!
+ *
+ * Revision 1.2 1998/03/07 23:17:30 fritz
+ * Added RCS keywords
+ * Bugfix: Did not compile without isdn_dumppkt beeing enabled.
+ *
+ */
+
+/*
+30.06.97:cal:angelegt
+04.11.97:cal:budget.period: int --> time_t
+*/
+
+#include <linux/config.h>
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/isdn.h>
+#include "isdn_common.h"
+#include "isdn_net.h"
+
+#ifdef CONFIG_ISDN_BUDGET
+
+#define VERBOSE_PRINTK(v, l, p...) { \
+ if(dev->net_verbose >= (v)) { \
+ printk(l ## p); \
+ } else { ; } \
+}
+
+
+int
+isdn_net_budget(int type, struct device *ndev) {
+ isdn_net_local *lp = (isdn_net_local *)ndev->priv;
+ int i, ret = 0;
+
+ switch(type) {
+ case ISDN_BUDGET_INIT:
+ for(i = 0; i < ISDN_BUDGET_NUM_BUDGET; i++) {
+ lp->budget [i] .amount = -1;
+ lp->budget [i] .used = 0;
+ lp->budget [i] .period = (time_t)0;
+ lp->budget [i] .period_started = (time_t)0;
+ lp->budget [i] .last_check = CURRENT_TIME;
+ lp->budget [i] .notified = 0;
+ }
+
+ return(0);
+ break;
+
+ case ISDN_BUDGET_CHECK_DIAL:
+ case ISDN_BUDGET_CHECK_CHARGE:
+ case ISDN_BUDGET_CHECK_ONLINE:
+ ret = 0;
+
+ for(i = 0; i < ISDN_BUDGET_NUM_BUDGET; i++) {
+ if(lp->budget [i] .amount < 0)
+ continue;
+
+ if(lp->budget [i] .period_started + lp->budget [i] .period < CURRENT_TIME) {
+ lp->budget [i] .used = 0;
+ lp->budget [i] .period_started = CURRENT_TIME;
+ lp->budget [i] .notified = 0;
+ }
+
+ if(lp->budget [i] .used >= lp->budget [i] .amount)
+ ret |= (1 << i);
+ }
+
+ switch(type) {
+ case ISDN_BUDGET_CHECK_DIAL:
+ if(! ret) {
+ lp->budget [ISDN_BUDGET_DIAL] .used++;
+ lp->budget [ISDN_BUDGET_DIAL] .last_check = CURRENT_TIME;
+ }
+ break;
+
+ case ISDN_BUDGET_CHECK_CHARGE:
+ lp->budget [ISDN_BUDGET_CHARGE] .used++;
+ lp->budget [ISDN_BUDGET_CHARGE] .last_check = CURRENT_TIME;
+ break;
+
+ case ISDN_BUDGET_CHECK_ONLINE:
+ if(lp->budget [ISDN_BUDGET_ONLINE] .last_check) {
+ lp->budget [ISDN_BUDGET_ONLINE] .used += (CURRENT_TIME - lp->budget [ISDN_BUDGET_ONLINE] .last_check);
+ }
+
+ lp->budget [ISDN_BUDGET_ONLINE] .last_check = CURRENT_TIME;
+ break;
+ }
+
+/*
+ if(ret)
+ lp->flags |= ISDN_NET_DM_OFF;
+*/
+ for(i = 0; i < ISDN_BUDGET_NUM_BUDGET; i++) {
+ if(ret & (1 << i) && ! lp->budget [i] .notified) {
+ switch(i) {
+ case ISDN_BUDGET_DIAL:
+ printk(KERN_WARNING "isdn_budget: dial budget used up.\n");
+ break;
+
+ case ISDN_BUDGET_CHARGE:
+ printk(KERN_WARNING "isdn_budget: charge budget used up.\n");
+ break;
+
+ case ISDN_BUDGET_ONLINE:
+ printk(KERN_WARNING "isdn_budget: online budget used up.\n");
+ break;
+
+ default:
+ printk(KERN_WARNING "isdn_budget: budget #%d used up.\n", i);
+ break;
+ }
+
+ lp->budget [i] .notified = 1;
+ }
+ }
+
+ return(ret);
+ break;
+
+ case ISDN_BUDGET_START_ONLINE:
+ lp->budget [ISDN_BUDGET_ONLINE] .last_check = CURRENT_TIME;
+ return(0);
+
+ break;
+ }
+
+ return(-1);
+}
+
+
+int
+isdn_budget_ioctl(isdn_ioctl_budget *iocmd) {
+ isdn_net_dev *p = isdn_net_findif(iocmd->name);
+
+ if(p) {
+ switch(iocmd->command) {
+ case ISDN_BUDGET_SET_BUDGET:
+ if(! suser())
+ return(-EPERM);
+
+ if(iocmd->budget < 0 || iocmd->budget > ISDN_BUDGET_NUM_BUDGET)
+ return(-EINVAL);
+
+ if(iocmd->amount < 0)
+ iocmd->amount = -1;
+
+ p->local->budget [iocmd->budget] .amount = iocmd->amount;
+ p->local->budget [iocmd->budget] .period = iocmd->period;
+
+ if(iocmd->used <= 0)
+ p->local->budget [iocmd->budget] .used = 0;
+ else
+ p->local->budget [iocmd->budget] .used = iocmd->used;
+
+ if(iocmd->period_started == (time_t)0)
+ p->local->budget [iocmd->budget] .period_started = CURRENT_TIME;
+ else
+ p->local->budget [iocmd->budget] .period_started = iocmd->period_started;
+
+ return(0);
+ break;
+
+ case ISDN_BUDGET_GET_BUDGET:
+ if(iocmd->budget < 0 || iocmd->budget > ISDN_BUDGET_NUM_BUDGET)
+ return(-EINVAL);
+
+ iocmd->amount = p->local->budget [iocmd->budget] .amount;
+ iocmd->used = p->local->budget [iocmd->budget] .used;
+ iocmd->period = p->local->budget [iocmd->budget] .period;
+ iocmd->period_started = p->local->budget [iocmd->budget] .period_started;
+
+ return(0);
+ break;
+
+ default:
+ return(-EINVAL);
+ break;
+ }
+ }
+ return(-ENODEV);
+}
+#endif
diff --git a/drivers/net/cycx_drv.c b/drivers/net/cycx_drv.c
new file mode 100644
index 000000000..e75552e48
--- /dev/null
+++ b/drivers/net/cycx_drv.c
@@ -0,0 +1,663 @@
+/*
+* cycx_drv.c cycx Support Module.
+*
+* This module is a library of common hardware-specific
+* functions used by all Cyclades sync and some async (8x & 16x)
+* drivers.
+*
+* Copyright: (c) 1998, 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+*
+* Author: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+*
+* Based on sdladrv.c by Gene Kozin <genek@compuserve.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.
+* ============================================================================
+* 1999/05/28 acme cycx_intack & cycx_intde gone for good
+* 1999/05/18 acme lots of unlogged work, submitting to Linus...
+* 1999/01/03 acme more judicious use of data types
+* 1999/01/03 acme judicious use of data types :>
+* cycx_inten trying to reset pending interrupts
+* from cyclom 2x - I think this isn't the way to
+* go, but for now...
+* 1999/01/02 acme cycx_intack ok, I think there's nothing to do
+* to ack an int in cycx_drv.c, only handle it in
+* cyx_isr (or in the other protocols: cyp_isr,
+* cyf_isr, when they get implemented.
+* Dec 31, 1998 Arnaldo cycx_data_boot & cycx_code_boot fixed, crossing
+* fingers to see x25_configure in cycx_x25.c
+* work... :)
+* Dec 26, 1998 Arnaldo load implementation fixed, seems to work! :)
+* cycx_2x_dpmbase_options with all the possible
+* DPM addresses (20).
+* cycx_intr implemented (test this!)
+* general code cleanup
+* Dec 8, 1998 Ivan Passos Cyclom-2X firmware load implementation.
+* Aug 8, 1998 Arnaldo Initial version.
+*/
+
+#include <linux/config.h>
+#ifdef MODULE
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+#include <linux/module.h>
+#else
+#define EXPORT_SYMBOL(function)
+#endif
+#include <linux/kernel.h> /* printk(), and other useful stuff */
+#include <linux/stddef.h> /* offsetof(), etc. */
+#include <linux/errno.h> /* return codes */
+#include <linux/sched.h> /* for jiffies, HZ, etc. */
+#include <linux/cycx_drv.h> /* API definitions */
+#include <linux/cycx_cfm.h> /* CYCX firmware module definitions */
+#include <linux/delay.h> /* udelay */
+#include <asm/io.h> /* for inb(), outb(), etc. */
+
+#define MOD_VERSION 0
+#define MOD_RELEASE 1
+
+#ifdef MODULE
+MODULE_AUTHOR("Arnaldo Carvalho de Melo");
+MODULE_DESCRIPTION("Cyclades Sync Cards Driver.");
+#endif
+
+/* Function Prototypes */
+/* Module entry points. These are called by the OS and must be public. */
+int init_module (void);
+void cleanup_module (void);
+
+/* Hardware-specific functions */
+static int cycx_detect (cycxhw_t *hw);
+static int cycx_load (cycxhw_t *hw, cfm_t *cfm, u32 len);
+static int cycx_init (cycxhw_t *hw);
+static int cycx_reset (cycxhw_t *hw);
+static void cycx_bootcfg (cycxhw_t *hw);
+
+static int init_cycx_2x (cycxhw_t *hw);
+static int reset_cycx_2x (u32 addr);
+static int detect_cycx_2x (u32 addr);
+
+/* Miscellaneous functions */
+static void delay_cycx (int sec);
+static int get_option_index (u32 *optlist, u32 optval);
+static u16 checksum (u8 *buf, u32 len);
+
+#define wait_cyc(addr) cycx_exec(addr + CMD_OFFSET)
+
+/* Global Data
+ * Note: All data must be explicitly initialized!!! */
+
+/* private data */
+static char modname[] = "cycx_drv";
+static char fullname[] = "Cyclom X Support Module";
+static char copyright[] = "(c) 1998, 1999 Arnaldo Carvalho de Melo";
+
+/* Hardware configuration options.
+ * These are arrays of configuration options used by verification routines.
+ * The first element of each array is its size (i.e. number of options).
+ */
+static u32 cycx_2x_dpmbase_options[] =
+{
+ 20,
+ 0xA0000, 0xA4000, 0xA8000, 0xAC000, 0xB0000, 0xB4000, 0xB8000,
+ 0xBC000, 0xC0000, 0xC4000, 0xC8000, 0xCC000, 0xD0000, 0xD4000,
+ 0xD8000, 0xDC000, 0xE0000, 0xE4000, 0xE8000, 0xEC000
+};
+
+static u32 cycx_2x_irq_options[] = { 7, 3, 5, 9, 10, 11, 12, 15 };
+
+/* Kernel Loadable Module Entry Points */
+/* Module 'insert' entry point.
+ * o print announcement
+ * o initialize static data
+ *
+ * Return: 0 Ok
+ * < 0 error.
+ * Context: process */
+#ifdef MODULE
+int init_module (void)
+{
+ printk(KERN_INFO "%s v%u.%u %s\n",
+ fullname, MOD_VERSION, MOD_RELEASE, copyright);
+ printk(KERN_INFO "version=0x%X\n", LINUX_VERSION_CODE);
+ return 0;
+}
+/* Module 'remove' entry point.
+ * o release all remaining system resources */
+void cleanup_module (void)
+{
+}
+#endif
+/* Kernel APIs */
+/* Set up adapter.
+ * o detect adapter type
+ * o verify hardware configuration options
+ * o check for hardware conflicts
+ * o set up adapter shared memory
+ * o test adapter memory
+ * o load firmware
+ * Return: 0 ok.
+ * < 0 error */
+EXPORT_SYMBOL(cycx_setup);
+int cycx_setup (cycxhw_t *hw, void *cfm, u32 len)
+{
+ u32 *irq_opt = NULL; /* IRQ options */
+ u32 *dpmbase_opt = NULL;/* DPM window base options */
+ int err = 0;
+
+ if (cycx_detect(hw)) {
+ printk(KERN_ERR "%s: adapter Cyclom %uX not found at "
+ "address 0x%lX!\n",
+ modname, hw->type, (unsigned long) hw->dpmbase);
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "%s: found Cyclom %uX card at address 0x%lx.\n",
+ modname, hw->type, (unsigned long) hw->dpmbase);
+
+ switch (hw->type) {
+ case CYCX_2X:
+ irq_opt = cycx_2x_irq_options;
+ dpmbase_opt = cycx_2x_dpmbase_options;
+ break;
+ default:
+ printk(KERN_ERR "%s: unknown card.\n", modname);
+ return -EINVAL;
+ }
+
+ /* Verify IRQ configuration options */
+ if (!get_option_index(irq_opt, hw->irq)) {
+ printk (KERN_ERR "%s: IRQ %d is illegal!\n", modname, hw->irq);
+ return -EINVAL;
+ }
+
+ /* Setup adapter dual-port memory window and test memory */
+ if (!hw->dpmbase) {
+ printk(KERN_ERR "%s: you must specify the dpm address!\n",
+ modname);
+ return -EINVAL;
+ }
+ else if (!get_option_index(dpmbase_opt, hw->dpmbase)) {
+ printk(KERN_ERR "%s: memory address 0x%lX is illegal!\n",
+ modname, (unsigned long) hw->dpmbase);
+ return -EINVAL;
+ }
+
+ hw->dpmsize = CYCX_WINDOWSIZE;
+ /* FIXME! Is this the only amount ever available? */
+ hw->memory = 0x40000;
+
+ cycx_init(hw);
+
+ printk(KERN_INFO "%s: dual-port memory window is set at 0x%lX.\n",
+ modname, (unsigned long) hw->dpmbase);
+ printk(KERN_INFO "%s: found %luK bytes of on-board memory.\n",
+ modname, (unsigned long) hw->memory / 1024);
+
+ /* Load firmware. If loader fails then shut down adapter */
+ err = cycx_load(hw, cfm, len);
+ if (err) cycx_down(hw); /* shutdown adapter */
+ return err;
+}
+
+/* Shut down CYCX: disable shared memory access and interrupts, stop CPU,etc.*/
+EXPORT_SYMBOL(cycx_down);
+int cycx_down (cycxhw_t *hw)
+{
+ return 0; /* FIXME: anything needed here? */
+}
+
+/* Enable interrupt generation. */
+EXPORT_SYMBOL(cycx_inten);
+int cycx_inten (cycxhw_t *hw)
+{
+ switch (hw->type) {
+ case CYCX_2X: writeb (0, hw->dpmbase); break;
+ default: return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Generate an interrupt to adapter's CPU. */
+EXPORT_SYMBOL(cycx_intr);
+int cycx_intr (cycxhw_t *hw)
+{
+ switch (hw->type) {
+ case CYCX_2X:
+ writew(0, hw->dpmbase + GEN_CYCX_INTR);
+ return 0;
+ default: return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Execute Adapter Command.
+ * o Set exec flag.
+ * o Busy-wait until flag is reset. */
+EXPORT_SYMBOL(cycx_exec);
+int cycx_exec (u32 addr)
+{
+ u16 i = 0;
+ /* wait till addr content is zeroed */
+
+ while (readw(addr) != 0) {
+ udelay(1000);
+ if (++i > 50) return -1;
+ }
+
+ return 0;
+}
+
+/* Read absolute adapter memory.
+ * Transfer data from adapter's memory to data buffer. */
+EXPORT_SYMBOL(cycx_peek);
+int cycx_peek (cycxhw_t *hw, u32 addr, void *buf, u32 len)
+{
+ if (len == 1) *(u8*)buf = readb (hw->dpmbase + addr);
+ else memcpy_fromio(buf, hw->dpmbase + addr, len);
+
+ return 0;
+}
+
+/* Write Absolute Adapter Memory.
+ * Transfer data from data buffer to adapter's memory. */
+EXPORT_SYMBOL(cycx_poke);
+int cycx_poke (cycxhw_t *hw, u32 addr, void *buf, u32 len)
+{
+ if (len == 1) writeb (*(u8*)buf, hw->dpmbase + addr);
+ else memcpy_toio(hw->dpmbase + addr, buf, len);
+
+ return 0;
+}
+
+/* Hardware-Specific Functions */
+/* Detect adapter type.
+ * o if adapter type is specified then call detection routine for that adapter
+ * type. Otherwise call detection routines for every adapter types until
+ * adapter is detected.
+ *
+ * Notes:
+ * 1) Detection tests are destructive! Adapter will be left in shutdown state
+ * after the test. */
+static int cycx_detect (cycxhw_t *hw)
+{
+ int err = 0;
+
+ if (!hw->dpmbase) return -EFAULT;
+
+ switch (hw->type) {
+ case CYCX_2X:
+ if (!detect_cycx_2x(hw->dpmbase)) err = -ENODEV;
+ break;
+ default:
+ if (detect_cycx_2x(hw->dpmbase)) hw->type = CYCX_2X;
+ else err = -ENODEV;
+ }
+
+ return err;
+}
+
+/* Load Aux Routines */
+/* Reset board hardware.
+ return 1 if memory exists at addr and 0 if not. */
+static int memory_exists(u32 addr)
+{
+ int timeout = 0;
+
+ for (; timeout < 3 ; timeout++) {
+ writew (TEST_PATTERN, addr + 0x10);
+
+ if (readw (addr + 0x10) == TEST_PATTERN)
+ if (readw (addr + 0x10) == TEST_PATTERN) return 1;
+
+ delay_cycx(1);
+ }
+
+ return 0;
+}
+
+/* Reset board hardware. */
+static int cycx_reset(cycxhw_t *hw)
+{
+ /* Reset board */
+ switch (hw->type) {
+ case CYCX_2X: return reset_cycx_2x(hw->dpmbase);
+ }
+
+ return -EINVAL;
+}
+
+/* Load reset code. */
+static void reset_load(u32 addr, u8 *buffer, u32 cnt)
+{
+ u32 pt_code = addr + RESET_OFFSET;
+ u16 i, j;
+
+ for ( i = 0 ; i < cnt ; i++) {
+ for (j = 0 ; j < 50 ; j++); /* Delay - FIXME busy waiting... */
+ writeb(*buffer++, pt_code++);
+ }
+}
+
+/* Load buffer using boot interface.
+ * o copy data from buffer to Cyclom-X memory
+ * o wait for reset code to copy it to right portion of memory */
+static int buffer_load(u32 addr, u8 *buffer, u32 cnt)
+{
+ memcpy_toio(addr + DATA_OFFSET, buffer, cnt);
+ writew(GEN_BOOT_DAT, addr + CMD_OFFSET);
+ return wait_cyc(addr);
+}
+
+/* Set up entry point and kick start Cyclom-X CPU. */
+static void cycx_start (u32 addr)
+{
+ /* put in 0x30 offset the jump instruction to the code entry point */
+ writeb(0xea, addr + 0x30);
+ writeb(0x00, addr + 0x31);
+ writeb(0xc4, addr + 0x32);
+ writeb(0x00, addr + 0x33);
+ writeb(0x00, addr + 0x34);
+
+ /* cmd to start executing code */
+ writew(GEN_START, addr + CMD_OFFSET);
+}
+
+/* Load and boot reset code. */
+static void cycx_reset_boot(u32 addr, u8 *code, u32 len)
+{
+ u32 pt_start = addr + START_OFFSET;
+
+ writeb(0xea, pt_start++); /* jmp to f000:3f00 */
+ writeb(0x00, pt_start++);
+ writeb(0xfc, pt_start++);
+ writeb(0x00, pt_start++);
+ writeb(0xf0, pt_start);
+ reset_load(addr, code, len);
+
+ /* 80186 was in hold, go */
+ writeb(0, addr + START_CPU);
+ delay_cycx(1);
+}
+
+/* Load data.bin file through boot (reset) interface. */
+static int cycx_data_boot(u32 addr, u8 *code, u32 len)
+{
+ u32 pt_boot_cmd = addr + CMD_OFFSET;
+ u32 i;
+
+ /* boot buffer lenght */
+ writew(CFM_LOAD_BUFSZ, pt_boot_cmd + sizeof(u16));
+ writew(GEN_DEFPAR, pt_boot_cmd);
+
+ if (wait_cyc(addr) < 0) return 2;
+
+ writew(0, pt_boot_cmd + sizeof(u16));
+ writew(0x4000, pt_boot_cmd + 2 * sizeof(u16));
+ writew(GEN_SET_SEG, pt_boot_cmd);
+
+ if (wait_cyc(addr) < 0) return 2;
+
+ for (i = 0 ; i < len ; i += CFM_LOAD_BUFSZ)
+ if (buffer_load(addr, code + i,
+ MIN(CFM_LOAD_BUFSZ, (len - i))) < 0) {
+ printk(KERN_ERR "%s: Error !!\n", modname);
+ return 4;
+ }
+
+ return 0;
+}
+
+
+/* Load code.bin file through boot (reset) interface. */
+static int cycx_code_boot(u32 addr, u8 *code, u32 len)
+{
+ u32 pt_boot_cmd = addr + CMD_OFFSET;
+ u32 i;
+
+ /* boot buffer lenght */
+ writew(CFM_LOAD_BUFSZ, pt_boot_cmd + sizeof(u16));
+ writew(GEN_DEFPAR, pt_boot_cmd);
+
+ if (wait_cyc(addr) == -1) return 2;
+
+ writew(0x0000, pt_boot_cmd + sizeof(u16));
+ writew(0xc400, pt_boot_cmd + 2 * sizeof(u16));
+ writew(GEN_SET_SEG, pt_boot_cmd);
+
+ if (wait_cyc(addr) == -1) return 1;
+
+ for (i = 0 ; i < len ; i += CFM_LOAD_BUFSZ)
+ if (buffer_load(addr, code + i,MIN(CFM_LOAD_BUFSZ,(len - i)))) {
+ printk(KERN_ERR "%s: Error !!\n", modname);
+ return 4;
+ }
+
+ return 0;
+}
+
+/* Initialize CYCX hardware: setup memory window, IRQ, etc. */
+static int cycx_init (cycxhw_t *hw)
+{
+ switch (hw->type) {
+ case CYCX_2X: return init_cycx_2x(hw);
+ }
+
+ return -EINVAL;
+}
+
+/* Load adapter from the memory image of the CYCX firmware module.
+ * o verify firmware integrity and compatibility
+ * o start adapter up */
+static int cycx_load (cycxhw_t *hw, cfm_t *cfm, u32 len)
+{
+ int i, j, status;
+ cycx_header_t *img_hdr;
+ u8 *reset_image,
+ *data_image,
+ *code_image;
+ u32 pt_cycld = hw->dpmbase + 0x400;
+ u16 cksum;
+
+ /* Announce */
+ printk(KERN_INFO "%s: firmware signature=\"%s\"\n",
+ modname, cfm->signature);
+
+ /* Verify firmware signature */
+ if (strcmp(cfm->signature, CFM_SIGNATURE)) {
+ printk(KERN_ERR "%s:cycx_load: not Cyclom-2X firmware!\n",
+ modname);
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "%s: firmware version=%u\n", modname, cfm->version);
+
+ /* Verify firmware module format version */
+ if (cfm->version != CFM_VERSION) {
+ printk(KERN_ERR "%s:cycx_load: firmware format %u rejected! "
+ "Expecting %u.\n",
+ modname, cfm->version, CFM_VERSION);
+ return -EINVAL;
+ }
+
+ /* Verify firmware module length and checksum */
+ cksum = checksum((u8*)&cfm->info, sizeof(cfm_info_t) +
+ cfm->info.codesize);
+/*
+ FIXME cfm->info.codesize is off by 2
+ if (((len - sizeof(cfm_t) - 1) != cfm->info.codesize) ||
+*/
+ if (cksum != cfm->checksum) {
+ printk(KERN_ERR "%s:cycx_load: firmware corrupted!\n", modname);
+ printk(KERN_ERR " cdsize = 0x%x (expected 0x%lx)\n",
+ len - sizeof(cfm_t) - 1, cfm->info.codesize);
+ printk(KERN_ERR " chksum = 0x%x (expected 0x%x)\n",
+ cksum, cfm->checksum);
+ return -EINVAL;
+ }
+
+ /* If everything is ok, set reset, data and code pointers */
+
+ img_hdr = (cycx_header_t*)(((u8*) cfm) + sizeof(cfm_t) - 1);
+#ifdef FIRMWARE_DEBUG
+ printk(KERN_INFO "%s:cycx_load: image sizes\n", modname);
+ printk(KERN_INFO " reset=%lu\n", img_hdr->reset_size);
+ printk(KERN_INFO " data=%lu\n", img_hdr->data_size);
+ printk(KERN_INFO " code=%lu\n", img_hdr->code_size);
+#endif
+ reset_image = ((u8 *) img_hdr) + sizeof(cycx_header_t);
+ data_image = reset_image + img_hdr->reset_size;
+ code_image = data_image + img_hdr->data_size;
+
+ /*---- Start load ----*/
+ /* Announce */
+ printk(KERN_INFO "%s: loading firmware %s (ID=%u)...\n", modname,
+ (cfm->descr[0] != '\0') ? cfm->descr : "unknown firmware",
+ cfm->info.codeid);
+
+ for (i = 0 ; i < 5 ; i++) {
+ /* Reset Cyclom hardware */
+ if ((status = cycx_reset(hw)) != 0) {
+ printk(KERN_ERR "%s: dpm problem or board not "
+ "found (%d).\n", modname, status);
+ return -EINVAL;
+ }
+
+ /* Load reset.bin */
+ cycx_reset_boot(hw->dpmbase, reset_image, img_hdr->reset_size);
+ /* reset is waiting for boot */
+ writew(GEN_POWER_ON, pt_cycld);
+ delay_cycx(1);
+
+ for (j = 0 ; j < 3 ; j++)
+ if (!readw(pt_cycld)) goto reset_loaded;
+ else delay_cycx(1);
+ }
+
+ printk(KERN_ERR "%s: reset not started.\n", modname);
+ return -EINVAL;
+reset_loaded:
+ /* Load data.bin */
+ if((status = cycx_data_boot(hw->dpmbase, data_image,
+ img_hdr->data_size)) != 0) {
+ printk(KERN_ERR "%s: cannot load data file (%d).\n",
+ modname, status);
+ return -EINVAL;
+ }
+
+ /* Load code.bin */
+ if((status = cycx_code_boot(hw->dpmbase, code_image,
+ img_hdr->code_size)) != 0) {
+ printk(KERN_ERR "%s: cannot load code file (%d).\n",
+ modname, status);
+ return -EINVAL;
+ }
+
+ /* Prepare boot-time configuration data */
+ cycx_bootcfg(hw);
+
+ /* kick-off CPU */
+ cycx_start(hw->dpmbase);
+
+ /* Arthur Ganzert's tip: wait a while after the firmware loading...
+ seg abr 26 17:17:12 EST 1999 - acme */
+ delay_cycx(7);
+ printk(KERN_INFO "%s: firmware loaded!\n", modname);
+
+ /* enable interrupts */
+ if (cycx_inten(hw)) {
+ printk(KERN_ERR "%s: adapter hardware failure!\n", modname);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/* Prepare boot-time firmware configuration data.
+ * o initialize configuration data area
+ From async.doc - V_3.4.0 - 07/18/1994
+ - As of now, only static buffers are available to the user.
+ So, the bit VD_RXDIRC must be set in 'valid'. That means that user
+ wants to use the static transmission and reception buffers. */
+static void cycx_bootcfg (cycxhw_t *hw)
+{
+ /* use fixed buffers */
+ writeb(FIXED_BUFFERS, hw->dpmbase + CONF_OFFSET);
+}
+
+/* Initialize CYCX_2X adapter. */
+static int init_cycx_2x (cycxhw_t *hw)
+{
+ if (!detect_cycx_2x(hw->dpmbase)) return -ENODEV;
+ return 0;
+}
+
+/* Detect Cyclom 2x adapter.
+ * Following tests are used to detect Cyclom 2x adapter:
+ * to be completed based on the tests done below
+ * Return 1 if detected o.k. or 0 if failed.
+ * Note: This test is destructive! Adapter will be left in shutdown
+ * state after the test. */
+static int detect_cycx_2x (u32 addr)
+{
+ printk(KERN_INFO "%s: looking for a cyclom 2x at 0x%lX...\n",
+ modname, (unsigned long) addr);
+
+ reset_cycx_2x(addr);
+ return memory_exists(addr);
+}
+
+/* Miscellaneous */
+/* Get option's index into the options list.
+ * Return option's index (1 .. N) or zero if option is invalid. */
+static int get_option_index (u32 *optlist, u32 optval)
+{
+ int i = 1;
+ for (; i <= optlist[0]; ++i) if (optlist[i] == optval) return i;
+ return 0;
+}
+
+/* Reset adapter's CPU. */
+static int reset_cycx_2x (u32 addr)
+{
+ writeb (0, addr + RST_ENABLE); delay_cycx (2);
+ writeb (0, addr + RST_DISABLE); delay_cycx (2);
+ return memory_exists(addr) ? 0 : 1;
+}
+
+/* Delay */
+static void delay_cycx (int sec)
+{
+/* acme
+ Thu dez 31 21:45:16 EDT 1998
+ FIXME I'll keep this comment here just in case, as of now I don't
+ know it all the contexts where this routine is used are interruptible... */
+
+ current->state = TASK_INTERRUPTIBLE;
+ current->counter = 0; /* make us low-priority */
+ schedule_timeout(sec*HZ);
+}
+
+/* Calculate 16-bit CRC using CCITT polynomial. */
+static u16 checksum (u8 *buf, u32 len)
+{
+ u16 crc = 0;
+ u16 mask, flag;
+
+ for (; len; --len, ++buf)
+ for (mask = 0x80; mask; mask >>= 1) {
+ flag = (crc & 0x8000);
+ crc <<= 1;
+ crc |= ((*buf & mask) ? 1 : 0);
+ if (flag) crc ^= 0x1021;
+ }
+
+ return crc;
+}
+/* End */
diff --git a/drivers/net/cycx_main.c b/drivers/net/cycx_main.c
new file mode 100644
index 000000000..d0371c3a1
--- /dev/null
+++ b/drivers/net/cycx_main.c
@@ -0,0 +1,377 @@
+/*
+* cycx_main.c Cyclades Cyclom X Multiprotocol WAN Link Driver. Main module.
+*
+* Author: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+*
+* Copyright: (c) 1998, 1999 Arnaldo Carvalho de Melo
+*
+* Based on sdlamain.c by Gene Kozin <genek@compuserve.com> &
+* Jaspreet Singh <jaspreet@sangoma.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.
+* ============================================================================
+* 1999/05/19 acme works directly linked into the kernel
+* init_waitqueue_head for 2.3.* kernel
+* 1999/05/18 acme major cleanup (polling not needed), etc
+* Aug 28, 1998 Arnaldo minor cleanup (ioctls for firmware deleted)
+* queue_task activated
+* Aug 08, 1998 Arnaldo Initial version.
+*/
+
+#include <linux/config.h> /* OS configuration options */
+#include <linux/stddef.h> /* offsetof(), etc. */
+#include <linux/errno.h> /* return codes */
+#include <linux/string.h> /* inline memset(), etc. */
+#include <linux/malloc.h> /* kmalloc(), kfree() */
+#include <linux/kernel.h> /* printk(), and other useful stuff */
+#include <linux/module.h> /* support for loadable modules */
+#include <linux/ioport.h> /* request_region(), release_region() */
+#include <linux/tqueue.h> /* for kernel task queues */
+#include <linux/wanrouter.h> /* WAN router definitions */
+#include <linux/cyclomx.h> /* cyclomx common user API definitions */
+#include <asm/uaccess.h> /* kernel <-> user copy */
+#include <linux/init.h> /* __initfunc (when not using as a module) */
+
+#ifdef MODULE
+MODULE_AUTHOR("Arnaldo Carvalho de Melo");
+MODULE_DESCRIPTION("Cyclades Sync Cards Driver.");
+#endif
+
+/* Defines & Macros */
+
+#define DRV_VERSION 0 /* version number */
+#define DRV_RELEASE 3 /* release (minor version) number */
+#define MAX_CARDS 1 /* max number of adapters */
+
+#ifndef CONFIG_CYCLOMX_CARDS /* configurable option */
+#define CONFIG_CYCLOMX_CARDS 1
+#endif
+
+/* Function Prototypes */
+
+/* Module entry points */
+int init_module (void);
+void cleanup_module (void);
+
+/* WAN link driver entry points */
+static int setup (wan_device_t *wandev, wandev_conf_t *conf);
+static int shutdown (wan_device_t *wandev);
+static int ioctl (wan_device_t *wandev, unsigned cmd, unsigned long arg);
+
+/* Miscellaneous functions */
+static void cycx_isr (int irq, void *dev_id, struct pt_regs *regs);
+
+/* Global Data
+ * Note: All data must be explicitly initialized!!!
+ */
+
+/* private data */
+static char drvname[] = "cyclomx";
+static char fullname[] = "CYCLOM X(tm) Multiprotocol Driver";
+static char copyright[] = "(c) 1998, 1999 Arnaldo Carvalho de Melo";
+static int ncards = CONFIG_CYCLOMX_CARDS;
+static cycx_t *card_array = NULL; /* adapter data space */
+
+/* Kernel Loadable Module Entry Points */
+
+/*
+ * Module 'insert' entry point.
+ * o print announcement
+ * o allocate adapter data space
+ * o initialize static data
+ * o register all cards with WAN router
+ * o calibrate CYCX shared memory access delay.
+ *
+ * Return: 0 Ok
+ * < 0 error.
+ * Context: process
+ */
+#ifdef MODULE
+int init_module (void)
+#else
+__initfunc(int cyclomx_init (void))
+#endif
+{
+ int cnt, err = 0;
+
+ printk(KERN_INFO "%s v%u.%u %s\n",
+ fullname, DRV_VERSION, DRV_RELEASE, copyright);
+
+ /* Verify number of cards and allocate adapter data space */
+ ncards = min(ncards, MAX_CARDS);
+ ncards = max(ncards, 1);
+ card_array = kmalloc(sizeof(cycx_t) * ncards, GFP_KERNEL);
+
+ if (card_array == NULL) return -ENOMEM;
+
+ memset(card_array, 0, sizeof(cycx_t) * ncards);
+
+ /* Register adapters with WAN router */
+ for (cnt = 0; cnt < ncards; ++cnt) {
+ cycx_t *card = &card_array[cnt];
+ wan_device_t *wandev = &card->wandev;
+
+ sprintf(card->devname, "%s%d", drvname, cnt + 1);
+ wandev->magic = ROUTER_MAGIC;
+ wandev->name = card->devname;
+ wandev->private = card;
+ wandev->enable_tx_int = 0;
+ wandev->setup = &setup;
+ wandev->shutdown = &shutdown;
+ wandev->ioctl = &ioctl;
+ err = register_wan_device(wandev);
+
+ if (err) {
+ printk(KERN_ERR
+ "%s: %s registration failed with error %d!\n",
+ drvname, card->devname, err);
+ break;
+ }
+ }
+
+ if (cnt) ncards = cnt; /* adjust actual number of cards */
+ else {
+ kfree(card_array);
+ err = -ENODEV;
+ }
+
+ return err;
+}
+
+/*
+ * Module 'remove' entry point.
+ * o unregister all adapters from the WAN router
+ * o release all remaining system resources
+ */
+#ifdef MODULE
+void cleanup_module (void)
+{
+ int i = 0;
+
+ for (; i < ncards; ++i) {
+ cycx_t *card = &card_array[i];
+ unregister_wan_device(card->devname);
+ }
+
+ kfree(card_array);
+}
+#endif
+/* WAN Device Driver Entry Points */
+/*
+ * Setup/confugure WAN link driver.
+ * o check adapter state
+ * o make sure firmware is present in configuration
+ * o allocate interrupt vector
+ * o setup CYCLOM X hardware
+ * o call appropriate routine to perform protocol-specific initialization
+ * o mark I/O region as used
+ *
+ * This function is called when router handles ROUTER_SETUP IOCTL. The
+ * configuration structure is in kernel memory (including extended data, if
+ * any).
+ */
+static int setup (wan_device_t *wandev, wandev_conf_t *conf)
+{
+ cycx_t *card;
+ int err = 0;
+ int irq;
+
+ /* Sanity checks */
+ if (!wandev || !wandev->private || !conf) return -EFAULT;
+
+ card = wandev->private;
+
+ if (wandev->state != WAN_UNCONFIGURED) return -EBUSY;
+
+ if (!conf->data_size || (conf->data == NULL)) {
+ printk(KERN_ERR "%s: firmware not found in configuration "
+ "data!\n", wandev->name);
+ return -EINVAL;
+ }
+
+ if (conf->irq <= 0) {
+ printk(KERN_ERR "%s: can't configure without IRQ!\n",
+ wandev->name);
+ return -EINVAL;
+ }
+
+ /* Allocate IRQ */
+ irq = conf->irq == 2 ? 9 : conf->irq; /* IRQ2 -> IRQ9 */
+
+ if (request_irq(irq, cycx_isr, 0, wandev->name, card)) {
+ printk(KERN_ERR "%s: can't reserve IRQ %d!\n",
+ wandev->name, irq);
+ return -EINVAL;
+ }
+
+ /* Configure hardware, load firmware, etc. */
+ memset(&card->hw, 0, sizeof(cycxhw_t));
+ card->hw.irq = (conf->irq == 9) ? 2 : conf->irq;
+ card->hw.dpmbase = conf->maddr;
+ card->hw.dpmsize = CYCX_WINDOWSIZE;
+ card->hw.type = conf->hw_opt[0];
+ card->hw.fwid = CFID_X25_2X;
+ card->lock = SPIN_LOCK_UNLOCKED;
+#if LINUX_VERSION_CODE >= 0x020300
+ init_waitqueue_head(&card->wait_stats);
+#else
+ card->wait_stats = NULL;
+#endif
+ err = cycx_setup(&card->hw, conf->data, conf->data_size);
+
+ if (err) {
+ free_irq(irq, card);
+ return err;
+ }
+
+ /* Intialize WAN device data space */
+ wandev->irq = irq;
+ wandev->dma = wandev->ioport = 0;
+ wandev->maddr = (unsigned long*)card->hw.dpmbase;
+ wandev->msize = card->hw.dpmsize;
+ wandev->hw_opt[0] = card->hw.type;
+ wandev->hw_opt[1] = card->hw.pclk;
+ wandev->hw_opt[2] = card->hw.memory;
+ wandev->hw_opt[3] = card->hw.fwid;
+
+ /* Protocol-specific initialization */
+ switch (card->hw.fwid) {
+#ifdef CONFIG_CYCLOMX_X25
+ case CFID_X25_2X: err = cyx_init(card, conf); break;
+#endif
+ default:
+ printk(KERN_ERR "%s: this firmware is not supported!\n",
+ wandev->name);
+ err = -EINVAL;
+ }
+
+ if (err) {
+ cycx_down(&card->hw);
+ free_irq(irq, card);
+ return err;
+ }
+
+ wandev->critical = 0;
+ return 0;
+}
+
+/*
+ * Shut down WAN link driver.
+ * o shut down adapter hardware
+ * o release system resources.
+ *
+ * This function is called by the router when device is being unregistered or
+ * when it handles ROUTER_DOWN IOCTL.
+ */
+static int shutdown (wan_device_t *wandev)
+{
+ cycx_t *card;
+
+ /* sanity checks */
+ if (!wandev || !wandev->private) return -EFAULT;
+
+ if (wandev->state == WAN_UNCONFIGURED) return 0;
+
+ card = wandev->private;
+ wandev->state = WAN_UNCONFIGURED;
+ cycx_down(&card->hw);
+ printk(KERN_INFO "%s: irq %d being freed!\n", wandev->name,wandev->irq);
+ free_irq(wandev->irq, card);
+ wandev->critical = 0;
+ return 0;
+}
+
+/*
+ * Driver I/O control.
+ * o verify arguments
+ * o perform requested action
+ *
+ * This function is called when router handles one of the reserved user
+ * IOCTLs. Note that 'arg' stil points to user address space.
+ */
+static int ioctl (wan_device_t *wandev, unsigned cmd, unsigned long arg)
+{
+ return -EINVAL;
+}
+
+/* Miscellaneous */
+/*
+ * CYCX Interrupt Service Routine.
+ * o acknowledge CYCX hardware interrupt.
+ * o call protocol-specific interrupt service routine, if any.
+ */
+static void cycx_isr (int irq, void *dev_id, struct pt_regs *regs)
+{
+#define card ((cycx_t*)dev_id)
+ if (!card || card->wandev.state == WAN_UNCONFIGURED) return;
+
+ if (card->in_isr) {
+ printk(KERN_WARNING "%s: interrupt re-entrancy on IRQ %d!\n",
+ card->devname, card->wandev.irq);
+ return;
+ }
+
+ if (card->isr) card->isr(card);
+#undef card
+}
+
+/*
+ * This routine is called by the protocol-specific modules when network
+ * interface is being open. The only reason we need this, is because we
+ * have to call MOD_INC_USE_COUNT, but cannot include 'module.h' where it's
+ * defined more than once into the same kernel module.
+ */
+void cyclomx_open (cycx_t *card)
+{
+ ++card->open_cnt;
+ MOD_INC_USE_COUNT;
+}
+
+/*
+ * This routine is called by the protocol-specific modules when network
+ * interface is being closed. The only reason we need this, is because we
+ * have to call MOD_DEC_USE_COUNT, but cannot include 'module.h' where it's
+ * defined more than once into the same kernel module.
+ */
+void cyclomx_close (cycx_t *card)
+{
+ --card->open_cnt;
+ MOD_DEC_USE_COUNT;
+}
+
+/* Set WAN device state. */
+void cyclomx_set_state (cycx_t *card, int state)
+{
+ unsigned long flags;
+
+ save_flags(flags); cli();
+
+ if (card->wandev.state != state) {
+ switch (state) {
+ case WAN_CONNECTED:
+ printk (KERN_INFO "%s: link connected!\n",
+ card->devname);
+ break;
+
+ case WAN_CONNECTING:
+ printk (KERN_INFO "%s: link connecting...\n",
+ card->devname);
+ break;
+
+ case WAN_DISCONNECTED:
+ printk (KERN_INFO "%s: link disconnected!\n",
+ card->devname);
+ break;
+ }
+
+ card->wandev.state = state;
+ }
+
+ card->state_tick = jiffies;
+ restore_flags(flags);
+}
+
+/* End */
diff --git a/drivers/net/cycx_x25.c b/drivers/net/cycx_x25.c
new file mode 100644
index 000000000..d6fbe07c3
--- /dev/null
+++ b/drivers/net/cycx_x25.c
@@ -0,0 +1,1538 @@
+/*
+* cycx_x25.c CYCLOM X Multiprotocol WAN Link Driver. X.25 module.
+*
+* Author: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+* Copyright: (c) 1998, 1999 Arnaldo Carvalho de Melo
+*
+* Based on sdla_x25.c by Gene Kozin <genek@compuserve.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.
+* ============================================================================
+* 1999/05/28 acme fixed nibble_to_byte, ackvc now properly treated
+* if_send simplified
+* 1999/05/25 acme fixed t1, t2, t21 & t23 configuration
+* use spinlocks instead of cli/sti in some points
+* 1999/05/24 acme finished the x25_get_stat function
+* 1999/05/23 acme dev->type = ARPHRD_X25 (tcpdump only works,
+* AFAIT, with ARPHRD_ETHER). This seems to be
+* needed to use socket(AF_X25)...
+* Now the config file must specify a peer media
+* address for svc channes over a crossover cable.
+* Removed hold_timeout from x25_channel_t,
+* not used.
+* A little enhancement in the DEBUG processing
+* 1999/05/22 acme go to DISCONNECTED in disconnect_confirm_intr,
+* instead of chan_disc.
+* 1999/05/16 marcelo fixed timer initialization in SVCs
+* 1999/01/05 acme x25_configure now get (most of) all
+* parameters...
+* 1999/01/05 acme pktlen now (correctly) uses log2 (value
+* configured)
+* 1999/01/03 acme judicious use of data types (u8, u16, u32, etc)
+* 1999/01/03 acme cyx_isr: reset dpmbase to acknowledge
+* indication (interrupt from cyclom 2x)
+* 1999/01/02 acme cyx_isr: first hackings...
+* 1999/01/0203 acme when initializing an array don't give less
+* elements than declared...
+* example: char send_cmd[6] = "?\xFF\x10";
+* you'll gonna lose a couple hours, 'cause your
+* brain won't admit that there's an error in the
+* above declaration... the side effect is that
+* memset is put into the unresolved symbols
+* instead of using the inline memset functions...
+* 1999/01/02 acme began chan_connect, chan_send, x25_send
+* Dec 31, 1998 Arnaldo x25_configure
+* this code can be compiled as non module
+* Dec 27, 1998 Arnaldo code cleanup
+* IPX code wiped out! let's decrease code
+* complexity for now, remember: I'm learning! :)
+* bps_to_speed_code OK
+* Dec 26, 1998 Arnaldo Minimal debug code cleanup
+* Aug 08, 1998 Arnaldo Initial version.
+*/
+#define CYCLOMX_X25_DEBUG 1
+
+#include <linux/version.h>
+#include <linux/kernel.h> /* printk(), and other useful stuff */
+#include <linux/stddef.h> /* offsetof(), etc. */
+#include <linux/errno.h> /* return codes */
+#include <linux/string.h> /* inline memset(), etc. */
+#include <linux/malloc.h> /* kmalloc(), kfree() */
+#include <linux/wanrouter.h> /* WAN router definitions */
+#include <asm/byteorder.h> /* htons(), etc. */
+#include <linux/if_arp.h> /* ARPHRD_X25 */
+#include <linux/cyclomx.h> /* CYCLOM X common user API definitions */
+#include <linux/cycx_x25.h> /* X.25 firmware API definitions */
+
+/* Defines & Macros */
+#define MAX_CMD_RETRY 5
+#define X25_CHAN_MTU 2048 /* unfragmented logical channel MTU */
+#define OUT_INTR 1
+#define IN_INTR 0
+
+/* Data Structures */
+/* This is an extention of the 'struct device' we create for each network
+ interface to keep the rest of X.25 channel-specific data. */
+typedef struct x25_channel {
+ char name[WAN_IFNAME_SZ+1]; /* interface name, ASCIIZ */
+ char addr[WAN_ADDRESS_SZ+1]; /* media address, ASCIIZ */
+ char *local_addr; /* local media address, ASCIIZ -
+ svc thru crossover cable */
+ s16 lcn; /* logical channel number/conn.req.key*/
+ u8 link;
+ struct timer_list timer; /* timer used for svc channel disc. */
+ spinlock_t lock;
+ u16 protocol; /* ethertype, 0 - multiplexed */
+ u8 svc; /* 0 - permanent, 1 - switched */
+ u8 state; /* channel state */
+ u8 drop_sequence; /* mark sequence for dropping */
+ u32 idle_tmout; /* sec, before disconnecting */
+ struct sk_buff *rx_skb; /* receive socket buffer */
+ cycx_t *card; /* -> owner */
+ struct enet_statistics ifstats; /* interface statistics */
+} x25_channel_t;
+
+/* Function Prototypes */
+/* WAN link driver entry points. These are called by the WAN router module. */
+static int update (wan_device_t *wandev),
+ new_if (wan_device_t *wandev, struct device *dev,wanif_conf_t *conf),
+ del_if (wan_device_t *wandev, struct device *dev);
+
+/* Network device interface */
+static int if_init (struct device *dev),
+ if_open (struct device *dev),
+ if_close (struct device *dev),
+ if_header (struct sk_buff *skb, struct device *dev,
+ u16 type, void *daddr, void *saddr, unsigned len),
+ if_rebuild_hdr (struct sk_buff *skb),
+ if_send (struct sk_buff *skb, struct device *dev);
+
+static struct net_device_stats * if_stats (struct device *dev);
+
+/* Interrupt handlers */
+static void cyx_isr (cycx_t *card),
+ tx_intr (cycx_t *card, TX25Cmd *cmd),
+ rx_intr (cycx_t *card, TX25Cmd *cmd),
+ log_intr (cycx_t *card, TX25Cmd *cmd),
+ stat_intr (cycx_t *card, TX25Cmd *cmd),
+ connect_confirm_intr (cycx_t *card, TX25Cmd *cmd),
+ disconnect_confirm_intr (cycx_t *card, TX25Cmd *cmd),
+ connect_intr (cycx_t *card, TX25Cmd *cmd),
+ disconnect_intr (cycx_t *card, TX25Cmd *cmd),
+ spur_intr (cycx_t *card, TX25Cmd *cmd);
+
+/* X.25 firmware interface functions */
+static int x25_configure (cycx_t *card, TX25Config *conf),
+ x25_get_stats (cycx_t *card),
+ x25_send (cycx_t *card, u8 link, u8 lcn, u8 bitm, int len,void *buf),
+ x25_connect_response (cycx_t *card, x25_channel_t *chan),
+ x25_disconnect_response (cycx_t *card, u8 link, u8 lcn);
+
+/* Miscellaneous functions */
+static int chan_connect (struct device *dev),
+ chan_send (struct device *dev, struct sk_buff *skb);
+
+static void set_chan_state (struct device *dev, u8 state, u8 outside_intr),
+ nibble_to_byte (u8 *s, u8 *d, u8 len, u8 nibble),
+ reset_timer (struct device *dev),
+ chan_disc (struct device *dev),
+ chan_timer (unsigned long data);
+
+static u8 bps_to_speed_code (u32 bps);
+static u8 log2 (u32 n);
+
+static unsigned dec_to_uint (u8 *str, int len);
+
+static struct device *get_dev_by_lcn (wan_device_t *wandev, s16 lcn);
+static struct device *get_dev_by_dte_addr (wan_device_t *wandev, char *dte);
+
+#ifdef CYCLOMX_X25_DEBUG
+static void hex_dump(char *msg, unsigned char *p, int len);
+static void x25_dump_config(TX25Config *conf);
+static void x25_dump_stats(TX25Stats *stats);
+static void x25_dump_devs(wan_device_t *wandev);
+#define dprintk(format, a...) printk(format, ##a)
+#else
+#define hex_dump(msg, p, len)
+#define x25_dump_config(conf)
+#define x25_dump_stats(stats)
+#define x25_dump_devs(wandev)
+#define dprintk(format, a...)
+#endif
+/* Public Functions */
+
+/* X.25 Protocol Initialization routine.
+ *
+ * This routine is called by the main CYCLOM X module during setup. At this
+ * point adapter is completely initialized and X.25 firmware is running.
+ * o read firmware version (to make sure it's alive)
+ * o configure adapter
+ * o initialize protocol-specific fields of the adapter data space.
+ *
+ * Return: 0 o.k.
+ * < 0 failure. */
+int cyx_init (cycx_t *card, wandev_conf_t *conf)
+{
+ TX25Config cfg;
+
+ /* Verify configuration ID */
+ if (conf->config_id != WANCONFIG_X25) {
+ printk(KERN_INFO "%s: invalid configuration ID %u!\n",
+ card->devname, conf->config_id);
+ return -EINVAL;
+ }
+
+ /* Initialize protocol-specific fields */
+ card->mbox = card->hw.dpmbase + X25_MBOX_OFFS;
+ card->u.x.critical = 0; /* critical section flag */
+ card->u.x.connection_keys = 0;
+
+ /* Configure adapter. Here we set resonable defaults, then parse
+ * device configuration structure and set configuration options.
+ * Most configuration options are verified and corrected (if
+ * necessary) since we can't rely on the adapter to do so and don't
+ * want it to fail either. */
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.link = 0;
+ cfg.clock = conf->clocking == WANOPT_EXTERNAL ? 8 : 55;
+ cfg.speed = bps_to_speed_code(conf->bps);
+ cfg.n3win = 7;
+ cfg.n2win = 2;
+ cfg.n2 = 5;
+ cfg.nvc = 1;
+ cfg.npvc = 1;
+ cfg.flags = 0x02; /* default = V35 */
+ cfg.t1 = 10; /* line carrier timeout */
+ cfg.t2 = 29; /* tx timeout */
+ cfg.t21 = 180; /* CALL timeout */
+ cfg.t23 = 180; /* CLEAR timeout */
+
+ /* adjust MTU */
+ if (!conf->mtu || conf->mtu >= 512)
+ card->wandev.mtu = 512;
+ else if (conf->mtu >= 256)
+ card->wandev.mtu = 256;
+ else if (conf->mtu >= 128)
+ card->wandev.mtu = 128;
+ else
+ card->wandev.mtu = 64;
+
+ cfg.pktlen = log2(card->wandev.mtu);
+
+ if (conf->station == WANOPT_DTE) {
+ cfg.locaddr = 3; /* DTE */
+ cfg.remaddr = 1; /* DCE */
+ } else {
+ cfg.locaddr = 1; /* DCE */
+ cfg.remaddr = 3; /* DTE */
+ }
+
+ if (conf->interface == WANOPT_RS232)
+ cfg.flags = 0; /* FIXME just reset the 2nd bit */
+
+ if (conf->u.x25.hi_pvc) {
+ card->u.x.hi_pvc = min(conf->u.x25.hi_pvc, 4095);
+ card->u.x.lo_pvc = min(conf->u.x25.lo_pvc, card->u.x.hi_pvc);
+ }
+
+ if (conf->u.x25.hi_svc) {
+ card->u.x.hi_svc = min(conf->u.x25.hi_svc, 4095);
+ card->u.x.lo_svc = min(conf->u.x25.lo_svc, card->u.x.hi_svc);
+ }
+
+ if (card->u.x.lo_pvc == 255)
+ cfg.npvc = 0;
+ else
+ cfg.npvc = card->u.x.hi_pvc - card->u.x.lo_pvc + 1;
+
+ cfg.nvc = card->u.x.hi_svc - card->u.x.lo_svc + 1 + cfg.npvc;
+
+ if (conf->u.x25.hdlc_window)
+ cfg.n2win = min(conf->u.x25.hdlc_window, 7);
+
+ if (conf->u.x25.pkt_window)
+ cfg.n3win = min(conf->u.x25.pkt_window, 7);
+
+ if (conf->u.x25.t1)
+ cfg.t1 = min(conf->u.x25.t1, 30);
+
+ if (conf->u.x25.t2)
+ cfg.t2 = min(conf->u.x25.t2, 30);
+
+ if (conf->u.x25.t11_t21)
+ cfg.t21 = min(conf->u.x25.t11_t21, 30);
+
+ if (conf->u.x25.t13_t23)
+ cfg.t23 = min(conf->u.x25.t13_t23, 30);
+
+ if (conf->u.x25.n2)
+ cfg.n2 = min(conf->u.x25.n2, 30);
+
+ /* initialize adapter */
+ if (x25_configure(card, &cfg))
+ return -EIO;
+
+ /* Initialize protocol-specific fields of adapter data space */
+ card->wandev.bps = conf->bps;
+ card->wandev.interface = conf->interface;
+ card->wandev.clocking = conf->clocking;
+ card->wandev.station = conf->station;
+ card->isr = &cyx_isr;
+ card->exec = NULL;
+ card->wandev.update = &update;
+ card->wandev.new_if = &new_if;
+ card->wandev.del_if = &del_if;
+ card->wandev.state = WAN_DISCONNECTED;
+ card->wandev.enable_tx_int = card->irq_dis_if_send_count = 0;
+ return 0;
+}
+
+/* WAN Device Driver Entry Points */
+/* Update device status & statistics. */
+static int update (wan_device_t *wandev)
+{
+ /* sanity checks */
+ if (!wandev || !wandev->private)
+ return -EFAULT;
+
+ if (wandev->state == WAN_UNCONFIGURED)
+ return -ENODEV;
+
+ x25_get_stats(wandev->private);
+ return 0;
+}
+
+/* Create new logical channel.
+ * This routine is called by the router when ROUTER_IFNEW IOCTL is being
+ * handled.
+ * o parse media- and hardware-specific configuration
+ * o make sure that a new channel can be created
+ * o allocate resources, if necessary
+ * o prepare network device structure for registaration.
+ *
+ * Return: 0 o.k.
+ * < 0 failure (channel will not be created) */
+static int new_if (wan_device_t *wandev, struct device *dev, wanif_conf_t *conf)
+{
+ cycx_t *card = wandev->private;
+ x25_channel_t *chan;
+ int err = 0;
+
+ if (conf->name[0] == '\0' || strlen(conf->name) > WAN_IFNAME_SZ) {
+ printk(KERN_INFO "%s: invalid interface name!\n",card->devname);
+ return -EINVAL;
+ }
+
+ /* allocate and initialize private data */
+ if ((chan = kmalloc(sizeof(x25_channel_t), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ memset(chan, 0, sizeof(x25_channel_t));
+ strcpy(chan->name, conf->name);
+ chan->card = card;
+ chan->link = conf->port;
+ chan->protocol = ETH_P_IP;
+ chan->rx_skb = NULL;
+ /* only used in svc connected thru crossover cable */
+ chan->local_addr = NULL;
+ chan->lock = SPIN_LOCK_UNLOCKED;
+
+ if (conf->addr[0] == '@') { /* SVC */
+ int local_len = strlen(conf->local_addr);
+
+ if (local_len) {
+ if (local_len > WAN_ADDRESS_SZ) {
+ printk(KERN_ERR "%s: %s local addr too long!\n",
+ wandev->name, chan->name);
+ kfree(chan);
+ return -EINVAL;
+ } else if ((chan->local_addr = kmalloc(local_len + 1,
+ GFP_KERNEL)) == NULL) {
+ kfree(chan);
+ return ENOMEM;
+ }
+
+ strncpy(chan->local_addr, conf->local_addr,
+ WAN_ADDRESS_SZ);
+ }
+
+ chan->svc = 1;
+ strncpy(chan->addr, &conf->addr[1], WAN_ADDRESS_SZ);
+ init_timer(&chan->timer);
+ chan->timer.function = chan_timer;
+ chan->timer.data = (unsigned long) dev;
+
+ /* Set channel timeouts (default if not specified) */
+ chan->idle_tmout = conf->idle_timeout ? conf->idle_timeout : 90;
+ } else if (is_digit(conf->addr[0])) { /* PVC */
+ s16 lcn = dec_to_uint(conf->addr, 0);
+
+ if (lcn >= card->u.x.lo_pvc && lcn <= card->u.x.hi_pvc)
+ chan->lcn = lcn;
+ else {
+ printk(KERN_ERR
+ "%s: PVC %u is out of range on interface %s!\n",
+ wandev->name, lcn, chan->name);
+ err = -EINVAL;
+ }
+ } else {
+ printk(KERN_ERR "%s: invalid media address on interface %s!\n",
+ wandev->name, chan->name);
+ err = -EINVAL;
+ }
+
+ if (err) {
+ if (chan->local_addr)
+ kfree(chan->local_addr);
+ kfree(chan);
+ return err;
+ }
+
+ /* prepare network device data space for registration */
+ dev->name = chan->name;
+ dev->init = &if_init;
+ dev->priv = chan;
+ return 0;
+}
+
+/* Delete logical channel. */
+static int del_if (wan_device_t *wandev, struct device *dev)
+{
+ if (!dev) {
+ printk(KERN_ERR "cycx_x25:del_if:dev == NULL!\n");
+ return 0;
+ }
+
+ if (dev->priv) {
+ x25_channel_t *chan = dev->priv;
+ if (chan->svc) {
+ if (chan->local_addr)
+ kfree(chan->local_addr);
+
+ if (chan->state == WAN_CONNECTED)
+ del_timer(&chan->timer);
+ }
+ kfree(chan);
+ dev->priv = NULL;
+ }
+
+ return 0;
+}
+
+/* Network Device Interface */
+/* Initialize Linux network interface.
+ *
+ * This routine is called only once for each interface, during Linux network
+ * interface registration. Returning anything but zero will fail interface
+ * registration. */
+static int if_init (struct device *dev)
+{
+ x25_channel_t *chan = dev->priv;
+ cycx_t *card = chan->card;
+ wan_device_t *wandev = &card->wandev;
+
+ /* Initialize device driver entry points */
+ dev->open = &if_open;
+ dev->stop = &if_close;
+ dev->hard_header = &if_header;
+ dev->rebuild_header = &if_rebuild_hdr;
+ dev->hard_start_xmit = &if_send;
+ dev->get_stats = &if_stats;
+
+ /* Initialize media-specific parameters */
+ dev->mtu = X25_CHAN_MTU;
+ dev->type = ARPHRD_X25; /* ARP h/w type */
+ dev->hard_header_len = 0; /* media header length */
+ dev->addr_len = 0; /* hardware address length */
+
+ if (!chan->svc)
+ *(u16*)dev->dev_addr = htons(chan->lcn);
+
+ /* Initialize hardware parameters (just for reference) */
+ dev->irq = wandev->irq;
+ dev->dma = wandev->dma;
+ dev->base_addr = wandev->ioport;
+ dev->mem_start = (unsigned long)wandev->maddr;
+ dev->mem_end = (unsigned long)(wandev->maddr + wandev->msize - 1);
+ dev->flags |= IFF_NOARP;
+
+ /* Set transmit buffer queue length */
+ dev->tx_queue_len = 10;
+
+ /* Initialize socket buffers */
+ dev_init_buffers(dev);
+ set_chan_state(dev, WAN_DISCONNECTED, OUT_INTR);
+ return 0;
+}
+
+/* Open network interface.
+ * o prevent module from unloading by incrementing use count
+ * o if link is disconnected then initiate connection
+ *
+ * Return 0 if O.k. or errno. */
+static int if_open (struct device *dev)
+{
+ x25_channel_t *chan = dev->priv;
+ cycx_t *card = chan->card;
+
+ if (dev->start)
+ return -EBUSY; /* only one open is allowed */
+
+ if (test_and_set_bit(0, (void*)&card->wandev.critical))
+ return -EAGAIN;
+
+ dev->interrupt = 0;
+ dev->tbusy = 0;
+ dev->start = 1;
+ cyclomx_open(card);
+
+ card->wandev.critical = 0;
+ return 0;
+}
+
+/* Close network interface.
+ * o reset flags.
+ * o if there's no more open channels then disconnect physical link. */
+static int if_close (struct device *dev)
+{
+ x25_channel_t *chan = dev->priv;
+ cycx_t *card = chan->card;
+
+ if (test_and_set_bit(0, (void*)&card->wandev.critical))
+ return -EAGAIN;
+
+ dev->start = 0;
+
+ if (chan->state == WAN_CONNECTED || chan->state == WAN_CONNECTING)
+ chan_disc(dev);
+
+ cyclomx_close(card);
+
+ card->wandev.critical = 0;
+ return 0;
+}
+
+/* Build media header.
+ * o encapsulate packet according to encapsulation type.
+ *
+ * The trick here is to put packet type (Ethertype) into 'protocol' field of
+ * the socket buffer, so that we don't forget it. If encapsulation fails,
+ * set skb->protocol to 0 and discard packet later.
+ *
+ * Return: media header length. */
+static int if_header (struct sk_buff *skb, struct device *dev,
+ u16 type, void *daddr, void *saddr, unsigned len)
+{
+ skb->protocol = type;
+ return dev->hard_header_len;
+}
+
+/* * Re-build media header.
+ * Return: 1 physical address resolved.
+ * 0 physical address not resolved */
+static int if_rebuild_hdr (struct sk_buff *skb)
+{
+ return 1;
+}
+
+/* Send a packet on a network interface.
+ * o set tbusy flag (marks start of the transmission).
+ * o check link state. If link is not up, then drop the packet.
+ * o check channel status. If it's down then initiate a call.
+ * o pass a packet to corresponding WAN device.
+ * o free socket buffer
+ *
+ * Return: 0 complete (socket buffer must be freed)
+ * non-0 packet may be re-transmitted (tbusy must be set)
+ *
+ * Notes:
+ * 1. This routine is called either by the protocol stack or by the "net
+ * bottom half" (with interrupts enabled).
+ * 2. Setting tbusy flag will inhibit further transmit requests from the
+ * protocol stack and can be used for flow control with protocol layer. */
+static int if_send (struct sk_buff *skb, struct device *dev)
+{
+ x25_channel_t *chan = dev->priv;
+ cycx_t *card = chan->card;
+
+ if (dev->tbusy) {
+ ++chan->ifstats.rx_dropped;
+ return -EBUSY;
+ }
+
+ dev->tbusy = 1;
+
+ reset_timer(dev);
+
+ if (!chan->svc)
+ chan->protocol = skb->protocol;
+
+ if (card->wandev.state != WAN_CONNECTED)
+ ++chan->ifstats.tx_dropped;
+ else if (chan->svc && chan->protocol &&
+ chan->protocol != skb->protocol) {
+ printk(KERN_INFO
+ "%s: unsupported Ethertype 0x%04X on interface %s!\n",
+ card->devname, skb->protocol, dev->name);
+ ++chan->ifstats.tx_errors;
+ } else switch (chan->state) {
+ case WAN_DISCONNECTED:
+ if (chan_connect(dev))
+ return -EBUSY;
+ /* fall thru */
+ case WAN_CONNECTED:
+ dev->trans_start = jiffies;
+ if (chan_send(dev, skb)) {
+ dev->tbusy = 1;
+ return -EBUSY;
+ }
+ break;
+ default:
+ ++chan->ifstats.tx_dropped;
+ ++card->wandev.stats.tx_dropped;
+ }
+
+ dev_kfree_skb(skb);
+ return 0;
+}
+
+/* Get Ethernet-style interface statistics.
+ * Return a pointer to struct net_device_stats */
+static struct net_device_stats *if_stats (struct device *dev)
+{
+ x25_channel_t *chan = dev->priv;
+
+ return chan ? &chan->ifstats : NULL;
+}
+
+/* Interrupt Handlers */
+/* X.25 Interrupt Service Routine. */
+static void cyx_isr (cycx_t *card)
+{
+ unsigned long host_cpu_flags;
+ TX25Cmd cmd;
+ u16 z = 0;
+
+ card->in_isr = 1;
+ card->buff_int_mode_unbusy = 0;
+
+ if (test_and_set_bit(0, (void*)&card->wandev.critical)) {
+ printk(KERN_INFO "cyx_isr: %s, wandev.critical set to 0x%02X\n",
+ card->devname, card->wandev.critical);
+ card->in_isr = 0;
+ return;
+ }
+
+ /* For all interrupts set the critical flag to CRITICAL_RX_INTR.
+ * If the if_send routine is called with this flag set it will set
+ * the enable transmit flag to 1. (for a delayed interrupt) */
+ card->wandev.critical = CRITICAL_IN_ISR;
+ cycx_peek(&card->hw, X25_RXMBOX_OFFS, &cmd, sizeof(cmd));
+ switch (cmd.command) {
+ case X25_DATA_INDICATION:
+ rx_intr(card, &cmd);
+ break;
+ case X25_ACK_FROM_VC:
+ tx_intr(card, &cmd);
+ break;
+ case X25_LOG:
+ log_intr(card, &cmd);
+ break;
+ case X25_STATISTIC:
+ stat_intr(card, &cmd);
+ break;
+ case X25_CONNECT_CONFIRM:
+ connect_confirm_intr(card, &cmd);
+ break;
+ case X25_CONNECT_INDICATION:
+ connect_intr(card, &cmd);
+ break;
+ case X25_DISCONNECT_INDICATION:
+ disconnect_intr(card, &cmd);
+ break;
+ case X25_DISCONNECT_CONFIRM:
+ disconnect_confirm_intr(card, &cmd);
+ break;
+ case X25_LINE_ON:
+ cyclomx_set_state(card, WAN_CONNECTED);
+ break;
+ case X25_LINE_OFF:
+ cyclomx_set_state(card, WAN_DISCONNECTED);
+ break;
+ default:
+ spur_intr(card, &cmd); /* unwanted interrupt */
+ }
+
+ cycx_poke(&card->hw, 0, &z, sizeof(z));
+ cycx_poke(&card->hw, X25_RXMBOX_OFFS, &z, sizeof(z));
+
+ card->wandev.critical = CRITICAL_INTR_HANDLED;
+
+ if (card->wandev.enable_tx_int)
+ card->wandev.enable_tx_int = 0;
+
+ spin_lock_irqsave(&card->lock, host_cpu_flags);
+ card->in_isr = 0;
+ card->wandev.critical = 0;
+ spin_unlock_irqrestore(&card->lock, host_cpu_flags);
+
+ if (card->buff_int_mode_unbusy)
+ mark_bh(NET_BH);
+}
+
+/* Transmit interrupt handler.
+ * o Release socket buffer
+ * o Clear 'tbusy' flag */
+static void tx_intr (cycx_t *card, TX25Cmd *cmd)
+{
+ struct device *dev;
+ wan_device_t *wandev = &card->wandev;
+ u8 lcn;
+
+ cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn));
+
+ /* unbusy device and then dev_tint(); */
+ if ((dev = get_dev_by_lcn (wandev, lcn)) != NULL) {
+ card->buff_int_mode_unbusy = 1;
+ dev->tbusy = 0;
+ } else
+ printk(KERN_ERR "%s:ackvc for inexistent lcn %d\n",
+ card->devname, lcn);
+}
+
+/* Receive interrupt handler.
+ * This routine handles fragmented IP packets using M-bit according to the
+ * RFC1356.
+ * o map ligical channel number to network interface.
+ * o allocate socket buffer or append received packet to the existing one.
+ * o if M-bit is reset (i.e. it's the last packet in a sequence) then
+ * decapsulate packet and pass socket buffer to the protocol stack.
+ *
+ * Notes:
+ * 1. When allocating a socket buffer, if M-bit is set then more data is
+ * comming and we have to allocate buffer for the maximum IP packet size
+ * expected on this channel.
+ * 2. If something goes wrong and X.25 packet has to be dropped (e.g. no
+ * socket buffers available) the whole packet sequence must be discarded. */
+static void rx_intr (cycx_t *card, TX25Cmd *cmd)
+{
+ wan_device_t *wandev = &card->wandev;
+ struct device *dev;
+ x25_channel_t *chan;
+ struct sk_buff *skb;
+ u8 bitm, lcn;
+ int pktlen = cmd->len - 5;
+
+ cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn));
+ cycx_peek(&card->hw, cmd->buf + 4, &bitm, sizeof(bitm));
+ bitm &= 0x10;
+
+ if ((dev = get_dev_by_lcn(wandev, lcn)) == NULL) {
+ /* Invalid channel, discard packet */
+ printk(KERN_INFO "%s: receiving on orphaned LCN %d!\n",
+ card->devname, lcn);
+ return;
+ }
+
+ chan = dev->priv;
+ reset_timer(dev);
+
+ if (chan->drop_sequence)
+ if (!bitm)
+ chan->drop_sequence = 0;
+ else
+ return;
+
+ if ((skb = chan->rx_skb) == NULL) {
+ /* Allocate new socket buffer */
+ int bufsize = bitm ? dev->mtu : pktlen;
+
+ if ((skb = dev_alloc_skb(bufsize +
+ dev->hard_header_len)) == NULL) {
+ printk(KERN_INFO "%s: no socket buffers available!\n",
+ card->devname);
+ chan->drop_sequence = 1;
+ ++chan->ifstats.rx_dropped;
+ return;
+ }
+
+ skb->dev = dev;
+ skb->protocol = htons(chan->protocol);
+ chan->rx_skb = skb;
+ }
+
+ if (skb_tailroom(skb) < pktlen) {
+ /* No room for the packet. Call off the whole thing! */
+ dev_kfree_skb(skb);
+ chan->rx_skb = NULL;
+
+ if (bitm)
+ chan->drop_sequence = 1;
+
+ printk(KERN_INFO "%s: unexpectedly long packet sequence "
+ "on interface %s!\n", card->devname, dev->name);
+ ++chan->ifstats.rx_length_errors;
+ return;
+ }
+
+ /* Append packet to the socket buffer */
+ cycx_peek(&card->hw, cmd->buf + 5, skb_put(skb, pktlen), pktlen);
+
+ if (bitm)
+ return; /* more data is coming */
+
+ dev->last_rx = jiffies; /* timestamp */
+ chan->rx_skb = NULL; /* dequeue packet */
+
+ skb->protocol = htons(ETH_P_IP);
+ skb->dev = dev;
+ skb->mac.raw = skb->data;
+ netif_rx(skb);
+ ++chan->ifstats.rx_packets;
+ chan->ifstats.rx_bytes += skb->len;
+}
+
+/* Connect interrupt handler. */
+static void connect_intr (cycx_t *card, TX25Cmd *cmd)
+{
+ wan_device_t *wandev = &card->wandev;
+ struct device *dev = NULL;
+ x25_channel_t *chan;
+ u8 data[32],
+ local[24],
+ rem[24];
+ u8 lcn, sizelocal, sizerem;
+
+ cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn));
+ cycx_peek(&card->hw, cmd->buf + 5, &sizelocal, sizeof(sizelocal));
+ cycx_peek(&card->hw, cmd->buf + 6, data, cmd->len - 6);
+
+ sizerem = sizelocal >> 4;
+ sizelocal &= 0x0F;
+
+ local[0] = rem[0] = '\0';
+
+ if (sizelocal)
+ nibble_to_byte(data, local, sizelocal, 0);
+
+ if (sizerem)
+ nibble_to_byte(data + (sizelocal >> 1), rem, sizerem, sizelocal & 1);
+ dprintk(KERN_INFO "connect_intr:lcn=%d, local=%s, remote=%s\n",
+ lcn, local, rem);
+ if ((dev = get_dev_by_dte_addr(wandev, rem)) == NULL) {
+ /* Invalid channel, discard packet */
+ printk(KERN_INFO "%s: connect not expected: remote %s!\n",
+ card->devname, rem);
+ return;
+ }
+
+ chan = dev->priv;
+ chan->lcn = lcn;
+ x25_connect_response(card, chan);
+ set_chan_state(dev, WAN_CONNECTED, IN_INTR);
+}
+
+/* Connect confirm interrupt handler. */
+static void connect_confirm_intr (cycx_t *card, TX25Cmd *cmd)
+{
+ wan_device_t *wandev = &card->wandev;
+ struct device *dev;
+ x25_channel_t *chan;
+ u8 lcn, key;
+
+ cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn));
+ cycx_peek(&card->hw, cmd->buf + 1, &key, sizeof(key));
+ dprintk(KERN_INFO "%s: connect_confirm_intr:lcn=%d, key=%d\n",
+ card->devname, lcn, key);
+ if ((dev = get_dev_by_lcn(wandev, -key)) == NULL) {
+ /* Invalid channel, discard packet */
+ clear_bit(--key, (void*)&card->u.x.connection_keys);
+ printk(KERN_INFO "%s: connect confirm not expected: lcn %d, "
+ "key=%d!\n", card->devname, lcn, key);
+ return;
+ }
+
+ clear_bit(--key, (void*)&card->u.x.connection_keys);
+ chan = dev->priv;
+ chan->lcn = lcn;
+ set_chan_state(dev, WAN_CONNECTED, IN_INTR);
+}
+
+/* Disonnect confirm interrupt handler. */
+static void disconnect_confirm_intr (cycx_t *card, TX25Cmd *cmd)
+{
+ wan_device_t *wandev = &card->wandev;
+ struct device *dev;
+ u8 lcn;
+
+ cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn));
+ dprintk(KERN_INFO "%s: disconnect_confirm_intr:lcn=%d\n",
+ card->devname, lcn);
+ if ((dev = get_dev_by_lcn(wandev, lcn)) == NULL) {
+ /* Invalid channel, discard packet */
+ printk(KERN_INFO "%s:disconnect confirm not expected!:lcn %d\n",
+ card->devname, lcn);
+ return;
+ }
+
+ set_chan_state(dev, WAN_DISCONNECTED, IN_INTR);
+}
+
+/* disconnect interrupt handler. */
+static void disconnect_intr (cycx_t *card, TX25Cmd *cmd)
+{
+ wan_device_t *wandev = &card->wandev;
+ struct device *dev;
+ u8 lcn;
+
+ cycx_peek(&card->hw, cmd->buf, &lcn, sizeof(lcn));
+ dprintk(KERN_INFO "disconnect_intr:lcn=%d\n", lcn);
+ x25_disconnect_response(card, 0, lcn);
+
+ if ((dev = get_dev_by_lcn(wandev, lcn)) != NULL)
+ set_chan_state(dev, WAN_DISCONNECTED, IN_INTR);
+}
+
+/* LOG interrupt handler. */
+static void log_intr (cycx_t *card, TX25Cmd *cmd)
+{
+#if CYCLOMX_X25_DEBUG
+ char bf[20];
+ u16 size, toread, link, msg_code;
+ u8 code, routine;
+
+ cycx_peek(&card->hw, cmd->buf, &msg_code, sizeof(msg_code));
+ cycx_peek(&card->hw, cmd->buf + 2, &link, sizeof(link));
+ cycx_peek(&card->hw, cmd->buf + 4, &size, sizeof(size));
+ /* at most 20 bytes are available... thanx to Daniela :) */
+ toread = size < 20 ? size : 20;
+ cycx_peek(&card->hw, cmd->buf + 10, &bf, toread);
+ cycx_peek(&card->hw, cmd->buf + 10 + toread, &code, 1);
+ cycx_peek(&card->hw, cmd->buf + 10 + toread + 1, &routine, 1);
+
+ printk(KERN_INFO "cyx_isr: X25_LOG (0x4500) indic.:\n");
+ printk(KERN_INFO "cmd->buf=0x%X\n", cmd->buf);
+ printk(KERN_INFO "Log message code=0x%X\n", msg_code);
+ printk(KERN_INFO "Link=%d\n", link);
+ printk(KERN_INFO "log code=0x%X\n", code);
+ printk(KERN_INFO "log routine=0x%X\n", routine);
+ printk(KERN_INFO "Message size=%d\n", size);
+ hex_dump("Message", bf, toread);
+#endif
+}
+
+/* STATISTIC interrupt handler. */
+static void stat_intr (cycx_t *card, TX25Cmd *cmd)
+{
+ cycx_peek(&card->hw, cmd->buf, &card->u.x.stats,
+ sizeof(card->u.x.stats));
+ hex_dump("stat_intr", (unsigned char*)&card->u.x.stats,
+ sizeof(card->u.x.stats));
+ x25_dump_stats(&card->u.x.stats);
+ wake_up_interruptible(&card->wait_stats);
+}
+
+/* Spurious interrupt handler.
+ * o print a warning
+ * If number of spurious interrupts exceeded some limit, then ??? */
+static void spur_intr (cycx_t *card, TX25Cmd *cmd)
+{
+ printk(KERN_INFO "%s: spurious interrupt (0x%X)!\n",
+ card->devname, cmd->command);
+}
+#ifdef CYCLOMX_X25_DEBUG
+static void hex_dump(char *msg, unsigned char *p, int len)
+{
+ unsigned char hex[1024],
+ * phex = hex;
+
+ if (len >= (sizeof(hex) / 2))
+ len = (sizeof(hex) / 2) - 1;
+
+ while (len--) {
+ sprintf(phex, "%02x", *p++);
+ phex += 2;
+ }
+
+ printk(KERN_INFO "%s: %s\n", msg, hex);
+}
+#endif
+/* CYCLOM X Firmware-Specific Functions
+ *
+ * Almost all X.25 commands can unexpetedly fail due to so called 'X.25
+ * asynchronous events' such as restart, interrupt, incoming call request,
+ * call clear request, etc. They can't be ignored and have to be dealt with
+ * immediately. To tackle with this problem we execute each interface command
+ * in a loop until good return code is received or maximum number of retries
+ * is reached. Each interface command returns non-zero return code, an
+ * asynchronous event/error handler x25_error() is called.
+ */
+/* Exec x25 command. */
+static int x25_exec (cycx_t *card, int command, int link,
+ void *data1, int len1, void *data2, int len2)
+{
+ TX25Cmd c;
+ u32 addr = 0x1200 + 0x2E0 * link + 0x1E2;
+ int err = 0;
+
+ c.command = command;
+ c.link = link;
+ c.len = len1 + len2;
+
+ if (test_and_set_bit(0, (void*)&card->u.x.critical))
+ return -EAGAIN;
+
+ /* write command */
+ cycx_poke(&card->hw, X25_MBOX_OFFS, &c, sizeof(c) - sizeof(c.buf));
+
+ /* write x25 data */
+ if (data1) {
+ cycx_poke(&card->hw, addr, data1, len1);
+
+ if (data2)
+ if (len2 > 254) {
+ u32 addr1 = 0xA00 + 0x400 * link;
+
+ cycx_poke(&card->hw, addr + len1, data2, 249);
+ cycx_poke(&card->hw, addr1, ((u8*) data2) + 249,
+ len2 - 249);
+ } else
+ cycx_poke(&card->hw, addr + len1, data2, len2);
+ }
+
+ /* generate interruption, executing command */
+ cycx_intr(&card->hw);
+
+ /* wait till card->mbox == 0 */
+ err = cycx_exec(card->mbox);
+ card->u.x.critical = 0;
+
+ return err;
+}
+
+/* Configure adapter. */
+static int x25_configure (cycx_t *card, TX25Config *conf)
+{
+ struct {
+ u16 nlinks;
+ TX25Config conf[2];
+ } x25_cmd_conf;
+
+ memset (&x25_cmd_conf, 0, sizeof(x25_cmd_conf));
+ x25_cmd_conf.nlinks = 2;
+ x25_cmd_conf.conf[0] = *conf;
+ /* FIXME: we need to find a way in the wanrouter framework
+ to configure the second link, for now lets use it
+ with the same config from the first link, fixing
+ the interface type to RS232, the speed in 38400 and
+ the clock to external */
+ x25_cmd_conf.conf[1] = *conf;
+ x25_cmd_conf.conf[1].link = 1;
+ x25_cmd_conf.conf[1].speed = 5; /* 38400 */
+ x25_cmd_conf.conf[1].clock = 8;
+ x25_cmd_conf.conf[1].flags = 0; /* default = RS232 */
+
+ x25_dump_config(&x25_cmd_conf.conf[0]);
+ x25_dump_config(&x25_cmd_conf.conf[1]);
+
+ return x25_exec(card, X25_CONFIG, 0,
+ &x25_cmd_conf, sizeof(x25_cmd_conf), NULL, 0);
+}
+
+/* Get protocol statistics. */
+static int x25_get_stats (cycx_t *card)
+{
+ /* the firmware expects 20 in the size field!!!
+ thanx to Daniela */
+ int err = x25_exec(card, X25_STATISTIC, 0, NULL, 20, NULL, 0);
+
+ if (err)
+ return err;
+
+ interruptible_sleep_on(&card->wait_stats);
+
+ if (signal_pending(current))
+ return -EINTR;
+
+ card->wandev.stats.rx_packets = card->u.x.stats.n2_rx_frames;
+ card->wandev.stats.rx_over_errors = card->u.x.stats.rx_over_errors;
+ card->wandev.stats.rx_crc_errors = card->u.x.stats.rx_crc_errors;
+ card->wandev.stats.rx_length_errors = 0; /* not available from fw */
+ card->wandev.stats.rx_frame_errors = 0; /* not available from fw */
+ card->wandev.stats.rx_missed_errors = card->u.x.stats.rx_aborts;
+ card->wandev.stats.rx_dropped = 0; /* not available from fw */
+ card->wandev.stats.rx_errors = 0; /* not available from fw */
+ card->wandev.stats.tx_packets = card->u.x.stats.n2_tx_frames;
+ card->wandev.stats.tx_aborted_errors = card->u.x.stats.tx_aborts;
+ card->wandev.stats.tx_dropped = 0; /* not available from fw */
+ card->wandev.stats.collisions = 0; /* not available from fw */
+ card->wandev.stats.tx_errors = 0; /* not available from fw */
+
+ x25_dump_devs(&card->wandev);
+ return 0;
+}
+
+/* return the number of nibbles */
+static int byte_to_nibble(u8 *s, u8 *d, char *nibble)
+{
+ int i = 0;
+
+ if (*nibble && *s) {
+ d[i] |= *s++ - '0';
+ *nibble = 0;
+ ++i;
+ }
+
+ while (*s) {
+ d[i] = (*s - '0') << 4;
+ if (*(s + 1))
+ d[i] |= *(s + 1) - '0';
+ else {
+ *nibble = 1;
+ break;
+ }
+ ++i;
+ s += 2;
+ }
+
+ return i;
+}
+
+static void nibble_to_byte(u8 *s, u8 *d, u8 len, u8 nibble)
+{
+ if (nibble) {
+ *d++ = '0' + (*s++ & 0x0F);
+ --len;
+ }
+
+ while (len) {
+ *d++ = '0' + (*s >> 4);
+ if (--len) {
+ *d++ = '0' + (*s & 0x0F);
+ --len;
+ } else break;
+
+ ++s;
+ }
+
+ *d = '\0';
+}
+
+/* Place X.25 call. */
+static int x25_place_call (cycx_t *card, x25_channel_t *chan)
+{
+ int err = 0,
+ retry = MAX_CMD_RETRY,
+ len;
+ char data[64],
+ nibble = 0,
+ mylen = chan->local_addr ? strlen(chan->local_addr) : 0,
+ remotelen = strlen(chan->addr);
+ u8 key;
+
+ if (card->u.x.connection_keys == ~0UL) {
+ printk(KERN_INFO "%s: too many simultaneous connection "
+ "requests!\n", card->devname);
+ return -EAGAIN;
+ }
+
+ key = ffz(card->u.x.connection_keys);
+ set_bit(key, (void*)&card->u.x.connection_keys);
+ ++key;
+ dprintk(KERN_INFO "%s:x25_place_call:key=%d\n", card->devname, key);
+ memset(data, 0, sizeof(data));
+ data[1] = key; /* user key */
+ data[2] = 0x10;
+ data[4] = 0x0B;
+
+ len = byte_to_nibble(chan->addr, data + 6, &nibble);
+ len += chan->local_addr ? byte_to_nibble(chan->local_addr,
+ data + 6 + len, &nibble) : 0;
+ if (nibble)
+ ++len;
+ data[5] = mylen << 4 | remotelen;
+ data[6 + len + 1] = 0xCC; /* TCP/IP over X.25, thanx to Daniela :) */
+
+ do err = x25_exec(card, X25_CONNECT_REQUEST, chan->link,
+ &data, 7 + len + 1, NULL, 0);
+ while (err && retry--);
+
+ if (err)
+ clear_bit(--key, (void*)&card->u.x.connection_keys);
+ else {
+ chan->lcn = -key;
+ chan->protocol = ETH_P_IP;
+ }
+
+ return err;
+}
+
+/* Place X.25 CONNECT RESPONSE. */
+static int x25_connect_response (cycx_t *card, x25_channel_t *chan)
+{
+ int err = 0,
+ retry = MAX_CMD_RETRY;
+ char data[32];
+
+ memset(data, 0, sizeof(data));
+ data[0] = data[3] = chan->lcn;
+ data[2] = 0x10;
+ data[4] = 0x0F;
+ data[7] = 0xCC; /* TCP/IP over X.25, thanx Daniela */
+
+ do err = x25_exec(card, X25_CONNECT_RESPONSE, chan->link,
+ &data, 8, NULL, 0);
+ while (err && retry--);
+
+ return err;
+}
+
+/* Place X.25 DISCONNECT RESPONSE. */
+static int x25_disconnect_response (cycx_t *card, u8 link, u8 lcn)
+{
+ int err = 0,
+ retry = MAX_CMD_RETRY;
+ char data[5];
+
+ memset(data, 0, sizeof(data));
+ data[0] = data[3] = lcn;
+ data[2] = 0x10;
+ data[4] = 0x17;
+ do err = x25_exec(card, X25_DISCONNECT_RESPONSE, link,
+ &data, 5, NULL, 0);
+ while (err && retry--);
+
+ return err;
+}
+
+/* Clear X.25 call. */
+static int x25_clear_call (cycx_t *card, u8 link, u8 lcn, u8 cause, u8 diagn)
+{
+ int retry = MAX_CMD_RETRY,
+ err;
+ u8 data[7];
+
+ memset(data, 0, sizeof(data));
+ data[0] = data[3] = lcn;
+ data[2] = 0x10;
+ data[4] = 0x13;
+ data[5] = cause;
+ data[6] = diagn;
+
+ do err = x25_exec(card, X25_DISCONNECT_REQUEST, link, data, 7, NULL, 0);
+ while (err && retry--);
+
+ return err;
+}
+
+/* Send X.25 data packet. */
+static int x25_send (cycx_t *card, u8 link, u8 lcn, u8 bitm, int len, void *buf)
+{
+ int err = 0,
+ retry = MAX_CMD_RETRY;
+ u8 data[] = "?\xFF\x10??";
+
+ data[0] = data[3] = lcn;
+ data[4] = bitm;
+
+ do err = x25_exec(card, X25_DATA_REQUEST, link, &data, 5, buf, len);
+ while (err && retry--);
+
+ return err;
+}
+
+/* Miscellaneous */
+/* Find network device by its channel number. */
+static struct device *get_dev_by_lcn (wan_device_t *wandev, s16 lcn)
+{
+ struct device *dev = wandev->dev;
+
+ for (; dev; dev = dev->slave)
+ if (((x25_channel_t*)dev->priv)->lcn == lcn)
+ break;
+ return dev;
+}
+
+/* Find network device by its remote dte address. */
+static struct device *get_dev_by_dte_addr (wan_device_t *wandev, char *dte)
+{
+ struct device *dev = wandev->dev;
+
+ for (; dev; dev = dev->slave)
+ if (!strcmp (((x25_channel_t*)dev->priv)->addr, dte))
+ break;
+ return dev;
+}
+
+/* Initiate connection on the logical channel.
+ * o for PVC we just get channel configuration
+ * o for SVCs place an X.25 call
+ *
+ * Return: 0 connected
+ * >0 connection in progress
+ * <0 failure */
+static int chan_connect (struct device *dev)
+{
+ x25_channel_t *chan = dev->priv;
+ cycx_t *card = chan->card;
+
+ if (chan->svc) {
+ if (!chan->addr[0])
+ return -EINVAL; /* no destination address */
+ dprintk(KERN_INFO "%s: placing X.25 call to %s...\n",
+ card->devname, chan->addr);
+ if (x25_place_call(card, chan))
+ return -EIO;
+ set_chan_state(dev, WAN_CONNECTING, OUT_INTR);
+ return 1;
+ } else
+ set_chan_state(dev, WAN_CONNECTED, OUT_INTR);
+
+ return 0;
+}
+
+/* Disconnect logical channel.
+ * o if SVC then clear X.25 call */
+static void chan_disc (struct device *dev)
+{
+ x25_channel_t *chan = dev->priv;
+
+ if (chan->svc) {
+ x25_clear_call(chan->card, chan->link, chan->lcn, 0, 0);
+ set_chan_state(dev, WAN_DISCONNECTING, OUT_INTR);
+ } else
+ set_chan_state(dev, WAN_DISCONNECTED, OUT_INTR);
+}
+
+/* Called by kernel timer */
+static void chan_timer (unsigned long data)
+{
+ struct device *dev = (struct device*) data;
+ x25_channel_t *chan = dev->priv;
+
+ switch (chan->state) {
+ case WAN_CONNECTED:
+ chan_disc(dev);
+ break;
+ default:
+ printk (KERN_ERR "%s: chan_timer for svc (%s) not "
+ "connected!\n",
+ chan->card->devname, dev->name);
+ }
+}
+
+/* Set logical channel state. */
+static void set_chan_state (struct device *dev, u8 state, u8 outside_intr)
+{
+ x25_channel_t *chan = dev->priv;
+ cycx_t *card = chan->card;
+ u32 flags = 0;
+
+ if (outside_intr)
+ spin_lock(&card->lock);
+ else
+ spin_lock_irqsave(&card->lock, flags);
+
+ if (chan->state != state) {
+ if (chan->svc && chan->state == WAN_CONNECTED)
+ del_timer(&chan->timer);
+
+ switch (state) {
+ case WAN_CONNECTED:
+ printk (KERN_INFO "%s: interface %s "
+ "connected!\n",
+ card->devname, dev->name);
+ *(u16*)dev->dev_addr = htons(chan->lcn);
+ dev->tbusy = 0;
+ reset_timer(dev);
+ break;
+
+ case WAN_CONNECTING:
+ printk (KERN_INFO "%s: interface %s "
+ "connecting...\n",
+ card->devname, dev->name);
+ break;
+
+ case WAN_DISCONNECTING:
+ printk (KERN_INFO "%s: interface %s "
+ "disconnecting...\n",
+ card->devname, dev->name);
+ break;
+
+ case WAN_DISCONNECTED:
+ printk (KERN_INFO "%s: interface %s "
+ "disconnected!\n",
+ card->devname, dev->name);
+ if (chan->svc) {
+ *(unsigned short*)dev->dev_addr = 0;
+ chan->lcn = 0;
+ }
+ break;
+ }
+
+ chan->state = state;
+ }
+
+ if (outside_intr)
+ spin_unlock(&card->lock);
+ else
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+/* Send packet on a logical channel.
+ * When this function is called, tx_skb field of the channel data space
+ * points to the transmit socket buffer. When transmission is complete,
+ * release socket buffer and reset 'tbusy' flag.
+ *
+ * Return: 0 - transmission complete
+ * 1 - busy
+ *
+ * Notes:
+ * 1. If packet length is greater than MTU for this channel, we'll fragment
+ * the packet into 'complete sequence' using M-bit.
+ * 2. When transmission is complete, an event notification should be issued
+ * to the router. */
+static int chan_send (struct device *dev, struct sk_buff *skb)
+{
+ x25_channel_t *chan = dev->priv;
+ cycx_t *card = chan->card;
+ int bitm = 0; /* final packet */
+ unsigned len = skb->len;
+
+ if (skb->len > card->wandev.mtu) {
+ len = card->wandev.mtu;
+ bitm = 0x10; /* set M-bit (more data) */
+ }
+
+ if (x25_send(card, chan->link, chan->lcn, bitm, len, skb->data))
+ return 1;
+
+ if (bitm) {
+ skb_pull(skb, len);
+ return 1;
+ }
+
+ ++chan->ifstats.tx_packets;
+ chan->ifstats.tx_bytes += len;
+ return 0;
+}
+
+/* Convert line speed in bps to a number used by cyclom 2x code. */
+static u8 bps_to_speed_code (u32 bps)
+{
+ u8 number = 0; /* defaults to the lowest (1200) speed ;> */
+
+ if (bps >= 512000) number = 8;
+ else if (bps >= 256000) number = 7;
+ else if (bps >= 64000) number = 6;
+ else if (bps >= 38400) number = 5;
+ else if (bps >= 19200) number = 4;
+ else if (bps >= 9600) number = 3;
+ else if (bps >= 4800) number = 2;
+ else if (bps >= 2400) number = 1;
+
+ return number;
+}
+
+/* log base 2 */
+static u8 log2 (u32 n)
+{
+ u8 log = 0;
+
+ if (!n)
+ return 0;
+
+ while (n > 1) {
+ n >>= 1;
+ ++log;
+ }
+
+ return log;
+}
+
+/* Convert decimal string to unsigned integer.
+ * If len != 0 then only 'len' characters of the string are converted. */
+static unsigned dec_to_uint (u8 *str, int len)
+{
+ unsigned val = 0;
+
+ if (!len)
+ len = strlen(str);
+
+ for (; len && is_digit(*str); ++str, --len)
+ val = (val * 10) + (*str - (unsigned)'0');
+
+ return val;
+}
+
+static void reset_timer(struct device *dev)
+{
+ x25_channel_t *chan = dev->priv;
+
+ if (!chan->svc)
+ return;
+
+ del_timer(&chan->timer);
+ chan->timer.expires = jiffies + chan->idle_tmout * HZ;
+ add_timer(&chan->timer);
+}
+#ifdef CYCLOMX_X25_DEBUG
+static void x25_dump_config(TX25Config *conf)
+{
+ printk (KERN_INFO "x25 configuration\n");
+ printk (KERN_INFO "-----------------\n");
+ printk (KERN_INFO "link number=%d\n", conf->link);
+ printk (KERN_INFO "line speed=%d\n", conf->speed);
+ printk (KERN_INFO "clock=%sternal\n", conf->clock == 8 ? "Ex" : "In");
+ printk (KERN_INFO "# level 2 retransm.=%d\n", conf->n2);
+ printk (KERN_INFO "level 2 window=%d\n", conf->n2win);
+ printk (KERN_INFO "level 3 window=%d\n", conf->n3win);
+ printk (KERN_INFO "# logical channels=%d\n", conf->nvc);
+ printk (KERN_INFO "level 3 pkt len=%d\n", conf->pktlen);
+ printk (KERN_INFO "my address=%d\n", conf->locaddr);
+ printk (KERN_INFO "remote address=%d\n", conf->remaddr);
+ printk (KERN_INFO "t1=%d seconds\n", conf->t1);
+ printk (KERN_INFO "t2=%d seconds\n", conf->t2);
+ printk (KERN_INFO "t21=%d seconds\n", conf->t21);
+ printk (KERN_INFO "# PVCs=%d\n", conf->npvc);
+ printk (KERN_INFO "t23=%d seconds\n", conf->t23);
+ printk (KERN_INFO "flags=0x%x\n", conf->flags);
+}
+
+static void x25_dump_stats(TX25Stats *stats)
+{
+ printk (KERN_INFO "x25 statistics\n");
+ printk (KERN_INFO "--------------\n");
+ printk (KERN_INFO "rx_crc_errors=%d\n", stats->rx_crc_errors);
+ printk (KERN_INFO "rx_over_errors=%d\n", stats->rx_over_errors);
+ printk (KERN_INFO "n2_tx_frames=%d\n", stats->n2_tx_frames);
+ printk (KERN_INFO "n2_rx_frames=%d\n", stats->n2_rx_frames);
+ printk (KERN_INFO "tx_timeouts=%d\n", stats->tx_timeouts);
+ printk (KERN_INFO "rx_timeouts=%d\n", stats->rx_timeouts);
+ printk (KERN_INFO "n3_tx_packets=%d\n", stats->n3_tx_packets);
+ printk (KERN_INFO "n3_rx_packets=%d\n", stats->n3_rx_packets);
+ printk (KERN_INFO "tx_aborts=%d\n", stats->tx_aborts);
+ printk (KERN_INFO "rx_aborts=%d\n", stats->rx_aborts);
+}
+
+static void x25_dump_devs(wan_device_t *wandev)
+{
+ struct device *dev = wandev->dev;
+
+ printk (KERN_INFO "x25 dev states\n");
+ printk (KERN_INFO "name: addr: tbusy:\n");
+ printk (KERN_INFO "----------------------------\n");
+
+ for (; dev; dev = dev->slave) {
+ x25_channel_t *chan = dev->priv;
+
+ printk (KERN_INFO "%-5.5s %-15.15s %ld\n",
+ chan->name, chan->addr, dev->tbusy);
+ }
+}
+
+#endif /* CYCLOMX_X25_DEBUG */
+/* End */
diff --git a/drivers/net/irda/litelink.c b/drivers/net/irda/litelink.c
new file mode 100644
index 000000000..84df367da
--- /dev/null
+++ b/drivers/net/irda/litelink.c
@@ -0,0 +1,206 @@
+/*********************************************************************
+ *
+ * Filename: litelink.c
+ * Version: 1.0
+ * Description: Driver for the Parallax LiteLink dongle
+ * Status: Stable
+ * Author: Dag Brattli <dagb@cs.uit.no>
+ * Created at: Fri May 7 12:50:33 1999
+ * Modified at: Wed May 19 07:25:15 1999
+ * Modified by: Dag Brattli <dagb@cs.uit.no>
+ *
+ * Copyright (c) 1999 Dag Brattli, All Rights Reserved.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ ********************************************************************/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+#include <net/irda/irda_device.h>
+#include <net/irda/dongle.h>
+
+#define MIN_DELAY 25 /* 15 us, but wait a little more to be sure */
+#define MAX_DELAY 10000 /* 1 ms */
+
+static void litelink_open(struct irda_device *idev, int type);
+static void litelink_close(struct irda_device *dev);
+static void litelink_change_speed(struct irda_device *dev, int baudrate);
+static void litelink_reset(struct irda_device *dev);
+static void litelink_init_qos(struct irda_device *idev, struct qos_info *qos);
+
+/* These are the baudrates supported */
+static int baud_rates[] = { 115200, 57600, 38400, 19200, 9600 };
+
+static struct dongle dongle = {
+ LITELINK_DONGLE,
+ litelink_open,
+ litelink_close,
+ litelink_reset,
+ litelink_change_speed,
+ litelink_init_qos,
+};
+
+__initfunc(int litelink_init(void))
+{
+ return irda_device_register_dongle(&dongle);
+}
+
+void litelink_cleanup(void)
+{
+ irda_device_unregister_dongle(&dongle);
+}
+
+static void litelink_open(struct irda_device *idev, int type)
+{
+ strcat(idev->description, " <-> litelink");
+
+ idev->io.dongle_id = type;
+ idev->flags |= IFF_DONGLE;
+
+ MOD_INC_USE_COUNT;
+}
+
+static void litelink_close(struct irda_device *idev)
+{
+ /* Power off dongle */
+ irda_device_set_dtr_rts(idev, FALSE, FALSE);
+
+ MOD_DEC_USE_COUNT;
+}
+
+/*
+ * Function litelink_change_speed (tty, baud)
+ *
+ * Change speed of the Litelink dongle. To cycle through the available
+ * baud rates, pulse RTS low for a few ms.
+ */
+static void litelink_change_speed(struct irda_device *idev, int baudrate)
+{
+ int i;
+
+ ASSERT(idev != NULL, return;);
+ ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ /* Clear RTS to reset dongle */
+ irda_device_set_dtr_rts(idev, TRUE, FALSE);
+
+ /* Sleep a minimum of 15 us */
+ udelay(MIN_DELAY);
+
+ /* Go back to normal mode */
+ irda_device_set_dtr_rts(idev, TRUE, TRUE);
+
+ /* Sleep a minimum of 15 us */
+ udelay(MIN_DELAY);
+
+ /* Cycle through avaiable baudrates until we reach the correct one */
+ for (i=0; i<5 && baud_rates[i] != baudrate; i++) {
+
+ /* Set DTR, clear RTS */
+ irda_device_set_dtr_rts(idev, FALSE, TRUE);
+
+ /* Sleep a minimum of 15 us */
+ udelay(MIN_DELAY);
+
+ /* Set DTR, Set RTS */
+ irda_device_set_dtr_rts(idev, TRUE, TRUE);
+
+ /* Sleep a minimum of 15 us */
+ udelay(MIN_DELAY);
+ }
+}
+
+/*
+ * Function litelink_reset (dev)
+ *
+ * Reset the Litelink type dongle. Warning, this function must only be
+ * called with a process context!
+ *
+ */
+static void litelink_reset(struct irda_device *idev)
+{
+ ASSERT(idev != NULL, return;);
+ ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ /* Power on dongle */
+ irda_device_set_dtr_rts(idev, TRUE, TRUE);
+
+ /* Sleep a minimum of 15 us */
+ udelay(MIN_DELAY);
+
+ /* Clear RTS to reset dongle */
+ irda_device_set_dtr_rts(idev, TRUE, FALSE);
+
+ /* Sleep a minimum of 15 us */
+ udelay(MIN_DELAY);
+
+ /* Go back to normal mode */
+ irda_device_set_dtr_rts(idev, TRUE, TRUE);
+
+ /* Sleep a minimum of 15 us */
+ udelay(MIN_DELAY);
+
+ /* This dongles speed defaults to 115200 bps */
+ idev->qos.baud_rate.value = 115200;
+}
+
+/*
+ * Function litelink_init_qos (qos)
+ *
+ * Initialize QoS capabilities
+ *
+ */
+static void litelink_init_qos(struct irda_device *idev, struct qos_info *qos)
+{
+ qos->baud_rate.bits &= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
+ qos->min_turn_time.bits &= 0x40; /* Needs 0.01 ms */
+}
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
+MODULE_DESCRIPTION("Parallax Litelink dongle driver");
+
+/*
+ * Function init_module (void)
+ *
+ * Initialize Litelink module
+ *
+ */
+int init_module(void)
+{
+ return litelink_init();
+}
+
+/*
+ * Function cleanup_module (void)
+ *
+ * Cleanup Litelink module
+ *
+ */
+void cleanup_module(void)
+{
+ litelink_cleanup();
+}
+
+#endif
diff --git a/drivers/net/irda/smc-ircc.c b/drivers/net/irda/smc-ircc.c
new file mode 100644
index 000000000..4daa5f7e2
--- /dev/null
+++ b/drivers/net/irda/smc-ircc.c
@@ -0,0 +1,969 @@
+/*********************************************************************
+ *
+ * Filename: smc-ircc.c
+ * Version: 0.1
+ * Description: Driver for the SMC Infrared Communications Controller (SMC)
+ * Status: Experimental.
+ * Author: Thomas Davis (tadavis@jps.net)
+ * Created at:
+ * Modified at: Wed May 19 15:30:08 1999
+ * Modified by: Dag Brattli <dagb@cs.uit.no>
+ *
+ * Copyright (c) 1998-1999 Thomas Davis, All Rights Reserved.
+ *
+ * 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.
+ *
+ * I, Thomas Davis, admit no liability nor provide warranty for any
+ * of this software. This material is provided "AS-IS" and at no charge.
+ *
+ * Applicable Models : Fujitsu Lifebook 635t
+ * Sony PCG-505TX (gets DMA wrong.)
+ *
+ ********************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/byteorder.h>
+
+#include <net/irda/wrapper.h>
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+#include <net/irda/irlap_frame.h>
+#include <net/irda/irda_device.h>
+
+#include <net/irda/smc-ircc.h>
+#include <net/irda/irport.h>
+
+static char *driver_name = "smc-ircc";
+
+#define CHIP_IO_EXTENT 8
+
+static unsigned int io[] = { 0x2e8, 0x140, ~0, ~0 };
+static unsigned int io2[] = { 0x2f8, 0x3e8, 0, 0};
+
+static struct ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL};
+
+/* Some prototypes */
+static int ircc_open( int i, unsigned int iobase, unsigned int board_addr);
+static int ircc_close( struct irda_device *idev);
+static int ircc_probe( int iobase, int board_addr);
+static int ircc_dma_receive( struct irda_device *idev);
+static int ircc_dma_receive_complete(struct irda_device *idev, int iobase);
+static int ircc_hard_xmit( struct sk_buff *skb, struct device *dev);
+static void ircc_dma_write( struct irda_device *idev, int iobase);
+static void ircc_change_speed( struct irda_device *idev, int baud);
+static void ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void ircc_wait_until_sent( struct irda_device *idev);
+static int ircc_is_receiving( struct irda_device *idev);
+
+static int ircc_net_init( struct device *dev);
+static int ircc_net_open( struct device *dev);
+static int ircc_net_close( struct device *dev);
+
+static int ircc_debug=3;
+static int ircc_irq=255;
+static int ircc_dma=255;
+
+static inline void register_bank(int port, int bank)
+{
+ outb(((inb(port+UART_MASTER) & 0xF0) | (bank & 0x07)),
+ port+UART_MASTER);
+}
+
+static inline unsigned int serial_in(int port, int offset)
+{
+ return inb(port+offset);
+}
+
+static inline void serial_out(int port, int offset, int value)
+{
+ outb(value, port+offset);
+}
+
+/*
+ * Function ircc_init ()
+ *
+ * Initialize chip. Just try to find out how many chips we are dealing with
+ * and where they are
+ */
+__initfunc(int ircc_init(void))
+{
+ int i;
+
+ DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+ for ( i=0; (io[i] < 2000) && (i < 4); i++) {
+ int ioaddr = io[i];
+ if (check_region(ioaddr, CHIP_IO_EXTENT))
+ continue;
+ if (ircc_open( i, io[i], io2[i]) == 0)
+ return 0;
+ }
+ DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+
+ return -ENODEV;
+}
+
+/*
+ * Function ircc_cleanup ()
+ *
+ * Close all configured chips
+ *
+ */
+#ifdef MODULE
+static void ircc_cleanup(void)
+{
+ int i;
+
+ DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+ for ( i=0; i < 4; i++) {
+ if ( dev_self[i])
+ ircc_close( &(dev_self[i]->idev));
+ }
+ DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+}
+#endif /* MODULE */
+
+/*
+ * Function ircc_open (iobase, irq)
+ *
+ * Open driver instance
+ *
+ */
+static int ircc_open( int i, unsigned int iobase, unsigned int iobase2)
+{
+ struct ircc_cb *self;
+ struct irda_device *idev;
+ int ret;
+ int config;
+
+ DEBUG( ircc_debug, __FUNCTION__ " -->\n");
+
+ if ((config = ircc_probe( iobase, iobase2)) == -1) {
+ DEBUG(ircc_debug,
+ __FUNCTION__ ": addr 0x%04x - no device found!\n", iobase);
+ return -1;
+ }
+
+ /*
+ * Allocate new instance of the driver
+ */
+ self = kmalloc( sizeof(struct ircc_cb), GFP_KERNEL);
+ if ( self == NULL) {
+ printk( KERN_ERR "IrDA: Can't allocate memory for "
+ "IrDA control block!\n");
+ return -ENOMEM;
+ }
+ memset(self, 0, sizeof(struct ircc_cb));
+
+ /* Need to store self somewhere */
+ dev_self[i] = self;
+
+ idev = &self->idev;
+
+ /* Initialize IO */
+ idev->io.iobase = iobase;
+ idev->io.iobase2 = iobase2; /* Used by irport */
+ idev->io.irq = config >> 4 & 0x0f;
+ if (ircc_irq < 255) {
+ printk(KERN_INFO "smc: Overriding IRQ - chip says %d, using %d\n",
+ idev->io.irq, ircc_irq);
+ idev->io.irq = ircc_irq;
+ }
+ idev->io.io_ext = CHIP_IO_EXTENT;
+ idev->io.io_ext2 = 8; /* Used by irport */
+ idev->io.dma = config & 0x0f;
+ if (ircc_dma < 255) {
+ printk(KERN_INFO "smc: Overriding DMA - chip says %d, using %d\n",
+ idev->io.dma, ircc_dma);
+ idev->io.dma = ircc_dma;
+ }
+ idev->io.fifo_size = 16;
+
+ /* Lock the port that we need */
+ ret = check_region( idev->io.iobase, idev->io.io_ext);
+ if ( ret < 0) {
+ DEBUG( 0, __FUNCTION__ ": can't get iobase of 0x%03x\n",
+ idev->io.iobase);
+ /* ircc_cleanup( self->idev); */
+ return -ENODEV;
+ }
+ ret = check_region( idev->io.iobase2, idev->io.io_ext2);
+ if ( ret < 0) {
+ DEBUG( 0, __FUNCTION__ ": can't get iobase of 0x%03x\n",
+ idev->io.iobase2);
+ /* ircc_cleanup( self->idev); */
+ return -ENODEV;
+ }
+ request_region( idev->io.iobase, idev->io.io_ext, idev->name);
+ request_region( idev->io.iobase2, idev->io.io_ext2, idev->name);
+
+ /* Initialize QoS for this device */
+ irda_init_max_qos_capabilies( &idev->qos);
+
+#if 1
+ /* The only value we must override it the baudrate */
+ idev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
+ IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8);
+#else
+ /* The only value we must override it the baudrate */
+ idev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
+ IR_115200;
+#endif
+
+ idev->qos.min_turn_time.bits = 0x07;
+ irda_qos_bits_to_value( &idev->qos);
+
+ idev->flags = IFF_FIR|IFF_SIR|IFF_DMA|IFF_PIO;
+
+ /* Specify which buffer allocation policy we need */
+ idev->rx_buff.flags = GFP_KERNEL | GFP_DMA;
+ idev->tx_buff.flags = GFP_KERNEL | GFP_DMA;
+
+ /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */
+ idev->rx_buff.truesize = 4000;
+ idev->tx_buff.truesize = 4000;
+
+ /* Initialize callbacks */
+ idev->change_speed = ircc_change_speed;
+ idev->wait_until_sent = ircc_wait_until_sent;
+ idev->is_receiving = ircc_is_receiving;
+
+ /* Override the network functions we need to use */
+ idev->netdev.init = ircc_net_init;
+ idev->netdev.hard_start_xmit = ircc_hard_xmit;
+ idev->netdev.open = ircc_net_open;
+ idev->netdev.stop = ircc_net_close;
+
+ irport_start(idev, iobase2);
+
+ /* Open the IrDA device */
+ irda_device_open( idev, driver_name, self);
+
+ DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+ return 0;
+}
+
+/*
+ * Function ircc_close (idev)
+ *
+ * Close driver instance
+ *
+ */
+static int ircc_close( struct irda_device *idev)
+{
+ int iobase;
+
+ DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+ ASSERT( idev != NULL, return -1;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+
+ iobase = idev->io.iobase;
+
+ irport_stop(idev, idev->io.iobase2);
+
+ register_bank(iobase, 0);
+ serial_out(iobase, UART_IER, 0);
+ serial_out(iobase, UART_MASTER, UART_MASTER_RESET);
+
+ register_bank(iobase, 1);
+
+ serial_out(iobase, UART_SCE_CFGA,
+ UART_CFGA_IRDA_SIR_A | UART_CFGA_TX_POLARITY);
+ serial_out(iobase, UART_SCE_CFGB, UART_CFGB_IR);
+
+ /* Release the PORT that this driver is using */
+ DEBUG( ircc_debug,
+ __FUNCTION__ ": releasing 0x%03x\n", idev->io.iobase);
+
+ release_region( idev->io.iobase, idev->io.io_ext);
+
+ if ( idev->io.iobase2) {
+ DEBUG( ircc_debug, __FUNCTION__ ": releasing 0x%03x\n",
+ idev->io.iobase2);
+ release_region( idev->io.iobase2, idev->io.io_ext2);
+ }
+
+ irda_device_close( idev);
+
+ DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+ return 0;
+}
+
+/*
+ * Function ircc_probe (iobase, board_addr, irq, dma)
+ *
+ * Returns non-negative on success.
+ *
+ */
+static int ircc_probe( int iobase, int iobase2)
+{
+ int version = 1;
+ int low, high, chip, config, dma, irq;
+
+ DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+ register_bank(iobase, 3);
+ high = serial_in(iobase, UART_ID_HIGH);
+ low = serial_in(iobase, UART_ID_LOW);
+ chip = serial_in(iobase, UART_CHIP_ID);
+ version = serial_in(iobase, UART_VERSION);
+ config = serial_in(iobase, UART_INTERFACE);
+ irq = config >> 4 & 0x0f;
+ dma = config & 0x0f;
+
+ if (high == 0x10 && low == 0xb8 && chip == 0xf1) {
+ DEBUG(0, "SMC IrDA Controller found; version = %d, "
+ "port 0x%04x, dma %d, interrupt %d\n",
+ version, iobase, dma, irq);
+ } else {
+ return -1;
+ }
+
+ serial_out(iobase, UART_MASTER, 0);
+
+ DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+
+ return config;
+}
+
+/*
+ * Function ircc_change_speed (idev, baud)
+ *
+ * Change the speed of the device
+ *
+ */
+static void ircc_change_speed( struct irda_device *idev, int speed)
+{
+ struct ircc_cb *self;
+ int iobase, ir_mode, select, fast;
+
+ DEBUG(ircc_debug+1, __FUNCTION__ " -->\n");
+
+ ASSERT(idev != NULL, return;);
+ ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ self = idev->priv;
+ iobase = idev->io.iobase;
+
+ /* Update accounting for new speed */
+ idev->io.baudrate = speed;
+
+ switch ( speed) {
+ case 9600:
+ case 19200:
+ case 37600:
+ case 57600:
+ case 115200:
+ DEBUG(ircc_debug+1,
+ __FUNCTION__ ": using irport to change speed to %d\n",
+ speed);
+ register_bank(iobase, 0);
+ serial_out(iobase, UART_IER, 0);
+ serial_out(iobase, UART_MASTER, UART_MASTER_RESET);
+ serial_out(iobase, UART_MASTER, UART_MASTER_INT_EN);
+ irport_start(idev, idev->io.iobase2);
+ irport_change_speed( idev, speed);
+ return;
+ break;
+
+ case 576000:
+ ir_mode = UART_CFGA_IRDA_HDLC;
+ select = 0;
+ fast = 0;
+ DEBUG( ircc_debug, __FUNCTION__ ": handling baud of 576000\n");
+ break;
+ case 1152000:
+ ir_mode = UART_CFGA_IRDA_HDLC;
+ select = UART_1152;
+ fast = 0;
+ DEBUG(ircc_debug, __FUNCTION__ ": handling baud of 1152000\n");
+ break;
+ case 4000000:
+ ir_mode = UART_CFGA_IRDA_4PPM;
+ select = 0;
+ fast = UART_LCR_A_FAST;
+ DEBUG(ircc_debug, __FUNCTION__ ": handling baud of 4000000\n");
+ break;
+ default:
+ DEBUG( 0, __FUNCTION__ ": unknown baud rate of %d\n", speed);
+ return;
+ }
+
+#if 0
+ serial_out(idev->io.iobase2, 4, 0x08);
+#endif
+
+ serial_out(iobase, UART_MASTER, UART_MASTER_RESET);
+
+ register_bank(iobase, 0);
+ serial_out(iobase, UART_IER, 0);
+
+ irport_stop(idev, idev->io.iobase2);
+
+ idev->netdev.tbusy = 0;
+
+ register_bank(iobase, 1);
+
+ serial_out(iobase, UART_SCE_CFGA,
+ ((serial_in(iobase, UART_SCE_CFGA) & 0x87) | ir_mode));
+
+ serial_out(iobase, UART_SCE_CFGB,
+ ((serial_in(iobase, UART_SCE_CFGB) & 0x3f) | UART_CFGB_IR));
+
+ (void) serial_in(iobase, UART_FIFO_THRESHOLD);
+ serial_out(iobase, UART_FIFO_THRESHOLD, 64);
+
+ register_bank(iobase, 4);
+
+ serial_out(iobase, UART_CONTROL,
+ (serial_in(iobase, UART_CONTROL) & 0x30)
+ | select | UART_CRC );
+
+ register_bank(iobase, 0);
+
+ serial_out(iobase, UART_LCR_A, fast);
+
+ DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+}
+
+/*
+ * Function ircc_hard_xmit (skb, dev)
+ *
+ * Transmit the frame!
+ *
+ */
+static int ircc_hard_xmit( struct sk_buff *skb, struct device *dev)
+{
+ struct irda_device *idev;
+ int iobase;
+ int mtt;
+
+ DEBUG(ircc_debug+1, __FUNCTION__ " -->\n");
+ idev = (struct irda_device *) dev->priv;
+
+ ASSERT( idev != NULL, return 0;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+
+ iobase = idev->io.iobase;
+
+ DEBUG(ircc_debug+1, __FUNCTION__ "(%ld), skb->len=%d\n", jiffies, (int) skb->len);
+
+ /* Use irport for SIR speeds */
+ if (idev->io.baudrate <= 115200) {
+ DEBUG(ircc_debug+1, __FUNCTION__ ": calling irport_hard_xmit\n");
+ return irport_hard_xmit(skb, dev);
+ }
+
+ DEBUG(ircc_debug, __FUNCTION__ ": using dma; len=%d\n", skb->len);
+
+ /* Lock transmit buffer */
+ if (irda_lock((void *) &dev->tbusy) == FALSE)
+ return -EBUSY;
+
+ memcpy( idev->tx_buff.head, skb->data, skb->len);
+
+ /* Make sure that the length is a multiple of 16 bits */
+ if ( skb->len & 0x01)
+ skb->len++;
+
+ idev->tx_buff.len = skb->len;
+ idev->tx_buff.data = idev->tx_buff.head;
+#if 0
+ idev->tx_buff.offset = 0;
+#endif
+
+ mtt = irda_get_mtt( skb);
+
+ /* Use udelay for delays less than 50 us. */
+ if (mtt)
+ udelay( mtt);
+
+ ircc_dma_write( idev, iobase);
+
+ dev_kfree_skb( skb);
+
+ DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+ return 0;
+}
+
+/*
+ * Function ircc_dma_xmit (idev, iobase)
+ *
+ * Transmit data using DMA
+ *
+ */
+static void ircc_dma_write( struct irda_device *idev, int iobase)
+{
+ struct ircc_cb *self;
+
+ DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+ ASSERT( idev != NULL, return;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ self = idev->priv;
+ iobase = idev->io.iobase;
+
+ setup_dma( idev->io.dma, idev->tx_buff.data, idev->tx_buff.len,
+ DMA_MODE_WRITE);
+
+ idev->io.direction = IO_XMIT;
+
+ serial_out(idev->io.iobase2, 4, 0x08);
+
+ register_bank(iobase, 4);
+ serial_out(iobase, UART_CONTROL,
+ (serial_in(iobase, UART_CONTROL) & 0xF0));
+
+ serial_out(iobase, UART_BOF_COUNT_LO, 2);
+ serial_out(iobase, UART_BRICKWALL_CNT_LO, 0);
+#if 1
+ serial_out(iobase, UART_BRICKWALL_TX_CNT_HI, idev->tx_buff.len >> 8);
+ serial_out(iobase, UART_TX_SIZE_LO, idev->tx_buff.len & 0xff);
+#else
+ serial_out(iobase, UART_BRICKWALL_TX_CNT_HI, 0);
+ serial_out(iobase, UART_TX_SIZE_LO, 0);
+#endif
+
+ register_bank(iobase, 1);
+ serial_out(iobase, UART_SCE_CFGB,
+ serial_in(iobase, UART_SCE_CFGB) | UART_CFGB_DMA_ENABLE);
+
+ register_bank(iobase, 0);
+
+ serial_out(iobase, UART_IER, UART_IER_ACTIVE_FRAME | UART_IER_EOM);
+ serial_out(iobase, UART_LCR_B,
+ UART_LCR_B_SCE_TRANSMIT|UART_LCR_B_SIP_ENABLE);
+
+ serial_out(iobase, UART_MASTER, UART_MASTER_INT_EN);
+
+ DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+}
+
+/*
+ * Function ircc_dma_xmit_complete (idev)
+ *
+ * The transfer of a frame in finished. This function will only be called
+ * by the interrupt handler
+ *
+ */
+static void ircc_dma_xmit_complete( struct irda_device *idev, int underrun)
+{
+ struct ircc_cb *self;
+ int iobase, d;
+
+ DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+ ASSERT( idev != NULL, return;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ register_bank(idev->io.iobase, 1);
+
+ serial_out(idev->io.iobase, UART_SCE_CFGB,
+ serial_in(idev->io.iobase, UART_SCE_CFGB) &
+ ~UART_CFGB_DMA_ENABLE);
+
+ d = get_dma_residue(idev->io.dma);
+
+ DEBUG(ircc_debug, __FUNCTION__ ": dma residue = %d, len=%d, sent=%d\n",
+ d, idev->tx_buff.len, idev->tx_buff.len - d);
+
+ self = idev->priv;
+
+ iobase = idev->io.iobase;
+
+ /* Check for underrrun! */
+ if ( underrun) {
+ idev->stats.tx_errors++;
+ idev->stats.tx_fifo_errors++;
+ } else {
+ idev->stats.tx_packets++;
+ idev->stats.tx_bytes += idev->tx_buff.len;
+ }
+
+ /* Unlock tx_buff and request another frame */
+ idev->netdev.tbusy = 0; /* Unlock */
+ idev->media_busy = FALSE;
+
+ /* Tell the network layer, that we can accept more frames */
+ mark_bh( NET_BH);
+
+ DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+}
+
+/*
+ * Function ircc_dma_receive (idev)
+ *
+ * Get ready for receiving a frame. The device will initiate a DMA
+ * if it starts to receive a frame.
+ *
+ */
+static int ircc_dma_receive( struct irda_device *idev)
+{
+ struct ircc_cb *self;
+ int iobase;
+
+ DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+ ASSERT( idev != NULL, return -1;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+
+ self = idev->priv;
+ iobase= idev->io.iobase;
+
+ setup_dma( idev->io.dma, idev->rx_buff.data, idev->rx_buff.truesize,
+ DMA_MODE_READ);
+
+ /* driver->media_busy = FALSE; */
+ idev->io.direction = IO_RECV;
+ idev->rx_buff.data = idev->rx_buff.head;
+#if 0
+ idev->rx_buff.offset = 0;
+#endif
+
+ register_bank(iobase, 4);
+ serial_out(iobase, UART_CONTROL,
+ (serial_in(iobase, UART_CONTROL) &0xF0));
+ serial_out(iobase, UART_BOF_COUNT_LO, 2);
+ serial_out(iobase, UART_BRICKWALL_CNT_LO, 0);
+ serial_out(iobase, UART_BRICKWALL_TX_CNT_HI, 0);
+ serial_out(iobase, UART_TX_SIZE_LO, 0);
+ serial_out(iobase, UART_RX_SIZE_HI, 0);
+ serial_out(iobase, UART_RX_SIZE_LO, 0);
+
+ register_bank(iobase, 0);
+ serial_out(iobase,
+ UART_LCR_B, UART_LCR_B_SCE_RECEIVE | UART_LCR_B_SIP_ENABLE);
+
+ register_bank(iobase, 1);
+ serial_out(iobase, UART_SCE_CFGB,
+ serial_in(iobase, UART_SCE_CFGB) |
+ UART_CFGB_DMA_ENABLE | UART_CFGB_DMA_BURST);
+
+ DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+ return 0;
+}
+
+/*
+ * Function ircc_dma_receive_complete (idev)
+ *
+ * Finished with receiving frames
+ *
+ *
+ */
+static int ircc_dma_receive_complete( struct irda_device *idev, int iobase)
+{
+ struct sk_buff *skb;
+ struct ircc_cb *self;
+ int len, msgcnt;
+
+ DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+ self = idev->priv;
+
+ msgcnt = serial_in(idev->io.iobase, UART_LCR_B) & 0x08;
+
+ DEBUG(ircc_debug, __FUNCTION__ ": dma count = %d\n",
+ get_dma_residue(idev->io.dma));
+
+ len = idev->rx_buff.truesize - get_dma_residue(idev->io.dma) - 4;
+
+ DEBUG(ircc_debug, __FUNCTION__ ": msgcnt = %d, len=%d\n", msgcnt, len);
+
+ skb = dev_alloc_skb( len+1);
+
+ if (skb == NULL) {
+ printk( KERN_INFO __FUNCTION__
+ ": memory squeeze, dropping frame.\n");
+ return FALSE;
+ }
+
+ /* Make sure IP header gets aligned */
+ skb_reserve( skb, 1);
+ skb_put( skb, len);
+
+ memcpy(skb->data, idev->rx_buff.data, len);
+ idev->stats.rx_packets++;
+
+ skb->dev = &idev->netdev;
+ skb->mac.raw = skb->data;
+ skb->protocol = htons(ETH_P_IRDA);
+ netif_rx( skb);
+
+ register_bank(idev->io.iobase, 1);
+ serial_out(idev->io.iobase, UART_SCE_CFGB,
+ serial_in(idev->io.iobase, UART_SCE_CFGB) &
+ ~UART_CFGB_DMA_ENABLE);
+
+ DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+ return TRUE;
+}
+
+/*
+ * Function ircc_interrupt (irq, dev_id, regs)
+ *
+ * An interrupt from the chip has arrived. Time to do some work
+ *
+ */
+static void ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ int iobase, iir;
+
+ struct irda_device *idev = (struct irda_device *) dev_id;
+
+ DEBUG(ircc_debug+1, __FUNCTION__ " -->\n");
+
+ if (idev == NULL) {
+ printk( KERN_WARNING "%s: irq %d for unknown device.\n",
+ driver_name, irq);
+ return;
+ }
+
+ if (idev->io.baudrate <= 115200) {
+ DEBUG(ircc_debug+1, __FUNCTION__
+ ": routing interrupt to irport_interrupt\n");
+ return irport_interrupt( irq, dev_id, regs);
+ }
+
+ iobase = idev->io.iobase;
+
+ idev->netdev.interrupt = 1;
+
+ serial_out(iobase, UART_MASTER, 0);
+
+ register_bank(iobase, 0);
+
+ iir = serial_in(iobase, UART_IIR);
+
+ serial_out(iobase, UART_IER, 0);
+
+ DEBUG(ircc_debug, __FUNCTION__ ": iir = 0x%02x\n", iir);
+
+ if (iir & UART_IIR_EOM) {
+ DEBUG(ircc_debug, __FUNCTION__ ": UART_IIR_EOM\n");
+ if (idev->io.direction == IO_RECV) {
+ ircc_dma_receive_complete(idev, iobase);
+ } else {
+ ircc_dma_xmit_complete(idev, iobase);
+ }
+ ircc_dma_receive(idev);
+ }
+
+ if (iir & UART_IIR_ACTIVE_FRAME) {
+ DEBUG(ircc_debug, __FUNCTION__ ": UART_IIR_ACTIVE_FRAME\n");
+ idev->rx_buff.state = INSIDE_FRAME;
+#if 0
+ ircc_dma_receive(idev);
+#endif
+ }
+
+ if (iir & UART_IIR_RAW_MODE) {
+ DEBUG(ircc_debug, __FUNCTION__ ": IIR RAW mode interrupt.\n");
+ }
+
+ idev->netdev.interrupt = 0;
+
+ register_bank(iobase, 0);
+ serial_out(iobase, UART_IER, UART_IER_ACTIVE_FRAME|UART_IER_EOM);
+ serial_out(iobase, UART_MASTER, UART_MASTER_INT_EN);
+
+ DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+}
+
+/*
+ * Function ircc_wait_until_sent (idev)
+ *
+ * This function should put the current thread to sleep until all data
+ * have been sent, so it is safe to change the speed.
+ */
+static void ircc_wait_until_sent( struct irda_device *idev)
+{
+ DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+ /* Just delay 60 ms */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(6);
+
+ DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+}
+
+/*
+ * Function ircc_is_receiving (idev)
+ *
+ * Return TRUE is we are currently receiving a frame
+ *
+ */
+static int ircc_is_receiving( struct irda_device *idev)
+{
+ int status = FALSE;
+ /* int iobase; */
+
+ DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+ ASSERT( idev != NULL, return FALSE;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return FALSE;);
+
+ DEBUG(ircc_debug, __FUNCTION__ ": dma count = %d\n",
+ get_dma_residue(idev->io.dma));
+
+ status = ( idev->rx_buff.state != OUTSIDE_FRAME);
+
+ DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+
+ return status;
+}
+
+/*
+ * Function ircc_net_init (dev)
+ *
+ * Initialize network device
+ *
+ */
+static int ircc_net_init( struct device *dev)
+{
+ DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+ /* Setup to be a normal IrDA network device driver */
+ irda_device_setup( dev);
+
+ /* Insert overrides below this line! */
+
+ DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+ return 0;
+}
+
+
+/*
+ * Function ircc_net_open (dev)
+ *
+ * Start the device
+ *
+ */
+static int ircc_net_open( struct device *dev)
+{
+ struct irda_device *idev;
+ int iobase;
+
+ DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+ ASSERT( dev != NULL, return -1;);
+ idev = (struct irda_device *) dev->priv;
+
+ ASSERT( idev != NULL, return 0;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+
+ iobase = idev->io.iobase;
+
+ if (request_irq( idev->io.irq, ircc_interrupt, 0, idev->name,
+ (void *) idev)) {
+ return -EAGAIN;
+ }
+ /*
+ * Always allocate the DMA channel after the IRQ,
+ * and clean up on failure.
+ */
+ if (request_dma(idev->io.dma, idev->name)) {
+ free_irq( idev->io.irq, idev);
+ return -EAGAIN;
+ }
+
+ /* Ready to play! */
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ /* turn on interrupts */
+
+ MOD_INC_USE_COUNT;
+
+ DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+ return 0;
+}
+
+/*
+ * Function ircc_net_close (dev)
+ *
+ * Stop the device
+ *
+ */
+static int ircc_net_close(struct device *dev)
+{
+ struct irda_device *idev;
+ int iobase;
+
+ DEBUG(ircc_debug, __FUNCTION__ " -->\n");
+
+ /* Stop device */
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ ASSERT( dev != NULL, return -1;);
+ idev = (struct irda_device *) dev->priv;
+
+ ASSERT( idev != NULL, return 0;);
+ ASSERT( idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+
+ iobase = idev->io.iobase;
+
+ disable_dma( idev->io.dma);
+
+ /* Disable interrupts */
+
+ free_irq( idev->io.irq, idev);
+ free_dma( idev->io.dma);
+
+ MOD_DEC_USE_COUNT;
+
+ DEBUG( ircc_debug, "--> " __FUNCTION__ "\n");
+ return 0;
+}
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Thomas Davis <tadavis@jps.net>");
+MODULE_DESCRIPTION("SMC IrCC controller driver");
+MODULE_PARM(ircc_debug,"1i");
+MODULE_PARM(ircc_dma, "1i");
+MODULE_PARM(ircc_irq, "1i");
+
+/*
+ * Function init_module (void)
+ *
+ *
+ *
+ */
+int init_module(void)
+{
+ return ircc_init();
+}
+
+/*
+ * Function cleanup_module (void)
+ *
+ *
+ *
+ */
+void cleanup_module(void)
+{
+ ircc_cleanup();
+}
+
+#endif
diff --git a/drivers/net/irda/toshoboe.c b/drivers/net/irda/toshoboe.c
new file mode 100644
index 000000000..c7ef984ea
--- /dev/null
+++ b/drivers/net/irda/toshoboe.c
@@ -0,0 +1,901 @@
+/*********************************************************************
+ *
+ * Filename: toshoboe.c
+ * Version: 0.1
+ * Description: Driver for the Toshiba OBOE (or type-O or 700 or 701)
+ * FIR Chipset.
+ * Status: Experimental.
+ * Author: James McKenzie <james@fishsoup.dhs.org>
+ * Created at: Sat May 8 12:35:27 1999
+ *
+ * Copyright (c) 1999 James McKenzie, All Rights Reserved.
+ *
+ * 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.
+ *
+ * Neither James McKenzie nor Cambridge University admit liability nor
+ * provide warranty for any of this software. This material is
+ * provided "AS-IS" and at no charge.
+ *
+ * Applicable Models : Libretto 100CT. and many more
+ * Toshiba refers to this chip as the type-O IR port.
+ *
+ ********************************************************************/
+
+/* This driver is experimental, I have only three ir devices */
+/* an olivetti notebook which doesn't have FIR, a toshiba libretto, and */
+/* an hp printer, this works fine at 4MBPS with my HP printer */
+
+static char *rcsid = "$Id: toshoboe.c,v 1.5 1999/05/12 12:24:39 root Exp root $";
+
+/*
+ * $Log: toshoboe.c,v $
+ * Revision 1.5 1999/05/12 12:24:39 root
+ * *** empty log message ***
+ *
+ * Revision 1.4 1999/05/12 11:55:08 root
+ * *** empty log message ***
+ *
+ * Revision 1.3 1999/05/09 01:33:12 root
+ * *** empty log message ***
+ *
+ * Revision 1.2 1999/05/09 01:30:38 root
+ * *** empty log message ***
+ *
+ * Revision 1.1 1999/05/09 01:25:04 root
+ * Initial revision
+ *
+ */
+
+/* Define this to have only one frame in the XMIT or RECV queue */
+/* Toshiba's drivers do this, but it disables back to back tansfers */
+/* I think that the chip may have some problems certainly, I have */
+/* seen it jump over tasks in the taskfile->xmit with this turned on */
+#define ONETASK
+
+/* To adjust the number of tasks in use edit toshoboe.h */
+
+/* Define this to enable FIR and MIR support */
+#define ENABLE_FAST
+
+/* Number of ports this driver can support, you also need to edit dev_self below */
+#define NSELFS 4
+
+/* Size of IO window */
+#define CHIP_IO_EXTENT 0x1f
+
+/* Transmit and receive buffer sizes, adjust at your peril */
+#define RX_BUF_SZ 4196
+#define TX_BUF_SZ 4196
+
+/* No user servicable parts below here */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <net/irda/wrapper.h>
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+#include <net/irda/irlap_frame.h>
+#include <net/irda/irda_device.h>
+
+#include <net/irda/toshoboe.h>
+
+static char *driver_name = "toshoboe";
+
+static struct toshoboe_cb *dev_self[NSELFS + 1] =
+{NULL, NULL, NULL, NULL, NULL};
+
+/* Shutdown the chip and point the taskfile reg somewhere else */
+static void
+toshoboe_stopchip (struct toshoboe_cb *self)
+{
+ DEBUG (4, __FUNCTION__ "()\n");
+
+ outb_p (0x0e, OBOE_REG_11);
+
+ outb_p (0x00, OBOE_RST);
+ outb_p (0x3f, OBOE_TFP2); /*Write the taskfile address */
+ outb_p (0xff, OBOE_TFP1);
+ outb_p (0xff, OBOE_TFP0);
+ outb_p (0x0f, OBOE_REG_1B);
+ outb_p (0xff, OBOE_REG_1A);
+ outb_p (0x00, OBOE_ISR); /*FIXME: should i do this to disbale ints */
+ outb_p (0x80, OBOE_RST);
+ outb_p (0xe, OBOE_LOCK);
+}
+
+/*Set the baud rate */
+static void
+toshoboe_setbaud (struct toshoboe_cb *self, int baud)
+{
+ DEBUG (4, __FUNCTION__ "()\n");
+
+ printk (KERN_WARNING "ToshOboe: seting baud to %d\n", baud);
+
+ cli ();
+ switch (baud)
+ {
+ case 2400:
+ outb_p (OBOE_PMDL_SIR, OBOE_PMDL);
+ outb_p (OBOE_SMDL_SIR, OBOE_SMDL);
+ outb_p (0xbf, OBOE_UDIV);
+ break;
+ case 4800:
+ outb_p (OBOE_PMDL_SIR, OBOE_PMDL);
+ outb_p (OBOE_SMDL_SIR, OBOE_SMDL);
+ outb_p (0x5f, OBOE_UDIV);
+ break;
+ case 9600:
+ outb_p (OBOE_PMDL_SIR, OBOE_PMDL);
+ outb_p (OBOE_SMDL_SIR, OBOE_SMDL);
+ outb_p (0x2f, OBOE_UDIV);
+ break;
+ case 19200:
+ outb_p (OBOE_PMDL_SIR, OBOE_PMDL);
+ outb_p (OBOE_SMDL_SIR, OBOE_SMDL);
+ outb_p (0x17, OBOE_UDIV);
+ break;
+ case 38400:
+ outb_p (OBOE_PMDL_SIR, OBOE_PMDL);
+ outb_p (OBOE_SMDL_SIR, OBOE_SMDL);
+ outb_p (0xb, OBOE_UDIV);
+ break;
+ case 57600:
+ outb_p (OBOE_PMDL_SIR, OBOE_PMDL);
+ outb_p (OBOE_SMDL_SIR, OBOE_SMDL);
+ outb_p (0x7, OBOE_UDIV);
+ break;
+ case 115200:
+ outb_p (OBOE_PMDL_SIR, OBOE_PMDL);
+ outb_p (OBOE_SMDL_SIR, OBOE_SMDL);
+ outb_p (0x3, OBOE_UDIV);
+ break;
+ case 1152000:
+ outb_p (OBOE_PMDL_MIR, OBOE_PMDL);
+ outb_p (OBOE_SMDL_MIR, OBOE_SMDL);
+ outb_p (0x1, OBOE_UDIV);
+ break;
+ case 4000000:
+ outb_p (OBOE_PMDL_FIR, OBOE_PMDL);
+ outb_p (OBOE_SMDL_FIR, OBOE_SMDL);
+ outb_p (0x0, OBOE_UDIV);
+ break;
+ }
+
+ sti ();
+
+ outb_p (0x00, OBOE_RST);
+ outb_p (0x80, OBOE_RST);
+ outb_p (0x01, OBOE_REG_9);
+
+}
+
+/* Wake the chip up and get it looking at the taskfile */
+static void
+toshoboe_startchip (struct toshoboe_cb *self)
+{
+ __u32 physaddr;
+
+ DEBUG (4, __FUNCTION__ "()\n");
+
+
+ outb_p (0, OBOE_LOCK);
+ outb_p (0, OBOE_RST);
+ outb_p (OBOE_NTR_VAL, OBOE_NTR);
+ outb_p (0xf0, OBOE_REG_D);
+ outb_p (0xff, OBOE_ISR);
+ outb_p (0x0f, OBOE_REG_1A);
+ outb_p (0xff, OBOE_REG_1B);
+
+
+ physaddr = virt_to_bus (self->taskfile);
+
+ outb_p ((physaddr >> 0x0a) & 0xff, OBOE_TFP0);
+ outb_p ((physaddr >> 0x12) & 0xff, OBOE_TFP1);
+ outb_p ((physaddr >> 0x1a) & 0x3f, OBOE_TFP2);
+
+ outb_p (0x0e, OBOE_REG_11);
+ outb_p (0x80, OBOE_RST);
+
+ toshoboe_setbaud (self, 9600);
+
+}
+
+/*Let the chip look at memory */
+static void
+toshoboe_enablebm (struct toshoboe_cb *self)
+{
+ DEBUG (4, __FUNCTION__ "()\n");
+ pci_set_master (self->pdev);
+}
+
+/*Don't let the chip look at memory */
+static void
+toshoboe_disablebm (struct toshoboe_cb *self)
+{
+ __u8 command;
+ DEBUG (4, __FUNCTION__ "()\n");
+
+ pci_read_config_byte (self->pdev, PCI_COMMAND, &command);
+ command &= ~PCI_COMMAND_MASTER;
+ pci_write_config_byte (self->pdev, PCI_COMMAND, command);
+
+}
+
+/*setup the taskfile */
+static void
+toshoboe_initbuffs (struct toshoboe_cb *self)
+{
+ int i;
+
+ DEBUG (4, __FUNCTION__ "()\n");
+
+ cli ();
+
+ for (i = 0; i < TX_SLOTS; ++i)
+ {
+ self->taskfile->xmit[i].len = 0;
+ self->taskfile->xmit[i].control = 0x00;
+ self->taskfile->xmit[i].buffer = virt_to_bus (self->xmit_bufs[i]);
+ }
+
+ for (i = 0; i < RX_SLOTS; ++i)
+ {
+ self->taskfile->recv[i].len = 0;
+ self->taskfile->recv[i].control = 0x83;
+ self->taskfile->recv[i].buffer = virt_to_bus (self->recv_bufs[i]);
+ }
+
+ sti ();
+}
+
+
+/*Transmit something */
+static int
+toshoboe_hard_xmit (struct sk_buff *skb, struct device *dev)
+{
+ struct irda_device *idev;
+ struct toshoboe_cb *self;
+ int mtt, len;
+
+ idev = (struct irda_device *) dev->priv;
+ ASSERT (idev != NULL, return 0;);
+ ASSERT (idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+
+ self = idev->priv;
+ ASSERT (self != NULL, return 0;);
+
+
+#ifdef ONETASK
+ if (self->txpending)
+ return -EBUSY;
+
+ self->txs = inb_p (OBOE_XMTT) - OBOE_XMTT_OFFSET;
+
+ self->txs &= 0x3f;
+
+#endif
+
+ if (self->taskfile->xmit[self->txs].control)
+ return -EBUSY;
+
+
+ if (inb_p (OBOE_RST) & OBOE_RST_WRAP)
+ {
+ len = async_wrap_skb (skb, self->xmit_bufs[self->txs], TX_BUF_SZ);
+ }
+ else
+ {
+ len = skb->len;
+ memcpy (self->xmit_bufs[self->txs], skb->data, len);
+ }
+ self->taskfile->xmit[self->txs].len = len & 0x0fff;
+
+
+
+ outb_p (0, OBOE_RST);
+ outb_p (0x1e, OBOE_REG_11);
+
+ self->taskfile->xmit[self->txs].control = 0x84;
+
+ mtt = irda_get_mtt (skb);
+ if (mtt)
+ udelay (mtt);
+
+ self->txpending++;
+
+ /*FIXME: ask about tbusy,media_busy stuff, for the moment */
+ /*tbusy means can't queue any more */
+#ifndef ONETASK
+ if (self->txpending == TX_SLOTS)
+ {
+#else
+ {
+#endif
+ if (irda_lock ((void *) &dev->tbusy) == FALSE)
+ return -EBUSY;
+ }
+
+ outb_p (0x80, OBOE_RST);
+ outb_p (1, OBOE_REG_9);
+
+ self->txs++;
+ self->txs %= TX_SLOTS;
+
+ dev_kfree_skb (skb);
+
+ return 0;
+}
+
+/*interrupt handler */
+static void
+toshoboe_interrupt (int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct irda_device *idev = (struct irda_device *) dev_id;
+ struct toshoboe_cb *self;
+ __u8 irqstat;
+ struct sk_buff *skb;
+
+ if (idev == NULL)
+ {
+ printk (KERN_WARNING "%s: irq %d for unknown device.\n",
+ driver_name, irq);
+ return;
+ }
+
+ self = idev->priv;
+
+ if (!self)
+ return;
+
+ DEBUG (4, __FUNCTION__ "()\n");
+
+ irqstat = inb_p (OBOE_ISR);
+
+/* woz it us */
+ if (!(irqstat & 0xf8))
+ return;
+
+ outb_p (irqstat, OBOE_ISR); /*Acknologede it */
+
+
+/* Txdone */
+ if (irqstat & OBOE_ISR_TXDONE)
+ {
+ self->txpending--;
+
+ idev->stats.tx_packets++;
+
+ idev->media_busy = FALSE;
+ idev->netdev.tbusy = 0;
+
+ mark_bh (NET_BH);
+ }
+
+ if (irqstat & OBOE_ISR_RXDONE)
+ {
+
+#ifdef ONETASK
+ self->rxs = inb_p (OBOE_RCVT);
+ self->rxs += (RX_SLOTS - 1);
+ self->rxs %= RX_SLOTS;
+#else
+ while (self->taskfile->recv[self->rxs].control == 0)
+#endif
+ {
+ int len = self->taskfile->recv[self->rxs].len;
+
+ if (len>2) len-=2;
+
+ skb = dev_alloc_skb (len + 1);
+ if (skb)
+ {
+ skb_reserve (skb, 1);
+
+ skb_put (skb, len);
+ memcpy (skb->data, self->recv_bufs[self->rxs], len);
+
+ idev->stats.rx_packets++;
+ skb->dev = &idev->netdev;
+ skb->mac.raw = skb->data;
+ skb->protocol = htons (ETH_P_IRDA);
+ }
+ else
+ {
+ printk (KERN_INFO __FUNCTION__
+ "(), memory squeeze, dropping frame.\n");
+ }
+
+
+
+ self->taskfile->recv[self->rxs].control = 0x83;
+ self->taskfile->recv[self->rxs].len = 0x0;
+
+ self->rxs++;
+ self->rxs %= RX_SLOTS;
+
+ if (skb)
+ netif_rx (skb);
+
+ }
+
+ }
+
+ if (irqstat & OBOE_ISR_20)
+ {
+ printk (KERN_WARNING "Oboe_irq: 20\n");
+ }
+ if (irqstat & OBOE_ISR_10)
+ {
+ printk (KERN_WARNING "Oboe_irq: 10\n");
+ }
+ if (irqstat & 0x8)
+ {
+ /*FIXME: I think this is a TX or RX error of some sort */
+
+ idev->stats.tx_errors++;
+ idev->stats.rx_errors++;
+
+ }
+
+
+}
+
+
+
+/* Change the baud rate */
+static void
+toshoboe_change_speed (struct irda_device *idev, int speed)
+{
+ struct toshoboe_cb *self;
+ DEBUG (4, __FUNCTION__ "()\n");
+
+ ASSERT (idev != NULL, return;);
+ ASSERT (idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ self = idev->priv;
+ ASSERT (self != NULL, return;);
+
+ idev->io.baudrate = speed;
+
+ toshoboe_setbaud (self, speed);
+
+}
+
+
+/* Check all xmit_tasks finished */
+static void
+toshoboe_wait_until_sent (struct irda_device *idev)
+{
+ struct toshoboe_cb *self;
+ int i;
+
+ DEBUG (4, __FUNCTION__ "()\n");
+
+ ASSERT (idev != NULL, return;);
+ ASSERT (idev->magic == IRDA_DEVICE_MAGIC, return;);
+
+ self = idev->priv;
+ ASSERT (self != NULL, return;);
+
+ for (i = 0; i < TX_SLOTS; ++i)
+ {
+ while (self->taskfile->xmit[i].control)
+ {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout (6);
+ }
+ }
+
+}
+
+static int
+toshoboe_is_receiving (struct irda_device *idev)
+{
+ DEBUG (4, __FUNCTION__ "()\n");
+
+/*FIXME Can't tell! */
+ return (FALSE);
+}
+
+
+static int
+toshoboe_net_init (struct device *dev)
+{
+ DEBUG (4, __FUNCTION__ "()\n");
+
+ /* Setup to be a normal IrDA network device driver */
+ irda_device_setup (dev);
+
+ /* Insert overrides below this line! */
+ return 0;
+}
+
+
+
+
+static int
+toshoboe_net_open (struct device *dev)
+{
+ struct irda_device *idev;
+ struct toshoboe_cb *self;
+
+ DEBUG (4, __FUNCTION__ "()\n");
+
+ ASSERT (dev != NULL, return -1;);
+ idev = (struct irda_device *) dev->priv;
+
+ ASSERT (idev != NULL, return 0;);
+ ASSERT (idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+
+ self = idev->priv;
+ ASSERT (self != NULL, return 0;);
+
+ if (request_irq (idev->io.irq, toshoboe_interrupt,
+ SA_SHIRQ | SA_INTERRUPT, idev->name, (void *) idev))
+ {
+
+ return -EAGAIN;
+ }
+
+ toshoboe_initbuffs (self);
+ toshoboe_enablebm (self);
+ toshoboe_startchip (self);
+
+
+ cli ();
+
+ /*FIXME: need to test this carefully to check which one */
+ /*of the two possible startup logics the chip uses */
+ /*although it won't make any difference if no-one xmits durining init */
+ /*and none what soever if using ONETASK */
+
+ self->rxs = inb_p (OBOE_RCVT);
+ self->txs = inb_p (OBOE_XMTT) - OBOE_XMTT_OFFSET;
+
+#ifdef 0
+ self->rxs = 0;
+ self->txs = 0;
+#endif
+#ifdef 0
+ self->rxs = RX_SLOTS - 1;
+ self->txs = 0;
+#endif
+
+
+ self->txpending = 0;
+
+ sti ();
+
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+
+}
+
+static int
+toshoboe_net_close (struct device *dev)
+{
+ struct irda_device *idev;
+ struct toshoboe_cb *self;
+
+ DEBUG (4, __FUNCTION__ "()\n");
+
+ ASSERT (dev != NULL, return -1;);
+ idev = (struct irda_device *) dev->priv;
+
+ ASSERT (idev != NULL, return 0;);
+ ASSERT (idev->magic == IRDA_DEVICE_MAGIC, return 0;);
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+
+ self = idev->priv;
+
+ ASSERT (self != NULL, return 0;);
+
+ free_irq (idev->io.irq, (void *) idev);
+
+ toshoboe_stopchip (self);
+ toshoboe_disablebm (self);
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+
+}
+
+
+
+#ifdef MODULE
+
+static int
+toshoboe_close (struct irda_device *idev)
+{
+ struct toshoboe_cb *self;
+ int i;
+
+ DEBUG (4, __FUNCTION__ "()\n");
+
+ ASSERT (idev != NULL, return -1;);
+ ASSERT (idev->magic == IRDA_DEVICE_MAGIC, return -1;);
+
+ self = idev->priv;
+
+ ASSERT (self != NULL, return -1;);
+
+ toshoboe_stopchip (self);
+
+ release_region (idev->io.iobase, idev->io.io_ext);
+
+
+ for (i = 0; i < TX_SLOTS; ++i)
+ {
+ kfree (self->xmit_bufs[i]);
+ self->xmit_bufs[i] = NULL;
+ }
+
+ for (i = 0; i < RX_SLOTS; ++i)
+ {
+ kfree (self->recv_bufs[i]);
+ self->recv_bufs[i] = NULL;
+ }
+
+
+ kfree (self->taskfilebuf);
+ self->taskfilebuf = NULL;
+ self->taskfile = NULL;
+
+
+ irda_device_close (idev);
+
+ return (0);
+
+}
+
+#endif
+
+
+
+static int
+toshoboe_open (struct pci_dev *pci_dev)
+{
+ struct toshoboe_cb *self;
+ struct irda_device *idev;
+ int i = 0;
+ int ok=0;
+
+
+ DEBUG (4, __FUNCTION__ "()\n");
+
+ while (dev_self[i])
+ i++;
+
+ if (i == NSELFS)
+ {
+ printk (KERN_ERR "Oboe: No more instances available");
+ return -ENOMEM;
+ }
+
+ self = kmalloc (sizeof (struct toshoboe_cb), GFP_KERNEL);
+
+ if (self == NULL)
+ {
+ printk (KERN_ERR "IrDA: Can't allocate memory for "
+ "IrDA control block!\n");
+ return -ENOMEM;
+ }
+
+ memset (self, 0, sizeof (struct toshoboe_cb));
+
+
+ dev_self[i] = self;
+
+ self->pdev = pci_dev;
+ self->base = pci_dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
+
+ idev = &self->idev;
+
+ /*Setup idev */
+
+ idev->io.iobase = self->base;
+ idev->io.irq = pci_dev->irq;
+ idev->io.io_ext = CHIP_IO_EXTENT;
+
+ /* Lock the port that we need */
+ i = check_region (idev->io.iobase, idev->io.io_ext);
+ if (i < 0)
+ {
+ DEBUG (0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
+ idev->io.iobase);
+
+ dev_self[i] = NULL;
+ kfree (self);
+
+ return -ENODEV;
+ }
+
+ request_region (idev->io.iobase, idev->io.io_ext, driver_name);
+
+ irda_init_max_qos_capabilies (&idev->qos);
+
+ idev->qos.baud_rate.bits = IR_2400 | /*IR_4800 | */ IR_9600 | IR_19200 |
+ IR_115200;
+#ifdef ENABLE_FAST
+ idev->qos.baud_rate.bits|= IR_576000 | IR_1152000 | (IR_4000000 << 8);
+#endif
+
+ idev->qos.min_turn_time.bits = 0xff; /*FIXME: what does this do? */
+
+ irda_qos_bits_to_value (&idev->qos);
+
+ idev->flags = IFF_SIR | IFF_DMA | IFF_PIO;
+
+#ifdef ENABLE_FAST
+ idev->flags |= IFF_FIR;
+#endif
+
+ /* These aren't much use as we need to have a whole panoply of
+ * buffers running */
+
+ idev->rx_buff.flags = 0;
+ idev->tx_buff.flags = 0;
+ idev->rx_buff.truesize = 0;
+ idev->rx_buff.truesize = 0;
+
+ idev->change_speed = toshoboe_change_speed;
+ idev->wait_until_sent = toshoboe_wait_until_sent;
+ idev->is_receiving = toshoboe_is_receiving;
+
+ idev->netdev.init = toshoboe_net_init;
+ idev->netdev.hard_start_xmit = toshoboe_hard_xmit;
+ idev->netdev.open = toshoboe_net_open;
+ idev->netdev.stop = toshoboe_net_close;
+
+
+ /* Now setup the endless buffers we need */
+
+ self->txs = 0;
+ self->rxs = 0;
+
+ self->taskfilebuf = kmalloc (OBOE_TASK_BUF_LEN, GFP_KERNEL | GFP_DMA);
+ if (!self->taskfilebuf) {
+ printk(KERN_ERR "toshoboe: kmalloc for DMA failed()\n");
+ kfree(self);
+ return -ENOMEM;
+ }
+
+
+ memset (self->taskfilebuf, 0, OBOE_TASK_BUF_LEN);
+
+ /*We need to align the taskfile on a taskfile size boundary */
+ {
+ __u32 addr;
+
+ addr = (__u32) self->taskfilebuf;
+ addr &= ~(sizeof (struct OboeTaskFile) - 1);
+ addr += sizeof (struct OboeTaskFile);
+
+ self->taskfile = (struct OboeTaskFile *) addr;
+ }
+
+ for (i = 0; i < TX_SLOTS; ++i)
+ {
+ self->xmit_bufs[i] = kmalloc (TX_BUF_SZ, GFP_KERNEL | GFP_DMA);
+ if (self->xmit_bufs[i]) ok++;
+ }
+
+ for (i = 0; i < RX_SLOTS; ++i)
+ {
+ self->recv_bufs[i] = kmalloc (TX_BUF_SZ, GFP_KERNEL | GFP_DMA);
+ if (self->recv_bufs[i]) ok++;
+ }
+
+ if (ok!=RX_SLOTS+TX_SLOTS) {
+ printk(KERN_ERR "toshoboe: kmalloc for buffers failed()\n");
+
+
+ for (i = 0; i < TX_SLOTS; ++i) if (self->xmit_bufs[i]) kfree(self->xmit_bufs[i]);
+ for (i = 0; i < RX_SLOTS; ++i) if (self->recv_bufs[i]) kfree(self->recv_bufs[i]);
+
+ kfree(self);
+ return -ENOMEM;
+
+ }
+
+
+ irda_device_open (idev, driver_name, self);
+
+ printk (KERN_WARNING "ToshOboe: Using ");
+#ifdef ONETASK
+ printk ("single");
+#else
+ printk ("multiple");
+#endif
+ printk (" tasks, version %s\n", rcsid);
+
+ return (0);
+}
+
+__initfunc (int toshoboe_init (void))
+{
+ struct pci_dev *pci_dev = NULL;
+ int found = 0;
+
+ do
+ {
+ pci_dev = pci_find_device (PCI_VENDOR_ID_TOSHIBA,
+ PCI_DEVICE_ID_FIR701, pci_dev);
+ if (pci_dev)
+ {
+ printk (KERN_WARNING "ToshOboe: Found 701 chip at 0x%0lx irq %d\n",
+ pci_dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK,
+ pci_dev->irq);
+
+ if (!toshoboe_open (pci_dev))
+ found++;
+ }
+
+ }
+ while (pci_dev);
+
+ if (found)
+ return 0;
+
+ return -ENODEV;
+}
+
+#ifdef MODULE
+
+static void
+toshoboe_cleanup (void)
+{
+ int i;
+
+ DEBUG (4, __FUNCTION__ "()\n");
+
+ for (i = 0; i < 4; i++)
+ {
+ if (dev_self[i])
+ toshoboe_close (&(dev_self[i]->idev));
+ }
+}
+
+
+
+int
+init_module (void)
+{
+ return toshoboe_init ();
+}
+
+
+void
+cleanup_module (void)
+{
+ toshoboe_cleanup ();
+}
+
+
+#endif
diff --git a/drivers/net/sk_mca.c b/drivers/net/sk_mca.c
new file mode 100644
index 000000000..720b50beb
--- /dev/null
+++ b/drivers/net/sk_mca.c
@@ -0,0 +1,1143 @@
+/*
+net-3-driver for the SKNET MCA-based cards
+
+This is an extension to the Linux operating system, and is covered by the
+same Gnu Public License that covers that work.
+
+Copyright 1999 by Alfred Arnold (alfred@ccac.rwth-aachen.de, aarnold@elsa.de)
+
+This driver is based both on the 3C523 driver and the SK_G16 driver.
+
+paper sources:
+ 'PC Hardware: Aufbau, Funktionsweise, Programmierung' by
+ Hans-Peter Messmer for the basic Microchannel stuff
+
+ 'Linux Geraetetreiber' by Allesandro Rubini, Kalle Dalheimer
+ for help on Ethernet driver programming
+
+ 'Ethernet/IEEE 802.3 Family 1992 World Network Data Book/Handbook' by AMD
+ for documentation on the AM7990 LANCE
+
+ 'SKNET Personal Technisches Manual', Version 1.2 by Schneider&Koch
+ for documentation on the Junior board
+
+ 'SK-NET MC2+ Technical Manual", Version 1.1 by Schneider&Koch for
+ documentation on the MC2 bord
+
+ A big thank you to the S&K support for providing me so quickly with
+ documentation!
+
+ Also see http://www.syskonnect.com/
+
+ Missing things:
+
+ -> set debug level via ioctl instead of compile-time switches
+ -> I didn't follow the development of the 2.1.x kernels, so my
+ assumptions about which things changed with which kernel version
+ are probably nonsense
+
+History:
+ May 16th, 1999
+ startup
+ May 22st, 1999
+ added private structure, methods
+ begun building data structures in RAM
+ May 23nd, 1999
+ can receive frames, send frames
+ May 24th, 1999
+ modularized intialization of LANCE
+ loadable as module
+ still Tx problem :-(
+ May 26th, 1999
+ MC2 works
+ support for multiple devices
+ display media type for MC2+
+ May 28th, 1999
+ fixed problem in GetLANCE leaving interrupts turned off
+ increase TX queue to 4 packets to improve send performance
+ May 29th, 1999
+ a few corrections in statistics, caught rcvr overruns
+ reinitialization of LANCE/board in critical situations
+ MCA info implemented
+ implemented LANCE multicast filter
+ Jun 6th, 1999
+ additions for Linux 2.2
+
+ *************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <linux/mca.h>
+#include <asm/processor.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#define _SK_MCA_DRIVER_
+#include "sk_mca.h"
+
+/* ------------------------------------------------------------------------
+ * global static data - not more since we can handle multiple boards and
+ * have to pack all state info into the device struct!
+ * ------------------------------------------------------------------------ */
+
+static char *MediaNames[Media_Count]=
+ {"10Base2", "10BaseT", "10Base5", "Unknown"};
+
+static unsigned char poly[] =
+ {1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0};
+
+/* ------------------------------------------------------------------------
+ * private subfunctions
+ * ------------------------------------------------------------------------ */
+
+/* dump parts of shared memory - only needed during debugging */
+
+#ifdef DEBUG
+static void dumpmem(struct device *dev, u32 start, u32 len)
+{
+ int z;
+
+ for (z = 0; z < len; z++)
+ {
+ if ((z & 15) == 0)
+ printk("%04x:", z);
+ printk(" %02x", readb(dev->mem_start + start + z));
+ if ((z & 15) == 15)
+ printk("\n");
+ }
+}
+
+/* print exact time - ditto */
+
+static void PrTime(void)
+{
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+ printk("%9d:%06d: ", tv.tv_sec, tv.tv_usec);
+}
+#endif
+
+/* deduce resources out of POS registers */
+
+static void getaddrs(int slot, int junior, int *base, int *irq,
+ skmca_medium *medium)
+{
+ u_char pos0, pos1, pos2;
+
+ if (junior)
+ {
+ pos0 = mca_read_stored_pos(slot, 2);
+ *base = ((pos0 & 0x0e) << 13) + 0xc0000;
+ *irq = ((pos0 & 0x10) >> 4) + 10;
+ *medium = Media_Unknown;
+ }
+ else
+ {
+ /* reset POS 104 Bits 0+1 so the shared memory region goes to the
+ configured area between 640K and 1M. Afterwards, enable the MC2.
+ I really don't know what rode SK to do this... */
+
+ mca_write_pos(slot, 4, mca_read_stored_pos(slot, 4) & 0xfc);
+ mca_write_pos(slot, 2, mca_read_stored_pos(slot, 2) | 0x01);
+
+ pos1 = mca_read_stored_pos(slot, 3);
+ pos2 = mca_read_stored_pos(slot, 4);
+ *base = ((pos1 & 0x07) << 14) + 0xc0000;
+ switch (pos2 & 0x0c)
+ {
+ case 0: *irq = 3; break;
+ case 4: *irq = 5; break;
+ case 8: *irq = 10; break;
+ case 12: *irq = 11; break;
+ }
+ *medium = (pos2 >> 6) & 3;
+ }
+}
+
+/* check for both cards:
+ When the MC2 is turned off, it was configured for more than 15MB RAM,
+ is disabled and won't get detected using the standard probe. We
+ therefore have to scan the slots manually :-( */
+
+static int dofind(int *junior, int firstslot)
+{
+ int slot;
+ unsigned int id;
+
+ for (slot = firstslot; slot < MCA_MAX_SLOT_NR; slot++)
+ {
+ id = mca_read_stored_pos(slot, 0)
+ + (((unsigned int) mca_read_stored_pos(slot, 1)) << 8);
+
+ *junior = 0;
+ if (id == SKNET_MCA_ID)
+ return slot;
+ *junior = 1;
+ if (id == SKNET_JUNIOR_MCA_ID)
+ return slot;
+ }
+ return MCA_NOTFOUND;
+}
+
+/* reset the whole board */
+
+static void ResetBoard(struct device *dev)
+{
+ skmca_priv *priv = (skmca_priv*) dev->priv;
+
+ writeb(CTRL_RESET_ON, priv->ctrladdr);
+ udelay(10);
+ writeb(CTRL_RESET_OFF, priv->ctrladdr);
+}
+
+/* set LANCE register - must be atomic */
+
+static void SetLANCE(struct device *dev, u16 addr, u16 value)
+{
+ skmca_priv *priv = (skmca_priv*) dev->priv;
+ unsigned long flags;
+
+ /* disable interrupts */
+
+ save_flags(flags);
+ cli();
+
+ /* wait until no transfer is pending */
+
+ while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY);
+
+ /* transfer register address to RAP */
+
+ writeb(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_RAP, priv->ctrladdr);
+ writew(addr, priv->ioregaddr);
+ writeb(IOCMD_GO, priv->cmdaddr);
+ udelay(1);
+ while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY);
+
+ /* transfer data to register */
+
+ writeb(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_DATA, priv->ctrladdr);
+ writew(value, priv->ioregaddr);
+ writeb(IOCMD_GO, priv->cmdaddr);
+ udelay(1);
+ while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY);
+
+ /* reenable interrupts */
+
+ restore_flags(flags);
+}
+
+/* get LANCE register */
+
+static u16 GetLANCE(struct device *dev, u16 addr)
+{
+ skmca_priv *priv = (skmca_priv*) dev->priv;
+ unsigned long flags;
+ unsigned int res;
+
+ /* disable interrupts */
+
+ save_flags(flags);
+ cli();
+
+ /* wait until no transfer is pending */
+
+ while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY);
+
+ /* transfer register address to RAP */
+
+ writeb(CTRL_RESET_OFF | CTRL_RW_WRITE | CTRL_ADR_RAP, priv->ctrladdr);
+ writew(addr, priv->ioregaddr);
+ writeb(IOCMD_GO, priv->cmdaddr);
+ udelay(1);
+ while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY);
+
+ /* transfer data from register */
+
+ writeb(CTRL_RESET_OFF | CTRL_RW_READ | CTRL_ADR_DATA, priv->ctrladdr);
+ writeb(IOCMD_GO, priv->cmdaddr);
+ udelay(1);
+ while ((readb(priv->ctrladdr) & STAT_IO_BUSY) == STAT_IO_BUSY);
+ res = readw(priv->ioregaddr);
+
+ /* reenable interrupts */
+
+ restore_flags(flags);
+
+ return res;
+}
+
+/* build up descriptors in shared RAM */
+
+static void InitDscrs(struct device *dev)
+{
+ u32 bufaddr;
+
+ /* Set up Tx descriptors. The board has only 16K RAM so bits 16..23
+ are always 0. */
+
+ bufaddr = RAM_DATABASE;
+ {
+ LANCE_TxDescr descr;
+ int z;
+
+ for (z = 0; z < TXCOUNT; z++)
+ {
+ descr.LowAddr = bufaddr;
+ descr.Flags = 0;
+ descr.Len = 0xf000;
+ descr.Status = 0;
+ memcpy_toio(dev->mem_start + RAM_TXBASE + (z * sizeof(LANCE_TxDescr)),
+ &descr, sizeof(LANCE_TxDescr));
+ memset_io(dev->mem_start + bufaddr, 0, RAM_BUFSIZE);
+ bufaddr += RAM_BUFSIZE;
+ }
+ }
+
+ /* do the same for the Rx descriptors */
+
+ {
+ LANCE_RxDescr descr;
+ int z;
+
+ for (z = 0; z < RXCOUNT; z++)
+ {
+ descr.LowAddr = bufaddr;
+ descr.Flags = RXDSCR_FLAGS_OWN;
+ descr.MaxLen = -RAM_BUFSIZE;
+ descr.Len = 0;
+ memcpy_toio(dev->mem_start + RAM_RXBASE + (z * sizeof(LANCE_RxDescr)),
+ &descr, sizeof(LANCE_RxDescr));
+ memset_io(dev->mem_start + bufaddr, 0, RAM_BUFSIZE);
+ bufaddr += RAM_BUFSIZE;
+ }
+ }
+}
+
+/* calculate the hash bit position for a given multicast address
+ taken more or less directly from the AMD datasheet... */
+
+static void UpdateCRC(unsigned char *CRC, int bit)
+{
+ int j;
+
+ /* shift CRC one bit */
+
+ memmove(CRC + 1, CRC, 32 * sizeof(unsigned char));
+ CRC[0] = 0;
+
+ /* if bit XOR controlbit = 1, set CRC = CRC XOR polynomial */
+
+ if (bit ^ CRC[32])
+ for (j = 0; j < 32; j++)
+ CRC[j] ^= poly[j];
+}
+
+static unsigned int GetHash(char *address)
+{
+ unsigned char CRC[33];
+ int i, byte, hashcode;
+
+ /* a multicast address has bit 0 in the first byte set */
+
+ if ((address[0] & 1) == 0)
+ return -1;
+
+ /* initialize CRC */
+
+ memset(CRC, 1, sizeof(CRC));
+
+ /* loop through address bits */
+
+ for (byte = 0; byte < 6; byte++)
+ for (i = 0; i < 8; i++)
+ UpdateCRC(CRC, (address[byte] >> i) & 1);
+
+ /* hashcode is the 6 least significant bits of the CRC */
+
+ hashcode = 0;
+ for (i = 0; i < 6; i++)
+ hashcode = (hashcode << 1) + CRC[i];
+ return hashcode;
+}
+
+/* feed ready-built initialization block into LANCE */
+
+static void InitLANCE(struct device *dev)
+{
+ skmca_priv *priv = (skmca_priv*) dev->priv;
+
+ /* build up descriptors. */
+
+ InitDscrs(dev);
+
+ /* next RX descriptor to be read is the first one. Since the LANCE
+ will start from the beginning after initialization, we have to
+ reset out pointers too. */
+
+ priv->nextrx = 0;
+
+ /* no TX descriptors active */
+
+ priv->nexttxput = priv->nexttxdone = priv->txbusy = 0;
+
+ /* set up the LANCE bus control register - constant for SKnet boards */
+
+ SetLANCE(dev, LANCE_CSR3, CSR3_BSWAP_OFF | CSR3_ALE_LOW | CSR3_BCON_HOLD);
+
+ /* write address of initialization block into LANCE */
+
+ SetLANCE(dev, LANCE_CSR1, RAM_INITBASE & 0xffff);
+ SetLANCE(dev, LANCE_CSR2, (RAM_INITBASE >> 16) & 0xff);
+
+ /* we don't get ready until the LANCE has read the init block */
+
+ dev->tbusy = 1;
+
+ /* let LANCE read the initialization block. LANCE is ready
+ when we receive the corresponding interrupt. */
+
+ SetLANCE(dev, LANCE_CSR0, CSR0_INEA | CSR0_INIT);
+}
+
+/* stop the LANCE so we can reinitialize it */
+
+static void StopLANCE(struct device *dev)
+{
+ /* can't take frames any more */
+
+ dev->tbusy = 1;
+
+ /* disable interrupts, stop it */
+
+ SetLANCE(dev, LANCE_CSR0, CSR0_STOP);
+}
+
+/* initialize card and LANCE for proper operation */
+
+static void InitBoard(struct device *dev)
+{
+ LANCE_InitBlock block;
+
+ /* Lay out the shared RAM - first we create the init block for the LANCE.
+ We do not overwrite it later because we need it again when we switch
+ promiscous mode on/off. */
+
+ block.Mode = 0;
+ if (dev->flags & IFF_PROMISC)
+ block.Mode |= LANCE_INIT_PROM;
+ memcpy(block.PAdr, dev->dev_addr, 6);
+ memset(block.LAdrF, 0, sizeof(block.LAdrF));
+ block.RdrP = (RAM_RXBASE & 0xffffff) | (LRXCOUNT << 29);
+ block.TdrP = (RAM_TXBASE & 0xffffff) | (LTXCOUNT << 29);
+
+ memcpy_toio(dev->mem_start + RAM_INITBASE, &block, sizeof(block));
+
+ /* initialize LANCE. Implicitly sets up other structures in RAM. */
+
+ InitLANCE(dev);
+}
+
+/* deinitialize card and LANCE */
+
+static void DeinitBoard(struct device *dev)
+{
+ /* stop LANCE */
+
+ StopLANCE(dev);
+
+ /* reset board */
+
+ ResetBoard(dev);
+}
+
+/* ------------------------------------------------------------------------
+ * interrupt handler(s)
+ * ------------------------------------------------------------------------ */
+
+/* LANCE has read initializazion block -> start it */
+
+static u16 irqstart_handler(struct device *dev, u16 oldcsr0)
+{
+ /* now we're ready to transmit */
+
+ dev->tbusy = 0;
+
+ /* reset IDON bit, start LANCE */
+
+ SetLANCE(dev, LANCE_CSR0, oldcsr0 | CSR0_IDON | CSR0_STRT);
+ return GetLANCE(dev, LANCE_CSR0);
+}
+
+/* receive interrupt */
+
+static u16 irqrx_handler(struct device *dev, u16 oldcsr0)
+{
+ skmca_priv *priv = (skmca_priv*) dev->priv;
+ LANCE_RxDescr descr;
+ unsigned int descraddr;
+
+ /* did we loose blocks due to a FIFO overrun ? */
+
+ if (oldcsr0 & CSR0_MISS)
+ priv->stat.rx_fifo_errors++;
+
+ /* run through queue until we reach a descriptor we do not own */
+
+ descraddr = RAM_RXBASE + (priv->nextrx * sizeof(LANCE_RxDescr));
+ while (1)
+ {
+ /* read descriptor */
+ memcpy_fromio(&descr, dev->mem_start + descraddr, sizeof(LANCE_RxDescr));
+
+ /* if we reach a descriptor we do not own, we're done */
+ if ((descr.Flags & RXDSCR_FLAGS_OWN) != 0)
+ break;
+
+#ifdef DEBUG
+ PrTime(); printk("Receive packet on descr %d len %d\n", priv->nextrx, descr.Len);
+#endif
+
+ /* erroneous packet ? */
+ if ((descr.Flags & RXDSCR_FLAGS_ERR) != 0)
+ {
+ priv->stat.rx_errors++;
+ if ((descr.Flags & RXDSCR_FLAGS_CRC) != 0)
+ priv->stat.rx_crc_errors++;
+ else if ((descr.Flags & RXDSCR_FLAGS_CRC) != 0)
+ priv->stat.rx_frame_errors++;
+ else if ((descr.Flags & RXDSCR_FLAGS_OFLO) != 0)
+ priv->stat.rx_fifo_errors++;
+ }
+
+ /* good packet ? */
+ else
+ {
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(descr.Len + 2);
+ if (skb == NULL)
+ priv->stat.rx_dropped++;
+ else
+ {
+ memcpy_fromio(skb_put(skb, descr.Len),
+ dev->mem_start + descr.LowAddr, descr.Len);
+ skb->dev = dev;
+ skb->protocol = eth_type_trans(skb, dev);
+ skb->ip_summed = CHECKSUM_NONE;
+ priv->stat.rx_packets++;
+#if LINUX_VERSION_CODE >= 0x020119 /* byte counters for >= 2.1.25 */
+ priv->stat.rx_bytes += descr.Len;
+#endif
+ netif_rx(skb);
+ }
+ }
+
+ /* give descriptor back to LANCE */
+ descr.Len = 0;
+ descr.Flags |= RXDSCR_FLAGS_OWN;
+
+ /* update descriptor in shared RAM */
+ memcpy_toio(dev->mem_start + descraddr, &descr, sizeof(LANCE_RxDescr));
+
+ /* go to next descriptor */
+ priv->nextrx++; descraddr += sizeof(LANCE_RxDescr);
+ if (priv->nextrx >= RXCOUNT)
+ {
+ priv->nextrx = 0;
+ descraddr = RAM_RXBASE;
+ }
+ }
+
+ /* reset RINT bit */
+
+ SetLANCE(dev, LANCE_CSR0, oldcsr0 | CSR0_RINT);
+ return GetLANCE(dev, LANCE_CSR0);
+}
+
+/* transmit interrupt */
+
+static u16 irqtx_handler(struct device *dev, u16 oldcsr0)
+{
+ skmca_priv *priv = (skmca_priv*) dev->priv;
+ LANCE_TxDescr descr;
+ unsigned int descraddr;
+
+ /* check descriptors at most until no busy one is left */
+
+ descraddr = RAM_TXBASE + (priv->nexttxdone * sizeof(LANCE_TxDescr));
+ while (priv->txbusy > 0)
+ {
+ /* read descriptor */
+ memcpy_fromio(&descr, dev->mem_start + descraddr, sizeof(LANCE_TxDescr));
+
+ /* if the LANCE still owns this one, we've worked out all sent packets */
+ if ((descr.Flags & TXDSCR_FLAGS_OWN) != 0)
+ break;
+
+#ifdef DEBUG
+ PrTime(); printk("Send packet done on descr %d\n", priv->nexttxdone);
+#endif
+
+ /* update statistics */
+ if ((descr.Flags & TXDSCR_FLAGS_ERR) == 0)
+ {
+ priv->stat.tx_packets++;
+#if LINUX_VERSION_CODE >= 0x020119 /* byte counters for >= 2.1.25 */
+ priv->stat.tx_bytes++;
+#endif
+ }
+ else
+ {
+ priv->stat.tx_errors++;
+ if ((descr.Status & TXDSCR_STATUS_UFLO) != 0)
+ {
+ priv->stat.tx_fifo_errors++;
+ InitLANCE(dev);
+ }
+ else if ((descr.Status & TXDSCR_STATUS_LCOL) != 0)
+ priv->stat.tx_window_errors++;
+ else if ((descr.Status & TXDSCR_STATUS_LCAR) != 0)
+ priv->stat.tx_carrier_errors++;
+ else if ((descr.Status & TXDSCR_STATUS_RTRY) != 0)
+ priv->stat.tx_aborted_errors++;
+ }
+
+ /* go to next descriptor */
+ priv->nexttxdone++;
+ descraddr += sizeof(LANCE_TxDescr);
+ if (priv->nexttxdone >= TXCOUNT)
+ {
+ priv->nexttxdone = 0;
+ descraddr = RAM_TXBASE;
+ }
+ priv->txbusy--;
+ }
+
+ /* reset TX interrupt bit */
+
+ SetLANCE(dev, LANCE_CSR0, oldcsr0 | CSR0_TINT);
+ oldcsr0 = GetLANCE(dev, LANCE_CSR0);
+
+ /* at least one descriptor is freed. Therefore we can accept
+ a new one */
+
+ dev->tbusy = 0;
+
+ /* inform upper layers we're in business again */
+
+ mark_bh(NET_BH);
+
+ return oldcsr0;
+}
+
+/* general interrupt entry */
+
+static void irq_handler(int irq, void *device, struct pt_regs *regs)
+{
+ struct device *dev = (struct device*) device;
+ u16 csr0val;
+
+ /* read CSR0 to get interrupt cause */
+
+ csr0val = GetLANCE(dev, LANCE_CSR0);
+
+ /* in case we're not meant... */
+
+ if ((csr0val & CSR0_INTR) == 0)
+ return;
+
+ dev->interrupt = 1;
+
+ /* loop through the interrupt bits until everything is clear */
+
+ do
+ {
+ if ((csr0val & CSR0_IDON) != 0)
+ csr0val = irqstart_handler(dev, csr0val);
+ if ((csr0val & CSR0_RINT) != 0)
+ csr0val = irqrx_handler(dev, csr0val);
+ if ((csr0val & CSR0_TINT) != 0)
+ csr0val = irqtx_handler(dev, csr0val);
+ }
+ while ((csr0val & CSR0_INTR) != 0);
+
+ dev->interrupt = 0;
+}
+
+/* ------------------------------------------------------------------------
+ * driver methods
+ * ------------------------------------------------------------------------ */
+
+/* MCA info */
+
+static int skmca_getinfo(char *buf, int slot, void *d)
+{
+ int len = 0, i;
+ struct device *dev = (struct device*) d;
+ skmca_priv *priv;
+
+ /* can't say anything about an uninitialized device... */
+
+ if (dev == NULL)
+ return len;
+ if (dev->priv == NULL)
+ return len;
+ priv = (skmca_priv*) dev->priv;
+
+ /* print info */
+
+ len += sprintf(buf + len, "IRQ: %d\n", priv->realirq);
+ len += sprintf(buf + len, "Memory: %#lx-%#lx\n", dev->mem_start,
+ dev->mem_end - 1);
+ len += sprintf(buf + len, "Transceiver: %s\n", MediaNames[priv->medium]);
+ len += sprintf(buf + len, "Device: %s\n", dev->name);
+ len += sprintf(buf + len, "MAC address:");
+ for (i = 0; i < 6; i ++ )
+ len += sprintf( buf+len, " %02x", dev->dev_addr[i] );
+ buf[len++] = '\n';
+ buf[len] = 0;
+
+ return len;
+}
+
+/* open driver. Means also initialization and start of LANCE */
+
+static int skmca_open(struct device *dev)
+{
+ int result;
+ skmca_priv *priv = (skmca_priv*) dev->priv;
+
+ /* register resources - only necessary for IRQ */
+ result = request_irq(priv->realirq, irq_handler, SA_SHIRQ | SA_SAMPLE_RANDOM,
+ "sk_mca", dev);
+ if (result != 0)
+ {
+ printk("%s: failed to register irq %d\n", dev->name, dev->irq);
+ return result;
+ }
+ dev->irq = priv->realirq;
+
+ /* set up the card and LANCE */
+ InitBoard(dev);
+
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+/* close driver. Shut down board and free allocated resources */
+
+static int skmca_close(struct device *dev)
+{
+ /* turn off board */
+ DeinitBoard(dev);
+
+ /* release resources */
+ if (dev->irq != 0)
+ free_irq(dev->irq, dev);
+ dev->irq = 0;
+
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+/* transmit a block. */
+
+static int skmca_tx(struct sk_buff *skb, struct device *dev)
+{
+ skmca_priv *priv = (skmca_priv*) dev->priv;
+ LANCE_TxDescr descr;
+ unsigned int address;
+ int tmplen, retval = 0;
+ unsigned long flags;
+
+ /* if we get called with a NULL descriptor, the Ethernet layer thinks
+ our card is stuck an we should reset it. We'll do this completely: */
+
+ if (skb == NULL)
+ {
+ DeinitBoard(dev);
+ InitBoard(dev);
+ return 0; /* don't try to free the block here ;-) */
+ }
+
+ /* is there space in the Tx queue ? If no, the upper layer gave us a
+ packet in spite of us not being ready and is really in trouble.
+ We'll do the dropping for him: */
+ if (priv->txbusy >= TXCOUNT)
+ {
+ priv->stat.tx_dropped++;
+ retval = -EIO;
+ goto tx_done;
+ }
+
+ /* get TX descriptor */
+ address = RAM_TXBASE + (priv->nexttxput * sizeof(LANCE_TxDescr));
+ memcpy_fromio(&descr, dev->mem_start + address, sizeof(LANCE_TxDescr));
+
+ /* enter packet length as 2s complement - assure minimum length */
+ tmplen = skb->len;
+ if (tmplen < 60)
+ tmplen = 60;
+ descr.Len = 65536 - tmplen;
+
+ /* copy filler into RAM - in case we're filling up...
+ we're filling a bit more than necessary, but that doesn't harm
+ since the buffer is far larger... */
+ if (tmplen > skb->len)
+ {
+ char *fill = "NetBSD is a nice OS too! ";
+ unsigned int destoffs = 0, l = strlen(fill);
+
+ while (destoffs < tmplen)
+ {
+ memcpy_toio(dev->mem_start + descr.LowAddr + destoffs, fill, l);
+ destoffs += l;
+ }
+ }
+
+ /* do the real data copying */
+ memcpy_toio(dev->mem_start + descr.LowAddr, skb->data, skb->len);
+
+ /* hand descriptor over to LANCE - this is the first and last chunk */
+ descr.Flags = TXDSCR_FLAGS_OWN | TXDSCR_FLAGS_STP | TXDSCR_FLAGS_ENP;
+
+#ifdef DEBUG
+ PrTime(); printk("Send packet on descr %d len %d\n", priv->nexttxput, skb->len);
+#endif
+
+ /* one more descriptor busy */
+ save_flags(flags);
+ cli();
+ priv->nexttxput++;
+ if (priv->nexttxput >= TXCOUNT)
+ priv->nexttxput = 0;
+ priv->txbusy++;
+ dev->tbusy = (priv->txbusy >= TXCOUNT);
+
+ /* write descriptor back to RAM */
+ memcpy_toio(dev->mem_start + address, &descr, sizeof(LANCE_TxDescr));
+
+ /* if no descriptors were active, give the LANCE a hint to read it
+ immediately */
+
+ if (priv->txbusy == 0)
+ SetLANCE(dev, LANCE_CSR0, CSR0_INEA | CSR0_TDMD);
+
+ restore_flags(flags);
+
+tx_done:
+
+ /* When did that change exactly ? */
+
+#if LINUX_VERSION_CODE >= 0x020200
+ dev_kfree_skb(skb);
+#else
+ dev_kfree_skb(skb, FREE_WRITE);
+#endif
+ return retval;
+}
+
+/* return pointer to Ethernet statistics */
+
+static struct enet_statistics *skmca_stats(struct device *dev)
+{
+ skmca_priv *priv = (skmca_priv*) dev->priv;
+
+ return &(priv->stat);
+}
+
+/* we don't support runtime reconfiguration, since am MCA card can
+ be unambigously identified by its POS registers. */
+
+static int skmca_config(struct device *dev, struct ifmap *map)
+{
+ return 0;
+}
+
+/* switch receiver mode. We use the LANCE's multicast filter to prefilter
+ multicast addresses. */
+
+static void skmca_set_multicast_list(struct device *dev)
+{
+ LANCE_InitBlock block;
+
+ /* first stop the LANCE... */
+ StopLANCE(dev);
+
+ /* ...then modify the initialization block... */
+ memcpy_fromio(&block, dev->mem_start + RAM_INITBASE, sizeof(block));
+ if (dev->flags & IFF_PROMISC)
+ block.Mode |= LANCE_INIT_PROM;
+ else
+ block.Mode &= ~LANCE_INIT_PROM;
+
+ if (dev->flags & IFF_ALLMULTI) /* get all multicasts */
+ {
+ memset(block.LAdrF, 8, 0xff);
+ }
+ else /* get selected/no multicasts */
+ {
+ struct dev_mc_list *mptr;
+ int code;
+
+ memset(block.LAdrF, 8, 0x00);
+ for (mptr = dev->mc_list; mptr != NULL; mptr = mptr->next)
+ {
+ code = GetHash(mptr->dmi_addr);
+ block.LAdrF[(code >> 3) & 7] |= 1 << (code & 7);
+ }
+ }
+
+ memcpy_toio(dev->mem_start + RAM_INITBASE, &block, sizeof(block));
+
+ /* ...then reinit LANCE with the correct flags */
+ InitLANCE(dev);
+}
+
+/* ------------------------------------------------------------------------
+ * hardware check
+ * ------------------------------------------------------------------------ */
+
+#ifdef MODULE
+static int startslot; /* counts through slots when probing multiple devices */
+#else
+#define startslot 0 /* otherwise a dummy, since there is only eth0 in-kern*/
+#endif
+
+int skmca_probe(struct device *dev)
+{
+ int force_detect = 0;
+ int junior, slot, i;
+ int base = 0, irq = 0;
+ skmca_priv *priv;
+ skmca_medium medium;
+
+ /* can't work without an MCA bus ;-) */
+
+ if (MCA_bus == 0)
+ return ENODEV;
+
+ /* start address of 1 --> forced detection */
+
+ if (dev->mem_start == 1)
+ force_detect = 1;
+
+ /* search through slots */
+
+ if (dev != NULL)
+ {
+ base = dev->mem_start;
+ irq = dev->irq;
+ }
+ slot = dofind(&junior, startslot);
+
+ while (slot != -1)
+ {
+ /* deduce card addresses */
+
+ getaddrs(slot, junior, &base, &irq, &medium);
+
+#if 0
+ /* this should work, but it doesn't with 2.2.9 :-(
+ somehow 'mca_is_adapter_used()' is missing in kernel syms... */
+#if LINUX_VERSION_CODE >= 0x020200
+ /* slot already in use ? */
+
+ if (mca_is_adapter_used(slot))
+ {
+ slot = dofind(&junior, slot + 1);
+ continue;
+ }
+#endif
+#endif
+
+ /* were we looking for something different ? */
+
+ if ((dev->irq != 0) || (dev->mem_start != 0))
+ {
+ if ((dev->irq != 0) && (dev->irq != irq))
+ {
+ slot = dofind(&junior, slot + 1);
+ continue;
+ }
+ if ((dev->mem_start != 0) && (dev->mem_start != base))
+ {
+ slot = dofind(&junior, slot + 1);
+ continue;
+ }
+ }
+
+ /* found something that matches */
+
+ break;
+ }
+
+ /* nothing found ? */
+
+ if (slot == -1)
+ return ((base != 0) || (irq != 0)) ? ENXIO : ENODEV;
+
+ /* make procfs entries */
+
+ if (junior)
+ mca_set_adapter_name(slot, "SKNET junior MC2 Ethernet Adapter");
+ else
+ mca_set_adapter_name(slot, "SKNET MC2+ Ethernet Adapter");
+ mca_set_adapter_procfn(slot, (MCA_ProcFn) skmca_getinfo, dev);
+
+#if LINUX_VERSION_CODE >= 0x020200
+ mca_mark_as_used(slot);
+#endif
+
+ /* announce success */
+ printk("%s: SKNet %s adapter found in slot %d\n", dev->name,
+ junior ? "Junior MC2" : "MC2+", slot + 1);
+
+ /* allocate structure */
+ priv = dev->priv = (skmca_priv*) kmalloc(sizeof(skmca_priv), GFP_KERNEL);
+ priv->slot = slot;
+ priv->macbase = base + 0x3fc0;
+ priv->ioregaddr = base + 0x3ff0;
+ priv->ctrladdr = base + 0x3ff2;
+ priv->cmdaddr = base + 0x3ff3;
+ priv->realirq = irq;
+ priv->medium = medium;
+ memset(&(priv->stat), 0, sizeof(struct enet_statistics));
+
+ /* set base + irq for this device (irq not allocated so far) */
+ dev->irq = 0;
+ dev->mem_start = base;
+ dev->mem_end = base + 0x4000;
+
+ /* set methods */
+ dev->open = skmca_open;
+ dev->stop = skmca_close;
+ dev->set_config = skmca_config;
+ dev->hard_start_xmit = skmca_tx;
+ dev->do_ioctl = NULL;
+ dev->get_stats = skmca_stats;
+ dev->set_multicast_list = skmca_set_multicast_list;
+ dev->flags |= IFF_MULTICAST;
+
+ /* generic setup */
+ ether_setup(dev);
+ dev->interrupt = 0;
+ dev->tbusy = 0;
+ dev->start = 0;
+
+ /* copy out MAC address */
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = readb(priv->macbase + (i << 1));
+
+ /* print config */
+ printk("%s: IRQ %d, memory %#lx-%#lx, "
+ "MAC address %02x:%02x:%02x:%02x:%02x:%02x.\n",
+ dev->name, priv->realirq, dev->mem_start, dev->mem_end - 1,
+ dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+ dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+ printk("%s: %s medium\n", dev->name, MediaNames[priv->medium]);
+
+ /* reset board */
+
+ ResetBoard(dev);
+
+#ifdef MODULE
+ startslot = slot + 1;
+#endif
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------------
+ * modularization support
+ * ------------------------------------------------------------------------ */
+
+#ifdef MODULE
+
+#define DEVMAX 5
+
+static char NameSpace[8 * DEVMAX];
+static struct device moddevs[DEVMAX] =
+ {{NameSpace + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
+ {NameSpace + 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
+ {NameSpace + 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
+ {NameSpace + 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe},
+ {NameSpace + 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, skmca_probe}};
+
+int irq=0;
+int io=0;
+
+int init_module(void)
+{
+ int z, res;
+
+ startslot = 0;
+ for (z = 0; z < DEVMAX; z++)
+ {
+ strcpy(moddevs[z].name, " ");
+ res = register_netdev(moddevs + z);
+ if (res != 0)
+ return (z > 0) ? 0 : -EIO;
+ }
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ struct device *dev;
+ skmca_priv *priv;
+ int z;
+
+ if (MOD_IN_USE)
+ {
+ printk("cannot unload, module in use\n");
+ return;
+ }
+
+ for (z = 0; z < DEVMAX; z++)
+ {
+ dev = moddevs + z;
+ if (dev->priv != NULL)
+ {
+ priv = (skmca_priv*) dev->priv;
+ DeinitBoard(dev);
+ if (dev->irq != 0)
+ free_irq(dev->irq, dev);
+ dev->irq = 0;
+ unregister_netdev(dev);
+#if LINUX_VERSION_CODE >= 0x020200
+ mca_mark_as_unused(priv->slot);
+#endif
+ mca_set_adapter_procfn(priv->slot, NULL, NULL);
+ kfree_s(dev->priv, sizeof(skmca_priv));
+ dev->priv = NULL;
+ }
+ }
+}
+#endif /* MODULE */
diff --git a/drivers/net/sk_mca.h b/drivers/net/sk_mca.h
new file mode 100644
index 000000000..f45aadf61
--- /dev/null
+++ b/drivers/net/sk_mca.h
@@ -0,0 +1,174 @@
+#ifndef _SK_MCA_INCLUDE_
+#define _SK_MCA_INCLUDE_
+
+#ifdef _SK_MCA_DRIVER_
+
+/* Adapter ID's */
+#define SKNET_MCA_ID 0x6afd
+#define SKNET_JUNIOR_MCA_ID 0x6be9
+
+/* media enumeration - defined in a way that it fits onto the MC2+'s
+ POS registers... */
+
+typedef enum {Media_10Base2, Media_10BaseT,
+ Media_10Base5, Media_Unknown, Media_Count} skmca_medium;
+
+/* private structure */
+typedef struct
+ {
+ unsigned int slot; /* MCA-Slot-# */
+ unsigned int macbase; /* base address of MAC address PROM */
+ unsigned int ioregaddr; /* address of I/O-register (Lo) */
+ unsigned int ctrladdr; /* address of control/stat register */
+ unsigned int cmdaddr; /* address of I/O-command register */
+ int nextrx; /* index of next RX descriptor to
+ be read */
+ int nexttxput; /* index of next free TX descriptor */
+ int nexttxdone; /* index of next TX descriptor to
+ be finished */
+ int txbusy; /* # of busy TX descriptors */
+ struct enet_statistics stat; /* packet statistics */
+ int realirq; /* memorizes actual IRQ, even when
+ currently not allocated */
+ skmca_medium medium; /* physical cannector */
+ } skmca_priv;
+
+/* card registers: control/status register bits */
+
+#define CTRL_ADR_DATA 0 /* Bit 0 = 0 ->access data register */
+#define CTRL_ADR_RAP 1 /* Bit 0 = 1 ->access RAP register */
+#define CTRL_RW_WRITE 0 /* Bit 1 = 0 ->write register */
+#define CTRL_RW_READ 2 /* Bit 1 = 1 ->read register */
+#define CTRL_RESET_ON 0 /* Bit 3 = 0 ->reset board */
+#define CTRL_RESET_OFF 8 /* Bit 3 = 1 ->no reset of board */
+
+#define STAT_ADR_DATA 0 /* Bit 0 of ctrl register read back */
+#define STAT_ADR_RAP 1
+#define STAT_RW_WRITE 0 /* Bit 1 of ctrl register read back */
+#define STAT_RW_READ 2
+#define STAT_RESET_ON 0 /* Bit 3 of ctrl register read back */
+#define STAT_RESET_OFF 8
+#define STAT_IRQ_ACT 0 /* interrupt pending */
+#define STAT_IRQ_NOACT 16 /* no interrupt pending */
+#define STAT_IO_NOBUSY 0 /* no transfer busy */
+#define STAT_IO_BUSY 32 /* transfer busy */
+
+/* I/O command register bits */
+
+#define IOCMD_GO 128 /* Bit 7 = 1 -> start register xfer */
+
+/* LANCE registers */
+
+#define LANCE_CSR0 0 /* Status/Control */
+
+#define CSR0_ERR 0x8000 /* general error flag */
+#define CSR0_BABL 0x4000 /* transmitter timeout */
+#define CSR0_CERR 0x2000 /* collision error */
+#define CSR0_MISS 0x1000 /* lost Rx block */
+#define CSR0_MERR 0x0800 /* memory access error */
+#define CSR0_RINT 0x0400 /* receiver interrupt */
+#define CSR0_TINT 0x0200 /* transmitter interrupt */
+#define CSR0_IDON 0x0100 /* initialization done */
+#define CSR0_INTR 0x0080 /* general interrupt flag */
+#define CSR0_INEA 0x0040 /* interrupt enable */
+#define CSR0_RXON 0x0020 /* receiver enabled */
+#define CSR0_TXON 0x0010 /* transmitter enabled */
+#define CSR0_TDMD 0x0008 /* force transmission now */
+#define CSR0_STOP 0x0004 /* stop LANCE */
+#define CSR0_STRT 0x0002 /* start LANCE */
+#define CSR0_INIT 0x0001 /* read initialization block */
+
+#define LANCE_CSR1 1 /* addr bit 0..15 of initialization */
+#define LANCE_CSR2 2 /* 16..23 block */
+
+#define LANCE_CSR3 3 /* Bus control */
+#define CSR3_BCON_HOLD 0 /* Bit 0 = 0 -> BM1,BM0,HOLD */
+#define CSR3_BCON_BUSRQ 1 /* Bit 0 = 1 -> BUSAK0,BYTE,BUSRQ */
+#define CSR3_ALE_HIGH 0 /* Bit 1 = 0 -> ALE asserted high */
+#define CSR3_ALE_LOW 2 /* Bit 1 = 1 -> ALE asserted low */
+#define CSR3_BSWAP_OFF 0 /* Bit 2 = 0 -> no byte swap */
+#define CSR3_BSWAP_ON 0 /* Bit 2 = 1 -> byte swap */
+
+/* LANCE structures */
+
+typedef struct /* LANCE initialization block */
+ {
+ u16 Mode; /* mode flags */
+ u8 PAdr[6]; /* MAC address */
+ u8 LAdrF[8]; /* Multicast filter */
+ u32 RdrP; /* Receive descriptor */
+ u32 TdrP; /* Transmit descriptor */
+ } LANCE_InitBlock;
+
+/* Mode flags init block */
+
+#define LANCE_INIT_PROM 0x8000 /* enable promiscous mode */
+#define LANCE_INIT_INTL 0x0040 /* internal loopback */
+#define LANCE_INIT_DRTY 0x0020 /* disable retry */
+#define LANCE_INIT_COLL 0x0010 /* force collision */
+#define LANCE_INIT_DTCR 0x0008 /* disable transmit CRC */
+#define LANCE_INIT_LOOP 0x0004 /* loopback */
+#define LANCE_INIT_DTX 0x0002 /* disable transmitter */
+#define LANCE_INIT_DRX 0x0001 /* disable receiver */
+
+typedef struct /* LANCE Tx descriptor */
+ {
+ u16 LowAddr; /* bit 0..15 of address */
+ u16 Flags; /* bit 16..23 of address + Flags */
+ u16 Len; /* 2s complement of packet length */
+ u16 Status; /* Result of transmission */
+ } LANCE_TxDescr;
+
+#define TXDSCR_FLAGS_OWN 0x8000 /* LANCE owns descriptor */
+#define TXDSCR_FLAGS_ERR 0x4000 /* summary error flag */
+#define TXDSCR_FLAGS_MORE 0x1000 /* more than one retry needed? */
+#define TXDSCR_FLAGS_ONE 0x0800 /* one retry? */
+#define TXDSCR_FLAGS_DEF 0x0400 /* transmission deferred? */
+#define TXDSCR_FLAGS_STP 0x0200 /* first packet in chain? */
+#define TXDSCR_FLAGS_ENP 0x0100 /* last packet in chain? */
+
+#define TXDSCR_STATUS_BUFF 0x8000 /* buffer error? */
+#define TXDSCR_STATUS_UFLO 0x4000 /* silo underflow during transmit? */
+#define TXDSCR_STATUS_LCOL 0x1000 /* late collision? */
+#define TXDSCR_STATUS_LCAR 0x0800 /* loss of carrier? */
+#define TXDSCR_STATUS_RTRY 0x0400 /* retry error? */
+
+typedef struct /* LANCE Rx descriptor */
+ {
+ u16 LowAddr; /* bit 0..15 of address */
+ u16 Flags; /* bit 16..23 of address + Flags */
+ u16 MaxLen; /* 2s complement of buffer length */
+ u16 Len; /* packet length */
+ } LANCE_RxDescr;
+
+#define RXDSCR_FLAGS_OWN 0x8000 /* LANCE owns descriptor */
+#define RXDSCR_FLAGS_ERR 0x4000 /* summary error flag */
+#define RXDSCR_FLAGS_FRAM 0x2000 /* framing error flag */
+#define RXDSCR_FLAGS_OFLO 0x1000 /* FIFO overflow? */
+#define RXDSCR_FLAGS_CRC 0x0800 /* CRC error? */
+#define RXDSCR_FLAGS_BUFF 0x0400 /* buffer error? */
+#define RXDSCR_FLAGS_STP 0x0200 /* first packet in chain? */
+#define RXDCSR_FLAGS_ENP 0x0100 /* last packet in chain? */
+
+/* RAM layout */
+
+#define TXCOUNT 4 /* length of TX descriptor queue */
+#define LTXCOUNT 2 /* log2 of it */
+#define RXCOUNT 4 /* length of RX descriptor queue */
+#define LRXCOUNT 2 /* log2 of it */
+
+#define RAM_INITBASE 0 /* LANCE init block */
+#define RAM_TXBASE 24 /* Start of TX descriptor queue */
+#define RAM_RXBASE \
+(RAM_TXBASE + (TXCOUNT * 8)) /* Start of RX descriptor queue */
+#define RAM_DATABASE \
+(RAM_RXBASE + (RXCOUNT * 8)) /* Start of data area for frames */
+#define RAM_BUFSIZE 1580 /* max. frame size - should never be
+ reached */
+
+#endif /* _SK_MCA_DRIVER_ */
+
+extern int skmca_probe(struct device *);
+
+
+#endif /* _SK_MCA_INCLUDE_ */ \ No newline at end of file
diff --git a/drivers/sbus/char/aurora.c b/drivers/sbus/char/aurora.c
new file mode 100644
index 000000000..607b0f230
--- /dev/null
+++ b/drivers/sbus/char/aurora.c
@@ -0,0 +1,2373 @@
+/*
+ * linux/drivers/sbus/char/aurora.c -- Aurora multiport driver
+ *
+ * Copyright (c) 1999 by Oliver Aldulea (oli@bv.ro)
+ *
+ * This code is based on the RISCom/8 multiport serial driver written
+ * by Dmitry Gorodchanin (pgmdsg@ibi.com), based on the Linux serial
+ * driver, written by Linus Torvalds, Theodore T'so and others.
+ * The Aurora multiport programming info was obtained mainly from the
+ * Cirrus Logic CD180 documentation (available on the web), and by
+ * doing heavy tests on the board. Many thanks to Eddie C. Dost for the
+ * help on the sbus interface.
+ *
+ * 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 1.0
+ *
+ * This is the first public release.
+ *
+ * Most of the information you need is in the aurora.h file. Please
+ * read that file before reading this one.
+ *
+ * Several parts of the code do not have comments yet.
+ */
+
+#include <linux/module.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#ifdef AURORA_INT_DEBUG
+#include <linux/timer.h>
+#endif
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/config.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/tqueue.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/oplib.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/kdebug.h>
+#include <asm/sbus.h>
+#include <asm/uaccess.h>
+
+#include "aurora.h"
+#include "cd180.h"
+
+unsigned char irqs[4] = {
+ 0, 0, 0, 0
+ };
+
+#ifdef AURORA_INT_DEBUG
+int irqhit=0;
+#endif
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define AURORA_TYPE_NORMAL 1
+
+static struct tty_driver aurora_driver;
+static struct Aurora_board aurora_board[AURORA_NBOARD] = {
+ {0,},
+};
+
+static struct Aurora_port aurora_port[AURORA_TNPORTS] = {
+ { 0, },
+};
+
+/* no longer used. static struct Aurora_board * IRQ_to_board[16] = { NULL, } ;*/
+static unsigned char * tmp_buf = NULL;
+static DECLARE_MUTEX(tmp_buf_sem);
+static int aurora_refcount = 0;
+static struct tty_struct * aurora_table[AURORA_TNPORTS] = { NULL, };
+static struct termios * aurora_termios[AURORA_TNPORTS] = { NULL, };
+static struct termios * aurora_termios_locked[AURORA_TNPORTS] = { NULL, };
+
+DECLARE_TASK_QUEUE(tq_aurora);
+
+/* Yes, the board can support 115.2 bit rates, but only on a few ports. The
+ * total badwidth of one chip (ports 0-7 or 8-15) is equal to OSC_FREQ div
+ * 16. In case of my board, each chip can take 6 channels of 115.2 kbaud.
+ * This information is not well-tested.
+ */
+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 inline int aurora_paranoia_check(struct Aurora_port const * port,
+ kdev_t device, const char *routine)
+{
+#ifdef AURORA_PARANOIA_CHECK
+ static const char *badmagic =
+ KERN_DEBUG "aurora: Warning: bad aurora port magic number for device %s in %s\n";
+ static const char *badinfo =
+ KERN_DEBUG "aurora: Warning: null aurora port for device %s in %s\n";
+
+ if (!port) {
+ printk(badinfo, kdevname(device), routine);
+ return 1;
+ }
+ if (port->magic != AURORA_MAGIC) {
+ printk(badmagic, kdevname(device), routine);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/*
+ *
+ * Service functions for aurora driver.
+ *
+ */
+
+/* Get board number from pointer */
+extern inline int board_No (struct Aurora_board const * bp)
+{
+ return bp - aurora_board;
+}
+
+/* Get port number from pointer */
+extern inline int port_No (struct Aurora_port const * port)
+{
+ return AURORA_PORT(port - aurora_port);
+}
+
+/* Get pointer to board from pointer to port */
+extern inline struct Aurora_board * port_Board(struct Aurora_port const * port)
+{
+ return &aurora_board[AURORA_BOARD(port - aurora_port)];
+}
+
+/* Wait for Channel Command Register ready */
+extern inline void aurora_wait_CCR(struct aurora_reg128 * r)
+{
+ unsigned long delay;
+
+#ifdef AURORA_DEBUG
+printk("aurora_wait_CCR\n");
+#endif
+ /* FIXME: need something more descriptive than 100000 :) */
+ for (delay = 100000; delay; delay--)
+ if (!r->r[CD180_CCR])
+ return;
+ printk(KERN_DEBUG "aurora: Timeout waiting for CCR.\n");
+}
+
+/*
+ * aurora probe functions.
+ */
+
+/* Must be called with enabled interrupts */
+extern inline void aurora_long_delay(unsigned long delay)
+{
+ unsigned long i;
+#ifdef AURORA_DEBUG
+printk("aurora_long_delay: start\n");
+#endif
+ for (i = jiffies + delay; i > jiffies; ) ;
+#ifdef AURORA_DEBUG
+printk("aurora_long_delay: end\n");
+#endif
+}
+
+/* Reset and setup CD180 chip */
+static int aurora_init_CD180(struct Aurora_board * bp, int chip)
+{
+ unsigned long flags;
+ int id;
+
+#ifdef AURORA_DEBUG
+printk("aurora_init_CD180: start %d:%d\n",board_No(bp),chip);
+#endif
+ save_flags(flags); cli();
+ bp->r[chip]->r[CD180_CAR]=0;
+ bp->r[chip]->r[CD180_GSVR]=0;
+ aurora_wait_CCR(bp->r[chip]); /* Wait for CCR ready */
+ bp->r[chip]->r[CD180_CCR]=CCR_HARDRESET; /* Reset CD180 chip */
+ udelay(1);
+ sti();
+ id=1000;
+ while((--id)&&(bp->r[chip]->r[CD180_GSVR]!=0xff))udelay(100);
+ if(!id) {
+ printk(KERN_ERR "aurora%d: Chip %d failed init.\n",board_No(bp),chip);
+ restore_flags(flags);
+ return(-1);
+ }
+ cli();
+ bp->r[chip]->r[CD180_GSVR]=(board_No(bp)<<5)|((chip+1)<<3); /* Set ID for this chip */
+ bp->r[chip]->r[CD180_MSMR]=0x80|bp->ACK_MINT; /* Prio for modem intr */
+ bp->r[chip]->r[CD180_TSMR]=0x80|bp->ACK_TINT; /* Prio for transmitter intr */
+ bp->r[chip]->r[CD180_RSMR]=0x80|bp->ACK_RINT; /* Prio for receiver intr */
+ /* Setting up prescaler. We need 4 tick per 1 ms */
+ bp->r[chip]->r[CD180_PPRH]=(bp->oscfreq/(1000000/AURORA_TPS)) >> 8;
+ bp->r[chip]->r[CD180_PPRL]=(bp->oscfreq/(1000000/AURORA_TPS)) & 0xff;
+
+ bp->r[chip]->r[CD180_SRCR]=SRCR_AUTOPRI|SRCR_GLOBPRI;
+
+ id=bp->r[chip]->r[CD180_GFRCR];
+ printk(KERN_INFO "aurora%d: Chip %d id %02x: ",board_No(bp),chip,id);
+ if(bp->r[chip]->r[CD180_SRCR]&128)
+ switch(id){
+ case 0x82:printk("CL-CD1864 rev A\n");break;
+ case 0x83:printk("CL-CD1865 rev A\n");break;
+ case 0x84:printk("CL-CD1865 rev B\n");break;
+ case 0x85:printk("CL-CD1865 rev C\n");break;
+ default:printk("Unknown.\n");
+ }else
+ switch(id){
+ case 0x81:printk("CL-CD180 rev B\n");break;
+ case 0x82:printk("CL-CD180 rev C\n");break;
+ default:printk("Unknown.\n");
+ };
+ restore_flags(flags);
+#ifdef AURORA_DEBUG
+printk("aurora_init_CD180: end\n");
+#endif
+ return 0;
+}
+
+static int valid_irq(unsigned char irq)
+{
+int i;
+for(i=0;i<TYPE_1_IRQS;i++)
+ if (type_1_irq[i]==irq) return 1;
+return 0;
+}
+
+static void aurora_interrupt(int irq, void * dev_id, struct pt_regs * regs);
+
+/* Main probing routine, also sets irq. */
+static int aurora_probe(void) {
+ struct linux_sbus *sbus;
+ struct linux_sbus_device *sdev;
+ int grrr;
+ char buf[30];
+ int bn=0;
+ struct Aurora_board *bp;
+
+ for_each_sbus(sbus) {
+ for_each_sbusdev(sdev, sbus) {
+/* printk("Try: %x %s\n",sdev,sdev->prom_name);*/
+ if (!strcmp(sdev->prom_name, "sio16")) {
+ #ifdef AURORA_DEBUG
+ printk(KERN_INFO "aurora: sio16 at %p\n",sdev);
+ #endif
+ prom_apply_sbus_ranges(sdev->my_bus, sdev->reg_addrs, sdev->num_registers, sdev);
+ if((sdev->reg_addrs[0].reg_size!=1)&&(sdev->reg_addrs[1].reg_size!=128)&&
+ (sdev->reg_addrs[2].reg_size!=128)&&(sdev->reg_addrs[3].reg_size!=4)){
+ printk(KERN_ERR "aurora%d: registers' sizes do not match.\n",bn);
+ break;
+ }
+ bp=&aurora_board[bn];
+ bp->r0 = (struct aurora_reg1 *) sparc_alloc_io(sdev->reg_addrs[0].phys_addr, 0,
+ sdev->reg_addrs[0].reg_size, "sio16",sdev->reg_addrs[0].which_io, 0x0);
+ if (!bp->r0) {
+ printk(KERN_ERR "aurora%d: can't map reg_addrs[0]\n",bn);
+ break;
+ }
+ #ifdef AURORA_DEBUG
+ printk("Map reg 0: %x\n",bp->r0);
+ #endif
+ bp->r[0] = (struct aurora_reg128 *) sparc_alloc_io(sdev->reg_addrs[1].phys_addr, 0,
+ sdev->reg_addrs[1].reg_size, "sio16", sdev->reg_addrs[1].which_io, 0x0);
+ if (!bp->r[0]) {
+ printk(KERN_ERR "aurora%d: can't map reg_addrs[1]\n",bn);
+ break;
+ }
+ #ifdef AURORA_DEBUG
+ printk("Map reg 1: %x\n",bp->r[0]);
+ #endif
+ bp->r[1] = (struct aurora_reg128 *) sparc_alloc_io(sdev->reg_addrs[2].phys_addr, 0,
+ sdev->reg_addrs[2].reg_size, "sio16", sdev->reg_addrs[2].which_io, 0x0);
+ if (!bp->r[1]) {
+ printk(KERN_ERR "aurora%d: can't map reg_addrs[2]\n",bn);
+ break;
+ }
+ #ifdef AURORA_DEBUG
+ printk("Map reg 2: %x\n",bp->r[1]);
+ #endif
+ bp->r3 = (struct aurora_reg4 *) sparc_alloc_io(sdev->reg_addrs[3].phys_addr, 0,
+ sdev->reg_addrs[3].reg_size, "sio16", sdev->reg_addrs[3].which_io, 0x0);
+ if (!bp->r3) {
+ printk(KERN_ERR "aurora%d: can't map reg_addrs[3]\n",bn);
+ break;
+ }
+ #ifdef AURORA_DEBUG
+ printk("Map reg 3: %x\n",bp->r3);
+ #endif
+ /* Variables setup */
+ bp->flags = 0;
+ #ifdef AURORA_DEBUG
+ grrr=prom_getint(sdev->prom_node,"intr");
+ printk("intr pri %d\n",grrr);
+ #endif
+ if ((bp->irq=irqs[bn]) && valid_irq(bp->irq) &&
+ !request_irq(bp->irq|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp)) {
+ free_irq(bp->irq|0x30, bp);
+ } else
+ if ((bp->irq=prom_getint(sdev->prom_node, "bintr")) && valid_irq(bp->irq) &&
+ !request_irq(bp->irq|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp)) {
+ free_irq(bp->irq|0x30, bp);
+ } else
+ if ((bp->irq=prom_getint(sdev->prom_node, "intr")) && valid_irq(bp->irq) &&
+ !request_irq(bp->irq|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp)) {
+ free_irq(bp->irq|0x30, bp);
+ } else
+ for(grrr=0;grrr<TYPE_1_IRQS;grrr++) {
+ if ((bp->irq=type_1_irq[grrr])&&!request_irq(bp->irq|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp)) {
+ free_irq(bp->irq|0x30, bp);
+ break;
+ } else {
+ printk(KERN_ERR "aurora%d: Could not get an irq for this board !!!\n",bn);
+ bp->flags=0xff;
+ }
+ }
+ if(bp->flags==0xff)break;
+ printk(KERN_INFO "aurora%d: irq %d\n",bn,bp->irq&0x0f);
+ buf[0]=0;
+ grrr=prom_getproperty(sdev->prom_node,"dtr_rts",buf,sizeof(buf));
+ if(!strcmp(buf,"swapped")){
+ printk(KERN_INFO "aurora%d: Swapped DTR and RTS\n",bn);
+ bp->DTR=MSVR_RTS;
+ bp->RTS=MSVR_DTR;
+ bp->MSVDTR=CD180_MSVRTS;
+ bp->MSVRTS=CD180_MSVDTR;
+ bp->flags|=AURORA_BOARD_DTR_FLOW_OK;
+ }else{
+ #ifdef AURORA_FORCE_DTR_FLOW
+ printk(KERN_INFO "aurora%d: Forcing swapped DTR-RTS\n",bn);
+ bp->DTR=MSVR_RTS;
+ bp->RTS=MSVR_DTR;
+ bp->MSVDTR=CD180_MSVRTS;
+ bp->MSVRTS=CD180_MSVDTR;
+ bp->flags|=AURORA_BOARD_DTR_FLOW_OK;
+ #else
+ printk(KERN_INFO "aurora%d: Normal DTR and RTS\n",bn);
+ bp->DTR=MSVR_DTR;
+ bp->RTS=MSVR_RTS;
+ bp->MSVDTR=CD180_MSVDTR;
+ bp->MSVRTS=CD180_MSVRTS;
+ #endif
+ }
+ bp->oscfreq=prom_getint(sdev->prom_node,"clk")*100;
+ printk(KERN_INFO "aurora%d: Oscillator: %d Hz\n",bn,bp->oscfreq);
+ grrr=prom_getproperty(sdev->prom_node,"chip",buf,sizeof(buf));
+ printk(KERN_INFO "aurora%d: Chips: %s\n",bn,buf);
+ grrr=prom_getproperty(sdev->prom_node,"manu",buf,sizeof(buf));
+ printk(KERN_INFO "aurora%d: Manufacturer: %s\n",bn,buf);
+ grrr=prom_getproperty(sdev->prom_node,"model",buf,sizeof(buf));
+ printk(KERN_INFO "aurora%d: Model: %s\n",bn,buf);
+ grrr=prom_getproperty(sdev->prom_node,"rev",buf,sizeof(buf));
+ printk(KERN_INFO "aurora%d: Revision: %s\n",bn,buf);
+ grrr=prom_getproperty(sdev->prom_node,"mode",buf,sizeof(buf));
+ printk(KERN_INFO "aurora%d: Mode: %s\n",bn,buf);
+ #ifdef MODULE
+ bp->count=0;
+ #endif
+ bp->flags = AURORA_BOARD_PRESENT;
+ /* hardware ack */
+ bp->ACK_MINT=1;
+ bp->ACK_TINT=2;
+ bp->ACK_RINT=3;
+ bn++;
+ }
+ }
+ }
+ return bn;
+}
+
+static void aurora_release_io_range(struct Aurora_board *bp)
+{
+sparc_free_io(bp->r0,1);
+sparc_free_io(bp->r[0],128);
+sparc_free_io(bp->r[1],128);
+sparc_free_io(bp->r3,4);
+}
+
+extern inline void aurora_mark_event(struct Aurora_port * port, int event)
+{
+#ifdef AURORA_DEBUG
+printk("aurora_mark_event: start\n");
+#endif
+ set_bit(event, &port->event);
+ queue_task(&port->tqueue, &tq_aurora);
+ mark_bh(AURORA_BH);
+#ifdef AURORA_DEBUG
+printk("aurora_mark_event: end\n");
+#endif
+}
+
+extern inline struct Aurora_port * aurora_get_port(struct Aurora_board const * bp,
+ int chip, unsigned char const * what)
+{
+ unsigned char channel;
+ struct Aurora_port * port;
+
+ channel = (chip<<3)|((bp->r[chip]->r[CD180_GSCR]&GSCR_CHAN)>>GSCR_CHAN_OFF);
+ port = &aurora_port[board_No(bp) * AURORA_NPORT * AURORA_NCD180 + channel];
+ if (port->flags & ASYNC_INITIALIZED) {
+ return port;
+ }
+ printk(KERN_DEBUG "aurora%d: %s interrupt from invalid port %d\n",
+ board_No(bp), what, channel);
+ return NULL;
+}
+
+extern inline void aurora_receive_exc(struct Aurora_board const * bp, int chip)
+{
+ struct Aurora_port *port;
+ struct tty_struct *tty;
+ unsigned char status;
+ unsigned char ch;
+
+ if (!(port = aurora_get_port(bp, chip, "Receive_x")))
+ return;
+
+ tty = port->tty;
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ #ifdef AURORA_INTNORM
+ printk("aurora%d: port %d: Working around flip buffer overflow.\n",
+ board_No(bp), port_No(port));
+ #endif
+ return;
+ }
+
+#ifdef AURORA_REPORT_OVERRUN
+ status = bp->r[chip]->r[CD180_RCSR];
+ if (status & RCSR_OE) {
+ port->overrun++;
+#if 1
+ printk("aurora%d: port %d: Overrun. Total %ld overruns.\n",
+ board_No(bp), port_No(port), port->overrun);
+#endif
+ }
+ status &= port->mark_mask;
+#else
+ status = bp->r[chip]->r[CD180_RCSR] & port->mark_mask;
+#endif
+ ch = bp->r[chip]->r[CD180_RDR];
+ if (!status) {
+ return;
+ }
+ if (status & RCSR_TOUT) {
+/* printk("aurora%d: port %d: Receiver timeout. Hardware problems ?\n",
+ board_No(bp), port_No(port));*/
+ return;
+
+ } else if (status & RCSR_BREAK) {
+ printk(KERN_DEBUG "aurora%d: port %d: Handling break...\n",
+ board_No(bp), port_No(port));
+ *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 aurora_receive(struct Aurora_board const * bp, int chip)
+{
+ struct Aurora_port *port;
+ struct tty_struct *tty;
+ unsigned char count,cnt;
+
+ if (!(port = aurora_get_port(bp, chip, "Receive")))
+ return;
+
+ tty = port->tty;
+
+ count = bp->r[chip]->r[CD180_RDCR];
+
+#ifdef AURORA_REPORT_FIFO
+ port->hits[count > 8 ? 9 : count]++;
+#endif
+
+ while (count--) {
+ if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+ #ifdef AURORA_INTNORM
+ printk("aurora%d: port %d: Working around flip buffer overflow.\n",
+ board_No(bp), port_No(port));
+ #endif
+ break;
+ }
+ cnt=bp->r[chip]->r[CD180_RDR];
+ *tty->flip.char_buf_ptr++ = cnt;
+ *tty->flip.flag_buf_ptr++ = 0;
+ tty->flip.count++;
+ }
+ queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+extern inline void aurora_transmit(struct Aurora_board const * bp, int chip)
+{
+ struct Aurora_port *port;
+ struct tty_struct *tty;
+ unsigned char count;
+
+
+ if (!(port = aurora_get_port(bp, chip, "Transmit")))
+ return;
+
+ tty = port->tty;
+
+ if (port->SRER & SRER_TXEMPTY) {
+ /* FIFO drained */
+ bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+ udelay(1);
+ port->SRER &= ~SRER_TXEMPTY;
+ bp->r[chip]->r[CD180_SRER]=port->SRER;
+ return;
+ }
+
+ if ((port->xmit_cnt <= 0 && !port->break_length)
+ || tty->stopped || tty->hw_stopped) {
+ bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+ udelay(1);
+ port->SRER &= ~SRER_TXRDY;
+ bp->r[chip]->r[CD180_SRER]=port->SRER;
+ return;
+ }
+
+ if (port->break_length) {
+ if (port->break_length > 0) {
+ if (port->COR2 & COR2_ETC) {
+ bp->r[chip]->r[CD180_TDR]=CD180_C_ESC;
+ bp->r[chip]->r[CD180_TDR]=CD180_C_SBRK;
+ port->COR2 &= ~COR2_ETC;
+ }
+ count = MIN(port->break_length, 0xff);
+ bp->r[chip]->r[CD180_TDR]=CD180_C_ESC;
+ bp->r[chip]->r[CD180_TDR]=CD180_C_DELAY;
+ bp->r[chip]->r[CD180_TDR]=count;
+ if (!(port->break_length -= count))
+ port->break_length--;
+ } else {
+ bp->r[chip]->r[CD180_TDR]=CD180_C_ESC;
+ bp->r[chip]->r[CD180_TDR]=CD180_C_EBRK;
+ bp->r[chip]->r[CD180_COR2]=port->COR2;
+ aurora_wait_CCR(bp->r[chip]);
+ bp->r[chip]->r[CD180_CCR]=CCR_CORCHG2;
+ port->break_length = 0;
+ }
+ return;
+ }
+
+ count = CD180_NFIFO;
+ do {
+ bp->r[chip]->r[CD180_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) {
+ bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+ udelay(1);
+ port->SRER &= ~SRER_TXRDY;
+ bp->r[chip]->r[CD180_SRER]=port->SRER;
+ }
+ if (port->xmit_cnt <= port->wakeup_chars)
+ aurora_mark_event(port, RS_EVENT_WRITE_WAKEUP);
+}
+
+extern inline void aurora_check_modem(struct Aurora_board const * bp, int chip)
+{
+ struct Aurora_port *port;
+ struct tty_struct *tty;
+ unsigned char mcr;
+
+ if (!(port = aurora_get_port(bp, chip, "Modem")))
+ return;
+
+ tty = port->tty;
+
+ mcr = bp->r[chip]->r[CD180_MCR];
+ if (mcr & MCR_CDCHG) {
+ if (bp->r[chip]->r[CD180_MSVR] & MSVR_CD)
+ wake_up_interruptible(&port->open_wait);
+ else if (!((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+ (port->flags & ASYNC_CALLOUT_NOHUP)))
+ queue_task(&port->tqueue_hangup,
+ &tq_scheduler);
+ }
+
+/* We don't have such things yet. My aurora board has DTR and RTS swapped, but that doesn't count in this driver. Let's hope
+ * Aurora didn't made any boards with CTS or DSR broken...
+ */
+/* #ifdef AURORA_BRAIN_DAMAGED_CTS
+ if (mcr & MCR_CTSCHG) {
+ if (aurora_in(bp, CD180_MSVR) & MSVR_CTS) {
+ tty->hw_stopped = 0;
+ port->SRER |= SRER_TXRDY;
+ if (port->xmit_cnt <= port->wakeup_chars)
+ aurora_mark_event(port, RS_EVENT_WRITE_WAKEUP);
+ } else {
+ tty->hw_stopped = 1;
+ port->SRER &= ~SRER_TXRDY;
+ }
+ bp->r[chip]->r[CD180_SRER, port->SRER);
+ }
+ if (mcr & MCR_DSRCHG) {
+ if (aurora_in(bp, CD180_MSVR) & MSVR_DSR) {
+ tty->hw_stopped = 0;
+ port->SRER |= SRER_TXRDY;
+ if (port->xmit_cnt <= port->wakeup_chars)
+ aurora_mark_event(port, RS_EVENT_WRITE_WAKEUP);
+ } else {
+ tty->hw_stopped = 1;
+ port->SRER &= ~SRER_TXRDY;
+ }
+ bp->r[chip]->r[CD180_SRER, port->SRER);
+ }
+#endif AURORA_BRAIN_DAMAGED_CTS */
+
+ /* Clear change bits */
+ bp->r[chip]->r[CD180_MCR]=0;
+}
+
+/* The main interrupt processing routine */
+static void aurora_interrupt(int irq, void * dev_id, struct pt_regs * regs)
+{
+ unsigned char status;
+ unsigned char ack,chip/*,chip_id*/;
+ struct Aurora_board * bp = (struct Aurora_board *) dev_id;
+ unsigned long loop=0;
+
+ #ifdef AURORA_INT_DEBUG
+ printk("IRQ%d %d\n",irq,++irqhit);
+ #ifdef AURORA_FLOODPRO
+ if (irqhit>=AURORA_FLOODPRO)
+ bp->r0->r=8;
+ #endif
+ #endif
+
+/* old bp = IRQ_to_board[irq&0x0f];*/
+
+ if (!bp || !(bp->flags & AURORA_BOARD_ACTIVE)) {
+ return;
+ }
+
+/* The while() below takes care of this.
+ status=bp->r[0]->r[CD180_SRSR];
+ #ifdef AURORA_INT_DEBUG
+ printk("mumu: %02x\n",status);
+ #endif
+ if (!(status&SRSR_ANYINT)) return; * Nobody has anything to say, so exit *
+*/
+ while ((loop++ < 48)&&(status=bp->r[0]->r[CD180_SRSR]&SRSR_ANYINT)){
+ #ifdef AURORA_INT_DEBUG
+ printk("SRSR: %02x\n",status);
+ #endif
+ if (status&SRSR_REXT) {
+ ack=bp->r3->r[bp->ACK_RINT];
+ #ifdef AURORA_INT_DEBUG
+ printk("R-ACK %02x\n",ack);
+ #endif
+ if ((ack>>5)==board_No(bp)) {
+ if ((chip=((ack>>3)&3)-1) < AURORA_NCD180) {
+ if ((ack&GSVR_ITMASK)==GSVR_IT_RGD) {
+ aurora_receive(bp,chip);
+ bp->r[chip]->r[CD180_EOSRR]=0;
+ } else
+ if ((ack&GSVR_ITMASK)==GSVR_IT_REXC) {
+ aurora_receive_exc(bp,chip);
+ bp->r[chip]->r[CD180_EOSRR]=0;
+ }
+ }
+ }
+ } else
+ if (status&SRSR_TEXT) {
+ ack=bp->r3->r[bp->ACK_TINT];
+ #ifdef AURORA_INT_DEBUG
+ printk("T-ACK %02x\n",ack);
+ #endif
+ if ((ack>>5)==board_No(bp)) {
+ if ((chip=((ack>>3)&3)-1) < AURORA_NCD180) {
+ if ((ack&GSVR_ITMASK)==GSVR_IT_TX) {
+ aurora_transmit(bp,chip);
+ bp->r[chip]->r[CD180_EOSRR]=0;
+ }
+ }
+ }
+ } else
+ if (status&SRSR_MEXT) {
+ ack=bp->r3->r[bp->ACK_MINT];
+ #ifdef AURORA_INT_DEBUG
+ printk("M-ACK %02x\n",ack);
+ #endif
+ if ((ack>>5)==board_No(bp)) {
+ if ((chip=((ack>>3)&3)-1) < AURORA_NCD180) {
+ if ((ack&GSVR_ITMASK)==GSVR_IT_MDM) {
+ aurora_check_modem(bp,chip);
+ bp->r[chip]->r[CD180_EOSRR]=0;
+ }
+ }
+ }
+ }
+ }
+/* I guess this faster code can be used with CD1865, using AUROPRI and GLOBPRI.
+ while ((loop++ < 48)&&(status=bp->r[0]->r[CD180_SRSR]&SRSR_ANYINT)){
+ #ifdef AURORA_INT_DEBUG
+ printk("SRSR: %02x\n",status);
+ #endif
+ ack=bp->r3->r[0];
+ #ifdef AURORA_INT_DEBUG
+ printk("ACK: %02x\n",ack);
+ #endif
+ if ((ack>>5)==board_No(bp)) {
+ if ((chip=((ack>>3)&3)-1) < AURORA_NCD180) {
+ ack&=GSVR_ITMASK;
+ if (ack==GSVR_IT_RGD) {
+ aurora_receive(bp,chip);
+ bp->r[chip]->r[CD180_EOSRR]=0;
+ } else
+ if (ack==GSVR_IT_REXC) {
+ aurora_receive_exc(bp,chip);
+ bp->r[chip]->r[CD180_EOSRR]=0;
+ } else
+ if (ack==GSVR_IT_TX) {
+ aurora_transmit(bp,chip);
+ bp->r[chip]->r[CD180_EOSRR]=0;
+ } else
+ if (ack==GSVR_IT_MDM) {
+ aurora_check_modem(bp,chip);
+ bp->r[chip]->r[CD180_EOSRR]=0;
+ }
+ }
+ }
+ }
+*/
+/* This is the old handling routine, used in riscom8 for only one CD180. I keep it here for reference.
+for(chip=0;chip<AURORA_NCD180;chip++){
+ chip_id=(board_No(bp)<<5)|((chip+1)<<3);
+ loop=0;
+ while ((loop++ < 1) && ((status = bp->r[chip]->r[CD180_SRSR]) &
+ (SRSR_TEXT | SRSR_MEXT | SRSR_REXT))) {
+
+ if (status & SRSR_REXT) {
+ ack = bp->r3->r[bp->ACK_RINT];
+ if (ack == (chip_id | GSVR_IT_RGD)){
+ #ifdef AURORA_INTMSG
+ printk("RX ACK\n");
+ #endif
+ aurora_receive(bp,chip);
+ }
+ else if (ack == (chip_id | GSVR_IT_REXC)){
+ #ifdef AURORA_INTMSG
+ printk("RXC ACK\n");
+ #endif
+ aurora_receive_exc(bp,chip);
+ }
+ else
+ #ifdef AURORA_INTNORM
+ printk("aurora%d-%d: Bad receive ack 0x%02x.\n",
+ board_No(bp), chip, ack)
+ #endif
+ ;
+
+ } else if (status & SRSR_TEXT) {
+ ack = bp->r3->r[bp->ACK_TINT];
+ if (ack == (chip_id | GSVR_IT_TX)){
+ #ifdef AURORA_INTMSG
+ printk("TX ACK\n");
+ #endif
+ aurora_transmit(bp,chip);
+ }
+ else{
+ #ifdef AURORA_INTNORM
+ printk("aurora%d-%d: Bad transmit ack 0x%02x.\n",
+ board_No(bp), chip, ack);
+ #endif
+ }
+
+ } else if (status & SRSR_MEXT) {
+ ack = bp->r3->r[bp->ACK_MINT];
+ if (ack == (chip_id | GSVR_IT_MDM)){
+ #ifdef AURORA_INTMSG
+ printk("MDM ACK\n");
+ #endif
+ aurora_check_modem(bp,chip);
+ }
+ else
+ #ifdef AURORA_INTNORM
+ printk("aurora%d-%d: Bad modem ack 0x%02x.\n",
+ board_No(bp), chip, ack)
+ #endif
+ ;
+
+ }
+ bp->r[chip]->r[CD180_EOSRR]=0;
+ }
+ }
+*/
+}
+
+
+#ifdef AURORA_INT_DEBUG
+static void aurora_timer (unsigned long ignored);
+
+static struct timer_list
+aurora_poll_timer = { NULL, NULL, 0, 0, aurora_timer };
+
+static void
+aurora_timer (unsigned long ignored)
+{
+ unsigned long flags;
+ int i;
+
+ save_flags(flags); cli();
+
+printk("SRSR: %02x,%02x - ",aurora_board[0].r[0]->r[CD180_SRSR],aurora_board[0].r[1]->r[CD180_SRSR]);
+for(i=0;i<4;i++){
+ udelay(1);
+ printk("%02x ",aurora_board[0].r3->r[i]);
+ }
+printk("\n");
+
+ aurora_poll_timer.expires = jiffies + 300;
+ add_timer (&aurora_poll_timer);
+
+ restore_flags(flags);
+}
+#endif
+
+/*
+ * Routines for open & close processing.
+ */
+
+/* Called with disabled interrupts */
+extern inline int aurora_setup_board(struct Aurora_board * bp)
+{
+ int error;
+
+#ifdef AURORA_ALLIRQ
+ int i;
+ for(i=0;i<AURORA_ALLIRQ;i++){
+ error = request_irq(allirq[i]|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp);
+ if (error){
+ printk(KERN_ERR "IRQ%d request error %d\n",allirq[i],error);
+ }
+ }
+#else
+ error = request_irq(bp->irq|0x30, aurora_interrupt, SA_SHIRQ, "sio16", bp);
+ if (error){
+ printk(KERN_ERR "IRQ request error %d\n",error);
+ return error;
+ }
+#endif
+ /* Board reset */
+ bp->r0->r=0;
+ udelay(1);
+ if(bp->flags&AURORA_BOARD_TYPE_2){
+ /* unknown yet */
+ } else {
+ bp->r0->r=AURORA_CFG_ENABLE_IO|AURORA_CFG_ENABLE_IRQ|(((bp->irq)&0x0f)>>2);
+ }
+ udelay(10000);
+
+ if (aurora_init_CD180(bp,0))error=1;error=0;
+ if (aurora_init_CD180(bp,1))error++;
+ if (error==AURORA_NCD180) {
+ printk(KERN_ERR "Both chips failed initialisation.\n");
+ return -EIO;
+ }
+
+#ifdef AURORA_INT_DEBUG
+ aurora_poll_timer.expires=jiffies+1;
+ add_timer(&aurora_poll_timer);
+#endif
+#ifdef AURORA_DEBUG
+printk("aurora_setup_board: end\n");
+#endif
+ return 0;
+}
+
+/* Called with disabled interrupts */
+extern inline void aurora_shutdown_board(struct Aurora_board *bp)
+{
+ int i;
+
+#ifdef AURORA_DEBUG
+printk("aurora_shutdown_board: start\n");
+#endif
+
+#ifdef AURORA_INT_DEBUG
+ del_timer(&aurora_poll_timer);
+#endif
+
+#ifdef AURORA_ALLIRQ
+for(i=0;i<AURORA_ALLIRQ;i++){
+ free_irq(allirq[i]|0x30, bp);
+/* IRQ_to_board[allirq[i]&0xf] = NULL;*/
+ }
+#else
+ free_irq(bp->irq|0x30, bp);
+/* IRQ_to_board[bp->irq&0xf] = NULL;*/
+#endif
+ /* Drop all DTR's */
+ for(i=0;i<16;i++){
+ bp->r[i>>3]->r[CD180_CAR]=i&7;
+ udelay(1);
+ bp->r[i>>3]->r[CD180_MSVR]=0;
+ udelay(1);
+ }
+ /* Board shutdown */
+ bp->r0->r=0;
+
+#ifdef AURORA_DEBUG
+printk("aurora_shutdown_board: end\n");
+#endif
+}
+
+/*
+ * Setting up port characteristics.
+ * Must be called with disabled interrupts
+ */
+static void aurora_change_speed(struct Aurora_board *bp, struct Aurora_port *port)
+{
+ struct tty_struct *tty;
+ unsigned long baud;
+ long tmp;
+ unsigned char cor1 = 0, cor3 = 0;
+ unsigned char mcor1 = 0, mcor2 = 0,chip;
+
+#ifdef AURORA_DEBUG
+printk("aurora_change_speed: start\n");
+#endif
+ if (!(tty = port->tty) || !tty->termios)
+ return;
+
+ chip=AURORA_CD180(port_No(port));
+
+ port->SRER = 0;
+ port->COR2 = 0;
+ port->MSVR = MSVR_RTS|MSVR_DTR;
+
+ 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;
+ }
+
+ /* Select port on the board */
+ bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+ udelay(1);
+
+ if (!baud_table[baud]) {
+ /* Drop DTR & exit */
+ port->MSVR &= ~(bp->DTR|bp->RTS);
+ bp->r[chip]->r[CD180_MSVR]=port->MSVR;
+ return;
+ } else {
+ /* Set DTR on */
+ port->MSVR |= bp->DTR;
+ bp->r[chip]->r[CD180_MSVR]=port->MSVR;
+ }
+
+ /*
+ * Now we must calculate some speed depended things
+ */
+
+ /* Set baud rate for port */
+ tmp = (((bp->oscfreq + baud_table[baud]/2) / baud_table[baud] +
+ CD180_TPC/2) / CD180_TPC);
+
+/* tmp = (bp->oscfreq/7)/baud_table[baud];
+ if((tmp%10)>4)tmp=tmp/10+1;else tmp=tmp/10;*/
+/* printk("Prescaler period: %d\n",tmp);*/
+
+ bp->r[chip]->r[CD180_RBPRH]=(tmp >> 8) & 0xff;
+ bp->r[chip]->r[CD180_TBPRH]=(tmp >> 8) & 0xff;
+ bp->r[chip]->r[CD180_RBPRL]=tmp & 0xff;
+ bp->r[chip]->r[CD180_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 - CD180_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 = (AURORA_TPS + AURORA_TPS/2 + baud/2) / baud;
+ tmp = (tmp > 0xff) ? 0xff : tmp;
+ bp->r[chip]->r[CD180_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 AURORA_BRAIN_DAMAGED_CTS
+ port->SRER |= SRER_DSR | SRER_CTS;
+ mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
+ mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
+ tty->hw_stopped = !(aurora_in(bp, CD180_MSVR) & (MSVR_CTS|MSVR_DSR));
+#else*/
+ port->COR2 |= COR2_CTSAE;
+/*#endif*/
+ if (bp->flags&AURORA_BOARD_DTR_FLOW_OK) {
+ mcor1 |= AURORA_RXTH;
+ }
+ }
+ /* Enable Software Flow Control. FIXME: I'm not sure about this */
+ /* Some people reported that it works, but I still doubt */
+ if (I_IXON(tty)) {
+ port->COR2 |= COR2_TXIBE;
+ cor3 |= (COR3_FCT | COR3_SCDE);
+ if (I_IXANY(tty))
+ port->COR2 |= COR2_IXM;
+ bp->r[chip]->r[CD180_SCHR1]=START_CHAR(tty);
+ bp->r[chip]->r[CD180_SCHR2]=STOP_CHAR(tty);
+ bp->r[chip]->r[CD180_SCHR3]=START_CHAR(tty);
+ bp->r[chip]->r[CD180_SCHR4]=STOP_CHAR(tty);
+ }
+ if (!C_CLOCAL(tty)) {
+ /* Enable CD check */
+ port->SRER |= SRER_CD;
+ mcor1 |= MCOR1_CDZD;
+ mcor2 |= MCOR2_CDOD;
+ }
+
+ if (C_CREAD(tty))
+ /* Enable receiver */
+ port->SRER |= SRER_RXD;
+
+ /* Set input FIFO size (1-8 bytes) */
+ cor3 |= AURORA_RXFIFO;
+ /* Setting up CD180 channel registers */
+ bp->r[chip]->r[CD180_COR1]=cor1;
+ bp->r[chip]->r[CD180_COR2]=port->COR2;
+ bp->r[chip]->r[CD180_COR3]=cor3;
+ /* Make CD180 know about registers change */
+ aurora_wait_CCR(bp->r[chip]);
+ bp->r[chip]->r[CD180_CCR]=CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3;
+ /* Setting up modem option registers */
+ bp->r[chip]->r[CD180_MCOR1]=mcor1;
+ bp->r[chip]->r[CD180_MCOR2]=mcor2;
+ /* Enable CD180 transmitter & receiver */
+ aurora_wait_CCR(bp->r[chip]);
+ bp->r[chip]->r[CD180_CCR]=CCR_TXEN | CCR_RXEN;
+ /* Enable interrupts */
+ bp->r[chip]->r[CD180_SRER]=port->SRER;
+ /* And finally set RTS on */
+ bp->r[chip]->r[CD180_MSVR]=port->MSVR;
+#ifdef AURORA_DEBUG
+printk("aurora_change_speed: end\n");
+#endif
+}
+
+/* Must be called with interrupts enabled */
+static int aurora_setup_port(struct Aurora_board *bp, struct Aurora_port *port)
+{
+ unsigned long flags;
+
+#ifdef AURORA_DEBUG
+printk("aurora_setup_port: start %d\n",port_No(port));
+#endif
+ 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);
+
+#ifdef MODULE
+ if (port->count == 1) {
+ MOD_INC_USE_COUNT;
+ if((++bp->count)==1)
+ bp->flags|=AURORA_BOARD_ACTIVE;
+ }
+#endif
+
+ port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+ aurora_change_speed(bp, port);
+ port->flags |= ASYNC_INITIALIZED;
+
+ restore_flags(flags);
+#ifdef AURORA_DEBUG
+printk("aurora_setup_port: end\n");
+#endif
+ return 0;
+}
+
+/* Must be called with interrupts disabled */
+static void aurora_shutdown_port(struct Aurora_board *bp, struct Aurora_port *port)
+{
+ struct tty_struct *tty;
+ unsigned char chip;
+
+#ifdef AURORA_DEBUG
+printk("aurora_shutdown_port: start\n");
+#endif
+ if (!(port->flags & ASYNC_INITIALIZED))
+ return;
+
+ chip=AURORA_CD180(port_No(port));
+
+#ifdef AURORA_REPORT_OVERRUN
+ printk("aurora%d: port %d: Total %ld overruns were detected.\n",
+ board_No(bp), port_No(port), port->overrun);
+#endif
+#ifdef AURORA_REPORT_FIFO
+ {
+ int i;
+
+ printk("aurora%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;
+ }
+
+ if (!(tty = port->tty) || C_HUPCL(tty)) {
+ /* Drop DTR */
+ port->MSVR &= ~(bp->DTR|bp->RTS);
+ bp->r[chip]->r[CD180_MSVR]=port->MSVR;
+ }
+
+ /* Select port */
+ bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+ udelay(1);
+ /* Reset port */
+ aurora_wait_CCR(bp->r[chip]);
+ bp->r[chip]->r[CD180_CCR]=CCR_SOFTRESET;
+ /* Disable all interrupts from this port */
+ port->SRER = 0;
+ bp->r[chip]->r[CD180_SRER]=port->SRER;
+
+ if (tty)
+ set_bit(TTY_IO_ERROR, &tty->flags);
+ port->flags &= ~ASYNC_INITIALIZED;
+
+#ifdef MODULE
+ if (--bp->count < 0) {
+ printk(KERN_DEBUG "aurora%d: aurora_shutdown_port: bad board count: %d\n",
+ board_No(bp), bp->count);
+ bp->count = 0;
+ }
+
+ MOD_DEC_USE_COUNT;
+ if (!bp->count)
+ bp->flags&=~AURORA_BOARD_ACTIVE;
+#endif
+
+#ifdef AURORA_DEBUG
+printk("aurora_shutdown_port: end\n");
+#endif
+}
+
+
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+ struct Aurora_port *port)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ struct Aurora_board *bp = port_Board(port);
+ int retval;
+ int do_clocal = 0;
+ int CD;
+ unsigned char chip;
+
+#ifdef AURORA_DEBUG
+printk("block_til_ready: start\n");
+#endif
+ chip=AURORA_CD180(port_No(port));
+
+ /*
+ * 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 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();
+ bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+ udelay(1);
+ CD = bp->r[chip]->r[CD180_MSVR] & MSVR_CD;
+ if (!(port->flags & ASYNC_CALLOUT_ACTIVE)) {
+ port->MSVR=bp->RTS;
+ bp->r[chip]->r[CD180_MSVR]=port->MSVR;/* auto drops DTR */
+ }
+ 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;
+#ifdef AURORA_DEBUG
+printk("block_til_ready: end\n");
+#endif
+ return 0;
+}
+
+static int aurora_open(struct tty_struct * tty, struct file * filp)
+{
+ int board;
+ int error;
+ struct Aurora_port * port;
+ struct Aurora_board * bp;
+ unsigned long flags;
+
+ #ifdef AURORA_DEBUG
+ printk("aurora_open: start\n");
+ #endif
+
+ board = AURORA_BOARD(MINOR(tty->device));
+ if (board > AURORA_NBOARD || !(aurora_board[board].flags & AURORA_BOARD_PRESENT)){
+ #ifdef AURORA_DEBUG
+ printk("aurora_open: error board %d present %d\n",board,aurora_board[board].flags &AURORA_BOARD_PRESENT);
+ #endif
+ return -ENODEV;
+ }
+
+ bp = &aurora_board[board];
+ port = aurora_port + board * AURORA_NPORT * AURORA_NCD180 + AURORA_PORT(MINOR(tty->device));
+ if (aurora_paranoia_check(port, tty->device, "aurora_open")){
+ #ifdef AURORA_DEBUG
+ printk("aurora_open: error paranoia check\n");
+ #endif
+ return -ENODEV;
+ }
+
+ port->count++;
+ tty->driver_data = port;
+ port->tty = tty;
+
+ if ((error = aurora_setup_port(bp, port))) {
+ #ifdef AURORA_DEBUG
+ printk("aurora_open: error aurora_setup_port ret %d\n",error);
+ #endif
+ return error;
+ }
+
+ if ((error = block_til_ready(tty, filp, port))){
+ #ifdef AURORA_DEBUG
+ printk("aurora_open: error block_til_ready ret %d\n",error);
+ #endif
+ return error;
+ }
+
+ if ((port->count == 1) && (port->flags & ASYNC_SPLIT_TERMIOS)) {
+ *tty->termios = port->normal_termios;
+ save_flags(flags); cli();
+ aurora_change_speed(bp, port);
+ restore_flags(flags);
+ }
+
+ port->session = current->session;
+ port->pgrp = current->pgrp;
+ #ifdef AURORA_DEBUG
+ printk("aurora_open: end\n");
+ #endif
+ return 0;
+}
+
+static void aurora_close(struct tty_struct * tty, struct file * filp)
+{
+ struct Aurora_port *port = (struct Aurora_port *) tty->driver_data;
+ struct Aurora_board *bp;
+ unsigned long flags;
+ unsigned long timeout;
+ unsigned char chip;
+
+ #ifdef AURORA_DEBUG
+ printk("aurora_close: start\n");
+ #endif
+
+ if (!port || aurora_paranoia_check(port, tty->device, "close"))
+ return;
+
+ chip=AURORA_CD180(port_No(port));
+
+ 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_DEBUG "aurora%d: aurora_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_DEBUG "aurora%d: aurora_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){
+ #ifdef AURORA_DEBUG
+ printk("aurora_close: waiting to flush...\n");
+ #endif
+ 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->SRER &= ~SRER_RXD;
+ if (port->flags & ASYNC_INITIALIZED) {
+ port->SRER &= ~SRER_TXRDY;
+ port->SRER |= SRER_TXEMPTY;
+ bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+ udelay(1);
+ bp->r[chip]->r[CD180_SRER]=port->SRER;
+ /*
+ * 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->SRER & SRER_TXEMPTY) {
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(port->timeout);
+ if (time_after(jiffies, timeout))
+ break;
+ }
+ }
+ #ifdef AURORA_DEBUG
+ printk("aurora_close: shutdown_port\n");
+ #endif
+ aurora_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;
+ schedule_timeout(port->close_delay);
+ }
+ 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);
+ #ifdef AURORA_DEBUG
+ printk("aurora_close: end\n");
+ #endif
+}
+
+static int aurora_write(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count)
+{
+ struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+ struct Aurora_board *bp;
+ int c, total = 0;
+ unsigned long flags;
+ unsigned char chip;
+
+#ifdef AURORA_DEBUG
+printk("aurora_write: start %d\n",count);
+#endif
+ if (aurora_paranoia_check(port, tty->device, "aurora_write"))
+ return 0;
+
+ chip=AURORA_CD180(port_No(port));
+
+ 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->SRER & SRER_TXRDY)) {
+ port->SRER |= SRER_TXRDY;
+ bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+ udelay(1);
+ bp->r[chip]->r[CD180_SRER]=port->SRER;
+ }
+ restore_flags(flags);
+#ifdef AURORA_DEBUG
+printk("aurora_write: end %d\n",total);
+#endif
+ return total;
+}
+
+static void aurora_put_char(struct tty_struct * tty, unsigned char ch)
+{
+ struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+ unsigned long flags;
+
+#ifdef AURORA_DEBUG
+printk("aurora_put_char: start %c\n",ch);
+#endif
+ if (aurora_paranoia_check(port, tty->device, "aurora_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);
+#ifdef AURORA_DEBUG
+printk("aurora_put_char: end\n");
+#endif
+}
+
+static void aurora_flush_chars(struct tty_struct * tty)
+{
+ struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+ unsigned long flags;
+ unsigned char chip;
+
+/*#ifdef AURORA_DEBUG
+printk("aurora_flush_chars: start\n");
+#endif*/
+ if (aurora_paranoia_check(port, tty->device, "aurora_flush_chars"))
+ return;
+
+ chip=AURORA_CD180(port_No(port));
+
+ if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+ !port->xmit_buf)
+ return;
+
+ save_flags(flags); cli();
+ port->SRER |= SRER_TXRDY;
+ port_Board(port)->r[chip]->r[CD180_CAR]=port_No(port)&7;
+ udelay(1);
+ port_Board(port)->r[chip]->r[CD180_SRER]=port->SRER;
+ restore_flags(flags);
+/*#ifdef AURORA_DEBUG
+printk("aurora_flush_chars: end\n");
+#endif*/
+}
+
+static int aurora_write_room(struct tty_struct * tty)
+{
+ struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+ int ret;
+
+#ifdef AURORA_DEBUG
+printk("aurora_write_room: start\n");
+#endif
+ if (aurora_paranoia_check(port, tty->device, "aurora_write_room"))
+ return 0;
+
+ ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
+ if (ret < 0)
+ ret = 0;
+#ifdef AURORA_DEBUG
+printk("aurora_write_room: end\n");
+#endif
+ return ret;
+}
+
+static int aurora_chars_in_buffer(struct tty_struct *tty)
+{
+ struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+
+ if (aurora_paranoia_check(port, tty->device, "aurora_chars_in_buffer"))
+ return 0;
+
+ return port->xmit_cnt;
+}
+
+static void aurora_flush_buffer(struct tty_struct *tty)
+{
+ struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+ unsigned long flags;
+
+#ifdef AURORA_DEBUG
+printk("aurora_flush_buffer: start\n");
+#endif
+ if (aurora_paranoia_check(port, tty->device, "aurora_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);
+#ifdef AURORA_DEBUG
+printk("aurora_flush_buffer: end\n");
+#endif
+}
+
+static int aurora_get_modem_info(struct Aurora_port * port, unsigned int *value)
+{
+ struct Aurora_board * bp;
+ unsigned char status,chip;
+ unsigned int result;
+ unsigned long flags;
+
+#ifdef AURORA_DEBUG
+printk("aurora_get_modem_info: start\n");
+#endif
+ chip=AURORA_CD180(port_No(port));
+
+ bp = port_Board(port);
+ save_flags(flags); cli();
+ bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+ udelay(1);
+ status = bp->r[chip]->r[CD180_MSVR];
+ result = 0/*bp->r[chip]->r[AURORA_RI] & (1u << port_No(port)) ? 0 : TIOCM_RNG*/;
+ restore_flags(flags);
+ result |= ((status & bp->RTS) ? TIOCM_RTS : 0)
+ | ((status & bp->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);
+#ifdef AURORA_DEBUG
+printk("aurora_get_modem_info: end\n");
+#endif
+ return 0;
+}
+
+static int aurora_set_modem_info(struct Aurora_port * port, unsigned int cmd,
+ unsigned int *value)
+{
+ int error;
+ unsigned int arg;
+ unsigned long flags;
+ struct Aurora_board *bp = port_Board(port);
+ unsigned char chip;
+
+#ifdef AURORA_DEBUG
+printk("aurora_set_modem_info: start\n");
+#endif
+ error = get_user(arg, value);
+ if (error)
+ return error;
+ chip=AURORA_CD180(port_No(port));
+ switch (cmd) {
+ case TIOCMBIS:
+ if (arg & TIOCM_RTS)
+ port->MSVR |= bp->RTS;
+ if (arg & TIOCM_DTR)
+ port->MSVR |= bp->DTR;
+ break;
+ case TIOCMBIC:
+ if (arg & TIOCM_RTS)
+ port->MSVR &= ~bp->RTS;
+ if (arg & TIOCM_DTR)
+ port->MSVR &= ~bp->DTR;
+ break;
+ case TIOCMSET:
+ port->MSVR = (arg & TIOCM_RTS) ? (port->MSVR | bp->RTS) :
+ (port->MSVR & ~bp->RTS);
+ port->MSVR = (arg & TIOCM_DTR) ? (port->MSVR | bp->RTS) :
+ (port->MSVR & ~bp->RTS);
+ break;
+ default:
+ return -EINVAL;
+ }
+ save_flags(flags); cli();
+ bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+ udelay(1);
+ bp->r[chip]->r[CD180_MSVR]=port->MSVR;
+ restore_flags(flags);
+#ifdef AURORA_DEBUG
+printk("aurora_set_modem_info: end\n");
+#endif
+ return 0;
+}
+
+extern inline void aurora_send_break(struct Aurora_port * port, unsigned long length)
+{
+ struct Aurora_board *bp = port_Board(port);
+ unsigned long flags;
+ unsigned char chip;
+
+#ifdef AURORA_DEBUG
+printk("aurora_send_break: start\n");
+#endif
+ chip=AURORA_CD180(port_No(port));
+
+ save_flags(flags); cli();
+ port->break_length = AURORA_TPS / HZ * length;
+ port->COR2 |= COR2_ETC;
+ port->SRER |= SRER_TXRDY;
+ bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+ udelay(1);
+ bp->r[chip]->r[CD180_COR2]=port->COR2;
+ bp->r[chip]->r[CD180_SRER]=port->SRER;
+ aurora_wait_CCR(bp->r[chip]);
+ bp->r[chip]->r[CD180_CCR]=CCR_CORCHG2;
+ aurora_wait_CCR(bp->r[chip]);
+ restore_flags(flags);
+#ifdef AURORA_DEBUG
+printk("aurora_send_break: end\n");
+#endif
+}
+
+extern inline int aurora_set_serial_info(struct Aurora_port * port,
+ struct serial_struct * newinfo)
+{
+ struct serial_struct tmp;
+ struct Aurora_board *bp = port_Board(port);
+ int change_speed;
+ unsigned long flags;
+ int error;
+
+#ifdef AURORA_DEBUG
+printk("aurora_set_serial_info: start\n");
+#endif
+ 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 != (bp->oscfreq + CD180_TPC/2) / CD180_TPC) ||
+ (tmp.custom_divisor != 0) ||
+ (tmp.xmit_fifo_size != CD180_NFIFO) ||
+ (tmp.flags & ~AURORA_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();
+ aurora_change_speed(bp, port);
+ restore_flags(flags);
+ }
+#ifdef AURORA_DEBUG
+printk("aurora_set_serial_info: end\n");
+#endif
+ return 0;
+}
+
+extern inline int aurora_get_serial_info(struct Aurora_port * port,
+ struct serial_struct * retinfo)
+{
+ struct serial_struct tmp;
+ struct Aurora_board *bp = port_Board(port);
+ int error;
+
+#ifdef AURORA_DEBUG
+printk("aurora_get_serial_info: start\n");
+#endif
+ 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 - aurora_port;
+ tmp.port = 0;
+ tmp.irq = bp->irq;
+ tmp.flags = port->flags;
+ tmp.baud_base = (bp->oscfreq + CD180_TPC/2) / CD180_TPC;
+ tmp.close_delay = port->close_delay * HZ/100;
+ tmp.closing_wait = port->closing_wait * HZ/100;
+ tmp.xmit_fifo_size = CD180_NFIFO;
+ copy_to_user(retinfo, &tmp, sizeof(tmp));
+#ifdef AURORA_DEBUG
+printk("aurora_get_serial_info: end\n");
+#endif
+ return 0;
+}
+
+static int aurora_ioctl(struct tty_struct * tty, struct file * filp,
+ unsigned int cmd, unsigned long arg)
+
+{
+ struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+ int error;
+ int retval;
+
+#ifdef AURORA_DEBUG
+printk("aurora_ioctl: start\n");
+#endif
+ if (aurora_paranoia_check(port, tty->device, "aurora_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)
+ aurora_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);
+ aurora_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:
+ retval = get_user(arg,(unsigned long *) arg);
+ if (retval)
+ return retval;
+ 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 aurora_get_modem_info(port, (unsigned int *) arg);
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ return aurora_set_modem_info(port, cmd, (unsigned int *) arg);
+ case TIOCGSERIAL:
+ return aurora_get_serial_info(port, (struct serial_struct *) arg);
+ case TIOCSSERIAL:
+ return aurora_set_serial_info(port, (struct serial_struct *) arg);
+ default:
+ return -ENOIOCTLCMD;
+ }
+#ifdef AURORA_DEBUG
+printk("aurora_ioctl: end\n");
+#endif
+ return 0;
+}
+
+static void aurora_throttle(struct tty_struct * tty)
+{
+ struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+ struct Aurora_board *bp;
+ unsigned long flags;
+ unsigned char chip;
+
+#ifdef AURORA_DEBUG
+printk("aurora_throttle: start\n");
+#endif
+ if (aurora_paranoia_check(port, tty->device, "aurora_throttle"))
+ return;
+
+ bp = port_Board(port);
+ chip=AURORA_CD180(port_No(port));
+
+ save_flags(flags); cli();
+ port->MSVR &= ~bp->RTS;
+ bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+ udelay(1);
+ if (I_IXOFF(tty)) {
+ aurora_wait_CCR(bp->r[chip]);
+ bp->r[chip]->r[CD180_CCR]=CCR_SSCH2;
+ aurora_wait_CCR(bp->r[chip]);
+ }
+ bp->r[chip]->r[CD180_MSVR]=port->MSVR;
+ restore_flags(flags);
+#ifdef AURORA_DEBUG
+printk("aurora_throttle: end\n");
+#endif
+}
+
+static void aurora_unthrottle(struct tty_struct * tty)
+{
+ struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+ struct Aurora_board *bp;
+ unsigned long flags;
+ unsigned char chip;
+
+#ifdef AURORA_DEBUG
+printk("aurora_unthrottle: start\n");
+#endif
+ if (aurora_paranoia_check(port, tty->device, "aurora_unthrottle"))
+ return;
+
+ bp = port_Board(port);
+
+ chip=AURORA_CD180(port_No(port));
+
+ save_flags(flags); cli();
+ port->MSVR |= bp->RTS;
+ bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+ udelay(1);
+ if (I_IXOFF(tty)) {
+ aurora_wait_CCR(bp->r[chip]);
+ bp->r[chip]->r[CD180_CCR]=CCR_SSCH1;
+ aurora_wait_CCR(bp->r[chip]);
+ }
+ bp->r[chip]->r[CD180_MSVR]=port->MSVR;
+ restore_flags(flags);
+#ifdef AURORA_DEBUG
+printk("aurora_unthrottle: end\n");
+#endif
+}
+
+static void aurora_stop(struct tty_struct * tty)
+{
+ struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+ struct Aurora_board *bp;
+ unsigned long flags;
+ unsigned char chip;
+
+#ifdef AURORA_DEBUG
+printk("aurora_stop: start\n");
+#endif
+ if (aurora_paranoia_check(port, tty->device, "aurora_stop"))
+ return;
+
+ bp = port_Board(port);
+
+ chip=AURORA_CD180(port_No(port));
+
+ save_flags(flags); cli();
+ port->SRER &= ~SRER_TXRDY;
+ bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+ udelay(1);
+ bp->r[chip]->r[CD180_SRER]=port->SRER;
+ restore_flags(flags);
+#ifdef AURORA_DEBUG
+printk("aurora_stop: end\n");
+#endif
+}
+
+static void aurora_start(struct tty_struct * tty)
+{
+ struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+ struct Aurora_board *bp;
+ unsigned long flags;
+ unsigned char chip;
+
+#ifdef AURORA_DEBUG
+printk("aurora_start: start\n");
+#endif
+ if (aurora_paranoia_check(port, tty->device, "aurora_start"))
+ return;
+
+ bp = port_Board(port);
+
+ chip=AURORA_CD180(port_No(port));
+
+ save_flags(flags); cli();
+ if (port->xmit_cnt && port->xmit_buf && !(port->SRER & SRER_TXRDY)) {
+ port->SRER |= SRER_TXRDY;
+ bp->r[chip]->r[CD180_CAR]=port_No(port)&7;
+ udelay(1);
+ bp->r[chip]->r[CD180_SRER]=port->SRER;
+ }
+ restore_flags(flags);
+#ifdef AURORA_DEBUG
+printk("aurora_start: end\n");
+#endif
+}
+
+/*
+ * 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_aurora_hangup() -> tty->hangup() -> aurora_hangup()
+ *
+ */
+static void do_aurora_hangup(void *private_)
+{
+ struct Aurora_port *port = (struct Aurora_port *) private_;
+ struct tty_struct *tty;
+
+#ifdef AURORA_DEBUG
+printk("do_aurora_hangup: start\n");
+#endif
+ tty = port->tty;
+ if (!tty)
+ return;
+
+ tty_hangup(tty);
+#ifdef AURORA_DEBUG
+printk("do_aurora_hangup: end\n");
+#endif
+}
+
+static void aurora_hangup(struct tty_struct * tty)
+{
+ struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+ struct Aurora_board *bp;
+
+#ifdef AURORA_DEBUG
+printk("aurora_hangup: start\n");
+#endif
+ if (aurora_paranoia_check(port, tty->device, "aurora_hangup"))
+ return;
+
+ bp = port_Board(port);
+
+ aurora_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);
+#ifdef AURORA_DEBUG
+printk("aurora_hangup: end\n");
+#endif
+}
+
+static void aurora_set_termios(struct tty_struct * tty, struct termios * old_termios)
+{
+ struct Aurora_port *port = (struct Aurora_port *)tty->driver_data;
+ unsigned long flags;
+
+#ifdef AURORA_DEBUG
+printk("aurora_set_termios: start\n");
+#endif
+ if (aurora_paranoia_check(port, tty->device, "aurora_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();
+ aurora_change_speed(port_Board(port), port);
+ restore_flags(flags);
+
+ if ((old_termios->c_cflag & CRTSCTS) &&
+ !(tty->termios->c_cflag & CRTSCTS)) {
+ tty->hw_stopped = 0;
+ aurora_start(tty);
+ }
+#ifdef AURORA_DEBUG
+printk("aurora_set_termios: end\n");
+#endif
+}
+
+static void do_aurora_bh(void)
+{
+ run_task_queue(&tq_aurora);
+}
+
+static void do_softint(void *private_)
+{
+ struct Aurora_port *port = (struct Aurora_port *) private_;
+ struct tty_struct *tty;
+
+#ifdef AURORA_DEBUG
+printk("do_softint: start\n");
+#endif
+ 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);
+ }
+#ifdef AURORA_DEBUG
+printk("do_softint: end\n");
+#endif
+}
+
+static int aurora_init_drivers(void)
+{
+ int error;
+ int i;
+
+#ifdef AURORA_DEBUG
+printk("aurora_init_drivers: start\n");
+#endif
+ if (!(tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL))) {
+ printk(KERN_ERR "aurora: Couldn't get free page.\n");
+ return 1;
+ }
+ init_bh(AURORA_BH, do_aurora_bh);
+/* memset(IRQ_to_board, 0, sizeof(IRQ_to_board));*/
+ memset(&aurora_driver, 0, sizeof(aurora_driver));
+ aurora_driver.magic = TTY_DRIVER_MAGIC;
+ aurora_driver.name = "ttyA";
+ aurora_driver.major = AURORA_MAJOR;
+ aurora_driver.num = AURORA_TNPORTS;
+ aurora_driver.type = TTY_DRIVER_TYPE_SERIAL;
+ aurora_driver.subtype = AURORA_TYPE_NORMAL;
+ aurora_driver.init_termios = tty_std_termios;
+ aurora_driver.init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ aurora_driver.flags = TTY_DRIVER_REAL_RAW;
+ aurora_driver.refcount = &aurora_refcount;
+ aurora_driver.table = aurora_table;
+ aurora_driver.termios = aurora_termios;
+ aurora_driver.termios_locked = aurora_termios_locked;
+
+ aurora_driver.open = aurora_open;
+ aurora_driver.close = aurora_close;
+ aurora_driver.write = aurora_write;
+ aurora_driver.put_char = aurora_put_char;
+ aurora_driver.flush_chars = aurora_flush_chars;
+ aurora_driver.write_room = aurora_write_room;
+ aurora_driver.chars_in_buffer = aurora_chars_in_buffer;
+ aurora_driver.flush_buffer = aurora_flush_buffer;
+ aurora_driver.ioctl = aurora_ioctl;
+ aurora_driver.throttle = aurora_throttle;
+ aurora_driver.unthrottle = aurora_unthrottle;
+ aurora_driver.set_termios = aurora_set_termios;
+ aurora_driver.stop = aurora_stop;
+ aurora_driver.start = aurora_start;
+ aurora_driver.hangup = aurora_hangup;
+
+ if ((error = tty_register_driver(&aurora_driver))) {
+ free_page((unsigned long)tmp_buf);
+ printk(KERN_ERR "aurora: Couldn't register aurora driver, error = %d\n",
+ error);
+ return 1;
+ }
+
+ memset(aurora_port, 0, sizeof(aurora_port));
+ for (i = 0; i < AURORA_TNPORTS; i++) {
+ aurora_port[i].normal_termios = aurora_driver.init_termios;
+ aurora_port[i].magic = AURORA_MAGIC;
+ aurora_port[i].tqueue.routine = do_softint;
+ aurora_port[i].tqueue.data = &aurora_port[i];
+ aurora_port[i].tqueue_hangup.routine = do_aurora_hangup;
+ aurora_port[i].tqueue_hangup.data = &aurora_port[i];
+ aurora_port[i].close_delay = 50 * HZ/100;
+ aurora_port[i].closing_wait = 3000 * HZ/100;
+ init_waitqueue_head(&aurora_port[i].open_wait);
+ init_waitqueue_head(&aurora_port[i].close_wait);
+ }
+#ifdef AURORA_DEBUG
+printk("aurora_init_drivers: end\n");
+#endif
+ return 0;
+}
+
+static void aurora_release_drivers(void)
+{
+#ifdef AURORA_DEBUG
+printk("aurora_release_drivers: start\n");
+#endif
+ free_page((unsigned long)tmp_buf);
+ tty_unregister_driver(&aurora_driver);
+#ifdef AURORA_DEBUG
+printk("aurora_release_drivers: end\n");
+#endif
+}
+
+#ifndef MODULE
+/*
+ * Called at boot time.
+ *
+ * You can specify IO base for up to RC_NBOARD cards,
+ * using line "riscom8=0xiobase1,0xiobase2,.." at LILO prompt.
+ * Note that there will be no probing at default
+ * addresses in this case.
+ *
+ */
+__init_func(void aurora_setup(char *str, int *ints))
+{
+ int i;
+
+ for(i=0;(i<ints[0])&&(i<4);i++) {
+ if (ints[i+1]) irqs[i]=ints[i+1];
+ }
+}
+
+__init_func(int aurora_init(void))
+#else
+int aurora_init(void)
+#endif
+{
+int found;
+int grrr,i;
+
+printk(KERN_INFO "aurora: Driver starting.\n");
+if(aurora_init_drivers())return -EIO;
+found=aurora_probe();
+if(!found){
+ aurora_release_drivers();
+ printk(KERN_INFO "aurora: No Aurora Multiport boards detected.\n");
+ return -EIO;
+ } else {
+ printk(KERN_INFO "aurora: %d boards found.\n",found);
+ }
+for(i=0;i<found;i++){
+ if ((grrr = aurora_setup_board(&aurora_board[i]))) {
+ #ifdef AURORA_DEBUG
+ printk(KERN_ERR "aurora_init: error aurora_setup_board ret %d\n",grrr);
+ #endif
+ return grrr;
+ }
+}
+return 0;
+}
+
+#ifdef MODULE
+int irq = 0;
+int irq1 = 0;
+int irq2 = 0;
+int irq3 = 0;
+MODULE_PARM(irq , "i");
+MODULE_PARM(irq1, "i");
+MODULE_PARM(irq2, "i");
+MODULE_PARM(irq3, "i");
+
+int init_module(void)
+{
+ if (irq ) irqs[0]=irq ;
+ if (irq1) irqs[1]=irq1;
+ if (irq2) irqs[2]=irq2;
+ if (irq3) irqs[3]=irq3;
+ return aurora_init();
+}
+
+void cleanup_module(void)
+{
+ int i;
+
+#ifdef AURORA_DEBUG
+printk("cleanup_module: aurora_release_drivers\n");
+#endif;
+
+ aurora_release_drivers();
+ for (i = 0; i < AURORA_NBOARD; i++)
+ if (aurora_board[i].flags & AURORA_BOARD_PRESENT) {
+ aurora_shutdown_board(&aurora_board[i]);
+ aurora_release_io_range(&aurora_board[i]);
+ }
+}
+#endif /* MODULE */
diff --git a/drivers/sbus/char/aurora.h b/drivers/sbus/char/aurora.h
new file mode 100644
index 000000000..e8250ead4
--- /dev/null
+++ b/drivers/sbus/char/aurora.h
@@ -0,0 +1,278 @@
+/*
+ * linux/drivers/sbus/char/aurora.h -- Aurora multiport driver
+ *
+ * Copyright (c) 1999 by Oliver Aldulea (oli@bv.ro)
+ *
+ * This code is based on the RISCom/8 multiport serial driver written
+ * by Dmitry Gorodchanin (pgmdsg@ibi.com), based on the Linux serial
+ * driver, written by Linus Torvalds, Theodore T'so and others.
+ * The Aurora multiport programming info was obtained mainly from the
+ * Cirrus Logic CD180 documentation (available on the web), and by
+ * doing heavy tests on the board. Many thanks to Eddie C. Dost for the
+ * help on the sbus interface.
+ *
+ * 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 1.0
+ *
+ * This is the first public release.
+ *
+ * This version needs a lot of feedback. This is the version that works
+ * with _my_ board. My board is model 1600se, revision '@(#)1600se.fth
+ * 1.2 3/28/95 1'. The driver might work with your board, but I do not
+ * guarantee it. If you have _any_ type of board, I need to know if the
+ * driver works or not, I need to know exactly your board parameters
+ * (get them with 'cd /proc/openprom/iommu/sbus/sio16/; ls *; cat *')
+ * Also, I need your board revision code, which is written on the board.
+ * Send me the output of my driver too (it outputs through klogd).
+ *
+ * If the driver does not work, you can try enabling the debug options
+ * to see what's wrong or what should be done.
+ *
+ * I'm sorry about the alignment of the code. It was written in a
+ * 128x48 environment.
+ *
+ * I must say that I do not like Aurora Technologies' policy. I asked
+ * them to help me do this driver faster, but they ended by something
+ * like "don't call us, we'll call you", and I never heard anything
+ * from them. They told me "knowing the way the board works, I don't
+ * doubt you and others on the net will make the driver."
+ * The truth about this board is that it has nothing intelligent on it.
+ * If you want to say to somebody what kind of board you have, say that
+ * it uses Cirrus Logic processors (CD180). The power of the board is
+ * in those two chips. The rest of the board is the interface to the
+ * sbus and to the peripherals. Still, they did something smart: they
+ * reversed DTR and RTS to make on-board automatic hardware flow
+ * control usable.
+ * Thanks to Aurora Technologies for wasting my time, nerves and money.
+ */
+
+#ifndef __LINUX_AURORA_H
+#define __LINUX_AURORA_H
+
+#include <linux/serial.h>
+
+#ifdef __KERNEL__
+
+/* This is the number of boards to support. I've only tested this driver with
+ * one board, so it might not work.
+ */
+#define AURORA_NBOARD 1
+
+/* Useful ? Yes. But you can safely comment the warnings if they annoy you
+ * (let me say that again: the warnings in the code, not this define).
+ */
+#define AURORA_PARANOIA_CHECK
+
+/* Well, after many lost nights, I found that the IRQ for this board is
+ * selected from four built-in values by writing some bits in the
+ * configuration register. This causes a little problem to occur: which
+ * IRQ to select ? Which one is the best for the user ? Well, I finally
+ * decided for the following algorithm: if the "bintr" value is not acceptable
+ * (not within type_1_irq[], then test the "intr" value, if that fails too,
+ * try each value from type_1_irq until succeded. Hope it's ok.
+ * You can safely reorder the irq's.
+ */
+#define TYPE_1_IRQS 4
+unsigned char type_1_irq[TYPE_1_IRQS] = {
+ 3, 5, 9, 13
+ };
+/* I know something about another method of interrupt setting, but not enough.
+ * Also, this is for another type of board, so I first have to learn how to
+ * detect it.
+#define TYPE_2_IRQS 3
+unsigned char type_2_irq[TYPE_2_IRQS] = {
+ 0, 0, 0 ** could anyone find these for me ? (see AURORA_ALLIRQ below) **
+ };
+unsigned char type_2_mask[TYPE_2_IRQS] = {
+ 32, 64, 128
+ };
+*/
+
+/* The following section should only be modified by those who know what
+ * they're doing (or don't, but want to help with some feedback). Modifying
+ * anything raises a _big_ probability for your system to hang, but the
+ * sacrifice worths. (I sacrificed my ext2fs many, many times...)
+ */
+
+/* This one tries to dump to console the name of almost every function called,
+ * and many other debugging info.
+ */
+#undef AURORA_DEBUG
+
+/* These are the most dangerous and useful defines. They do printk() during
+ * the interrupt processing routine(s), so if you manage to get "flooded" by
+ * irq's, start thinking about the "Power off/on" button...
+ */
+#undef AURORA_INTNORM /* This one enables the "normal" messages, but some
+ * of them cause flood, so I preffered putting
+ * them under a define */
+#undef AURORA_INT_DEBUG /* This one is really bad. */
+
+/* Here's something helpful: after n irq's, the board will be disabled. This
+ * prevents irq flooding during debug (no need to think about power
+ * off/on anymore...)
+ */
+#define AURORA_FLOODPRO 10
+
+/* This one helps finding which irq the board calls, in case of a strange/
+ * unsupported board. AURORA_INT_DEBUG should be enabled, because I don't
+ * think /proc/interrupts or any command will be available in case of an irq
+ * flood... "allirq" is the list of all free irq's.
+ */
+/*
+#define AURORA_ALLIRQ 6
+int allirq[AURORA_ALLIRQ]={
+ 2,3,5,7,9,13
+ };
+*/
+
+/* These must not be modified. These values are assumed during the code for
+ * performance optimisations.
+ */
+#define AURORA_NCD180 2 /* two chips per board */
+#define AURORA_NPORT 8 /* 8 ports per chip */
+
+/* several utilities */
+#define AURORA_BOARD(line) (((line) >> 4) & 0x01)
+#define AURORA_CD180(line) (((line) >> 3) & 0x01)
+#define AURORA_PORT(line) ((line) & 15)
+
+#define AURORA_TNPORTS (AURORA_NBOARD*AURORA_NCD180*AURORA_NPORT)
+
+/* Ticks per sec. Used for setting receiver timeout and break length */
+#define AURORA_TPS 4000
+
+#define AURORA_MAGIC 0x0A18
+
+/* Yeah, after heavy testing I decided it must be 6.
+ * Sure, You can change it if needed.
+ */
+#define AURORA_RXFIFO 6 /* Max. receiver FIFO size (1-8) */
+
+#define AURORA_RXTH 7
+
+struct aurora_reg1 {
+ __volatile__ unsigned char r;
+ };
+
+struct aurora_reg128 {
+ __volatile__ unsigned char r[128];
+ };
+
+struct aurora_reg4 {
+ __volatile__ unsigned char r[4];
+ };
+
+struct Aurora_board {
+ unsigned long flags;
+ struct aurora_reg1 * r0; /* This is the board configuration
+ * register (write-only). */
+ struct aurora_reg128 * r[2]; /* These are the registers for the
+ * two chips. */
+ struct aurora_reg4 * r3; /* These are used for hardware-based
+ * acknowledge. Software-based ack is
+ * not supported by CD180. */
+ unsigned int oscfreq; /* The on-board oscillator
+ * frequency, in Hz. */
+ unsigned char irq;
+#ifdef MODULE
+ signed char count; /* counts the use of the board */
+#endif
+ /* Values for the dtr_rts swapped mode. */
+ unsigned char DTR;
+ unsigned char RTS;
+ unsigned char MSVDTR;
+ unsigned char MSVRTS;
+ /* Values for hardware acknowledge. */
+ unsigned char ACK_MINT,ACK_TINT,ACK_RINT;
+};
+
+/* Board configuration register */
+#define AURORA_CFG_ENABLE_IO 8
+#define AURORA_CFG_ENABLE_IRQ 4
+
+/* Board flags */
+#define AURORA_BOARD_PRESENT 0x00000001
+#define AURORA_BOARD_ACTIVE 0x00000002
+#define AURORA_BOARD_TYPE_2 0x00000004 /* don't know how to
+ * detect this yet */
+#define AURORA_BOARD_DTR_FLOW_OK 0x00000008
+
+/* The story goes like this: Cirrus programmed the CD-180 chip to do automatic
+ * hardware flow control, and do it using CTS and DTR. CTS is ok, but, if you
+ * have a modem and the chip drops DTR, then the modem will drop the carrier
+ * (ain't that cute...). Luckily, the guys at Aurora decided to swap DTR and
+ * RTS, which makes the flow control usable. I hope that all the boards made
+ * by Aurora have these two signals swapped. If your's doesn't but you have a
+ * breakout box, you can try to reverse them yourself, then set the following
+ * flag.
+ */
+#undef AURORA_FORCE_DTR_FLOW
+
+/* In fact, a few more words have to be said about hardware flow control.
+ * This driver handles "output" flow control through the on-board facility
+ * CTS Auto Enable. For the "input" flow control there are two cases when
+ * the flow should be controlled. The first case is when the kernel is so
+ * busy that it cannot process IRQ's in time; this flow control can only be
+ * activated by the on-board chip, and if the board has RTS and DTR swapped,
+ * this facility is usable. The second case is when the application is so
+ * busy that it cannot receive bytes from the kernel, and this flow must be
+ * activated by software. This second case is not yet implemented in this
+ * driver. Unfortunately, I estimate that the second case is the one that
+ * occurs the most.
+ */
+
+
+struct Aurora_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;
+ wait_queue_head_t open_wait;
+ wait_queue_head_t 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 SRER;
+ unsigned char MSVR;
+ unsigned char COR2;
+#ifdef AURORA_REPORT_OVERRUN
+ unsigned long overrun;
+#endif
+#ifdef AURORA_REPORT_FIFO
+ unsigned long hits[10];
+#endif
+};
+
+#endif
+#endif /*__LINUX_AURORA_H*/
+
diff --git a/drivers/sbus/char/cd180.h b/drivers/sbus/char/cd180.h
new file mode 100644
index 000000000..445b86cc6
--- /dev/null
+++ b/drivers/sbus/char/cd180.h
@@ -0,0 +1,240 @@
+
+/* Definitions for Cirrus Logic CL-CD180 8-port async mux chip */
+#define CD180_NCH 8 /* Total number of channels */
+#define CD180_TPC 16 /* Ticks per character */
+#define CD180_NFIFO 8 /* TX FIFO size */
+
+/* Global registers */
+#define CD180_GFRCR 0x6b /* Global Firmware Revision Code Register */
+#define CD180_SRCR 0x66 /* Service Request Configuration Register */
+#define CD180_PPRH 0x70 /* Prescaler Period Register High */
+#define CD180_PPRL 0x71 /* Prescaler Period Register Low */
+#define CD180_MSMR 0x61 /* Modem Service Match Register */
+#define CD180_TSMR 0x62 /* Transmit Service Match Register */
+#define CD180_RSMR 0x63 /* Receive Service Match Register */
+#define CD180_GSVR 0x40 /* Global Service Vector Register */
+#define CD180_SRSR 0x65 /* Service Request Status Register */
+#define CD180_GSCR 0x41 /* Global Service Channel Register */
+#define CD180_CAR 0x64 /* Channel Access Register */
+
+/* Indexed registers */
+#define CD180_RDCR 0x07 /* Receive Data Count Register */
+#define CD180_RDR 0x78 /* Receiver Data Register */
+#define CD180_RCSR 0x7a /* Receiver Character Status Register */
+#define CD180_TDR 0x7b /* Transmit Data Register */
+#define CD180_EOSRR 0x7f /* End of Service Request Register */
+
+/* Channel Registers */
+#define CD180_SRER 0x02 /* Service Request Enable Register */
+#define CD180_CCR 0x01 /* Channel Command Register */
+#define CD180_COR1 0x03 /* Channel Option Register 1 */
+#define CD180_COR2 0x04 /* Channel Option Register 2 */
+#define CD180_COR3 0x05 /* Channel Option Register 3 */
+#define CD180_CCSR 0x06 /* Channel Control Status Register */
+#define CD180_RTPR 0x18 /* Receive Timeout Period Register */
+#define CD180_RBPRH 0x31 /* Receive Bit Rate Period Register High */
+#define CD180_RBPRL 0x32 /* Receive Bit Rate Period Register Low */
+#define CD180_TBPRH 0x39 /* Transmit Bit Rate Period Register High */
+#define CD180_TBPRL 0x3a /* Transmit Bit Rate Period Register Low */
+#define CD180_SCHR1 0x09 /* Special Character Register 1 */
+#define CD180_SCHR2 0x0a /* Special Character Register 2 */
+#define CD180_SCHR3 0x0b /* Special Character Register 3 */
+#define CD180_SCHR4 0x0c /* Special Character Register 4 */
+#define CD180_MCR 0x12 /* Modem Change Register */
+#define CD180_MCOR1 0x10 /* Modem Change Option 1 Register */
+#define CD180_MCOR2 0x11 /* Modem Change Option 2 Register */
+#define CD180_MSVR 0x28 /* Modem Signal Value Register */
+#define CD180_MSVRTS 0x29 /* Modem Signal Value RTS */
+#define CD180_MSVDTR 0x2a /* Modem Signal Value DTR */
+
+/* Global Interrupt Vector Register (R/W) */
+
+#define GSVR_ITMASK 0x07 /* Interrupt type mask */
+#define GSVR_IT_MDM 0x01 /* Modem Signal Change Interrupt */
+#define GSVR_IT_TX 0x02 /* Transmit Data Interrupt */
+#define GSVR_IT_RGD 0x03 /* Receive Good Data Interrupt */
+#define GSVR_IT_REXC 0x07 /* Receive Exception Interrupt */
+
+
+/* Global Interrupt Channel Register (R/W) */
+
+#define GSCR_CHAN 0x1c /* Channel Number Mask */
+#define GSCR_CHAN_OFF 2 /* Channel Number Offset */
+
+
+/* Channel Address Register (R/W) */
+
+#define CAR_CHAN 0x07 /* Channel Number Mask */
+
+
+/* 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 */
+
+
+/* Service Request Enable Register (R/W) */
+
+#define SRER_DSR 0x80 /* Enable interrupt on DSR change */
+#define SRER_CD 0x40 /* Enable interrupt on CD change */
+#define SRER_CTS 0x20 /* Enable interrupt on CTS change */
+#define SRER_RXD 0x10 /* Enable interrupt on Receive Data */
+#define SRER_RXSC 0x08 /* Enable interrupt on Receive Spec. Char */
+#define SRER_TXRDY 0x04 /* Enable interrupt on TX FIFO empty */
+#define SRER_TXEMPTY 0x02 /* Enable interrupt on TX completely empty */
+#define SRER_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 */
+
+
+/* Service Request Status Register */
+
+#define SRSR_CMASK 0xC0 /* Current Service Context Mask */
+#define SRSR_CNONE 0x00 /* Not in a service context */
+#define SRSR_CRX 0x40 /* Rx Context */
+#define SRSR_CTX 0x80 /* Tx Context */
+#define SRSR_CMDM 0xC0 /* Modem Context */
+#define SRSR_ANYINT 0x6F /* Any interrupt flag */
+#define SRSR_RINT 0x10 /* Receive Interrupt */
+#define SRSR_TINT 0x04 /* Transmit Interrupt */
+#define SRSR_MINT 0x01 /* Modem Interrupt */
+#define SRSR_REXT 0x20 /* Receive External Interrupt */
+#define SRSR_TEXT 0x08 /* Transmit External Interrupt */
+#define SRSR_MEXT 0x02 /* Modem External Interrupt */
+
+
+/* Service Request Configuration Register */
+
+#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
+
+/* Values for register-based Interrupt ACKs */
+#define CD180_ACK_MINT 0x75 /* goes to MSMR */
+#define CD180_ACK_TINT 0x76 /* goes to TSMR */
+#define CD180_ACK_RINT 0x77 /* goes to RSMR */
+
+/* Escape characters */
+
+#define CD180_C_ESC 0x00 /* Escape character */
+#define CD180_C_SBRK 0x81 /* Start sending BREAK */
+#define CD180_C_DELAY 0x82 /* Delay output */
+#define CD180_C_EBRK 0x83 /* Stop sending BREAK */
diff --git a/drivers/sound/cmpci.c b/drivers/sound/cmpci.c
new file mode 100644
index 000000000..f4befd5a3
--- /dev/null
+++ b/drivers/sound/cmpci.c
@@ -0,0 +1,2391 @@
+/*****************************************************************************/
+
+/*
+ * cmpci.c -- C-Media PCI audio driver.
+ *
+ * Copyright (C) 1999 ChenLi Tien (cltien@home.com)
+ *
+ * Based on the PCI drivers by Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ * 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.
+ *
+ * Special thanks to David C. Niemi
+ *
+ *
+ * Module command line parameters:
+ * none so far
+ *
+ *
+ * Supported devices:
+ * /dev/dsp standard /dev/dsp device, (mostly) OSS compatible
+ * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible
+ * /dev/midi simple MIDI UART interface, no ioctl
+ *
+ * The card has both an FM and a Wavetable synth, but I have to figure
+ * out first how to drive them...
+ *
+ * Revision history
+ * 06.05.98 0.1 Initial release
+ * 10.05.98 0.2 Fixed many bugs, esp. ADC rate calculation
+ * First stab at a simple midi interface (no bells&whistles)
+ * 13.05.98 0.3 Fix stupid cut&paste error: set_adc_rate was called instead of
+ * set_dac_rate in the FMODE_WRITE case in cm_open
+ * Fix hwptr out of bounds (now mpg123 works)
+ * 14.05.98 0.4 Don't allow excessive interrupt rates
+ * 08.06.98 0.5 First release using Alan Cox' soundcore instead of miscdevice
+ * 03.08.98 0.6 Do not include modversions.h
+ * Now mixer behaviour can basically be selected between
+ * "OSS documented" and "OSS actual" behaviour
+ * 31.08.98 0.7 Fix realplayer problems - dac.count issues
+ * 10.12.98 0.8 Fix drain_dac trying to wait on not yet initialized DMA
+ * 16.12.98 0.9 Fix a few f_file & FMODE_ bugs
+ * 06.01.99 0.10 remove the silly SA_INTERRUPT flag.
+ * hopefully killed the egcs section type conflict
+ * 12.03.99 0.11 cinfo.blocks should be reset after GETxPTR ioctl.
+ * reported by Johan Maes <joma@telindus.be>
+ * 22.03.99 0.12 return EAGAIN instead of EBUSY when O_NONBLOCK
+ * read/write cannot be executed
+ *
+ */
+
+/*****************************************************************************/
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/sound.h>
+#include <linux/malloc.h>
+#include <linux/soundcard.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <asm/spinlock.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+
+#include "dm.h"
+
+/* --------------------------------------------------------------------- */
+
+#undef OSS_DOCUMENTED_MIXER_SEMANTICS
+
+/* --------------------------------------------------------------------- */
+
+#ifndef PCI_VENDOR_ID_CMEDIA
+#define PCI_VENDOR_ID_CMEDIA 0x13F6
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8338A
+#define PCI_DEVICE_ID_CMEDIA_CM8338A 0x0100
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8338B
+#define PCI_DEVICE_ID_CMEDIA_CM8338B 0x0101
+#endif
+#ifndef PCI_DEVICE_ID_CMEDIA_CM8738
+#define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111
+#endif
+
+#define CM_MAGIC ((PCI_VENDOR_ID_CMEDIA<<16)|PCI_DEVICE_ID_CMEDIA_CM8338A)
+
+/*
+ * CM8338 registers definition
+ */
+
+#define CODEC_CMI_FUNCTRL0 (0x00)
+#define CODEC_CMI_FUNCTRL1 (0x04)
+#define CODEC_CMI_CHFORMAT (0x08)
+#define CODEC_CMI_INT_HLDCLR (0x0C)
+#define CODEC_CMI_INT_STATUS (0x10)
+#define CODEC_CMI_LEGACY_CTRL (0x14)
+#define CODEC_CMI_MISC_CTRL (0x18)
+#define CODEC_CMI_TDMA_POS (0x1C)
+#define CODEC_CMI_MIXER (0x20)
+#define CODEC_SB16_DATA (0x22)
+#define CODEC_SB16_ADDR (0x23)
+#define CODEC_CMI_MIXER1 (0x24)
+#define CODEC_CMI_MIXER2 (0x25)
+#define CODEC_CMI_AUX_VOL (0x26)
+#define CODEC_CMI_MISC (0x27)
+#define CODEC_CMI_AC97 (0x28)
+
+#define CODEC_CMI_CH0_FRAME1 (0x80)
+#define CODEC_CMI_CH0_FRAME2 (0x84)
+#define CODEC_CMI_CH1_FRAME1 (0x88)
+#define CODEC_CMI_CH1_FRAME2 (0x8C)
+
+#define CODEC_CMI_EXT_REG (0xF0)
+#define UCHAR unsigned char
+/*
+** Mixer registers for SB16
+*/
+
+#define DSP_MIX_DATARESETIDX ((UCHAR)(0x00))
+
+#define DSP_MIX_MASTERVOLIDX_L ((UCHAR)(0x30))
+#define DSP_MIX_MASTERVOLIDX_R ((UCHAR)(0x31))
+#define DSP_MIX_VOICEVOLIDX_L ((UCHAR)(0x32))
+#define DSP_MIX_VOICEVOLIDX_R ((UCHAR)(0x33))
+#define DSP_MIX_FMVOLIDX_L ((UCHAR)(0x34))
+#define DSP_MIX_FMVOLIDX_R ((UCHAR)(0x35))
+#define DSP_MIX_CDVOLIDX_L ((UCHAR)(0x36))
+#define DSP_MIX_CDVOLIDX_R ((UCHAR)(0x37))
+#define DSP_MIX_LINEVOLIDX_L ((UCHAR)(0x38))
+#define DSP_MIX_LINEVOLIDX_R ((UCHAR)(0x39))
+
+#define DSP_MIX_MICVOLIDX ((UCHAR)(0x3A))
+#define DSP_MIX_SPKRVOLIDX ((UCHAR)(0x3B))
+
+#define DSP_MIX_OUTMIXIDX ((UCHAR)(0x3C))
+
+#define DSP_MIX_ADCMIXIDX_L ((UCHAR)(0x3D))
+#define DSP_MIX_ADCMIXIDX_R ((UCHAR)(0x3E))
+
+#define DSP_MIX_INGAINIDX_L ((UCHAR)(0x3F))
+#define DSP_MIX_INGAINIDX_R ((UCHAR)(0x40))
+#define DSP_MIX_OUTGAINIDX_L ((UCHAR)(0x41))
+#define DSP_MIX_OUTGAINIDX_R ((UCHAR)(0x42))
+
+#define DSP_MIX_AGCIDX ((UCHAR)(0x43))
+
+#define DSP_MIX_TREBLEIDX_L ((UCHAR)(0x44))
+#define DSP_MIX_TREBLEIDX_R ((UCHAR)(0x45))
+#define DSP_MIX_BASSIDX_L ((UCHAR)(0x46))
+#define DSP_MIX_BASSIDX_R ((UCHAR)(0x47))
+#define CM_CH0_RESET 0x04
+#define CM_CH1_RESET 0x08
+#define CM_EXTENT_CODEC 0x100
+#define CM_EXTENT_MIDI 0x2
+#define CM_EXTENT_SYNTH 0x4
+#define CM_INT_CH0 1
+#define CM_INT_CH1 2
+
+#define CM_CFMT_STEREO 0x01
+#define CM_CFMT_16BIT 0x02
+#define CM_CFMT_MASK 0x03
+#define CM_CFMT_DACSHIFT 0
+#define CM_CFMT_ADCSHIFT 2
+
+static const unsigned sample_size[] = { 1, 2, 2, 4 };
+static const unsigned sample_shift[] = { 0, 1, 1, 2 };
+
+#define CM_CENABLE_RE 0x2
+#define CM_CENABLE_PE 0x1
+
+
+/* MIDI buffer sizes */
+
+#define MIDIINBUF 256
+#define MIDIOUTBUF 256
+
+#define FMODE_MIDI_SHIFT 2
+#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT)
+#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT)
+
+#define FMODE_DMFM 0x10
+
+#define SND_DEV_DSP16 5
+
+/* --------------------------------------------------------------------- */
+
+struct cm_state {
+ /* magic */
+ unsigned int magic;
+
+ /* we keep cm cards in a linked list */
+ struct cm_state *next;
+
+ /* soundcore stuff */
+ int dev_audio;
+ int dev_mixer;
+ int dev_midi;
+ int dev_dmfm;
+
+ /* hardware resources */
+ unsigned int iosb, iobase, iosynth, iomidi, iogame, irq;
+
+ /* mixer stuff */
+ struct {
+ unsigned int modcnt;
+#ifndef OSS_DOCUMENTED_MIXER_SEMANTICS
+ unsigned short vol[13];
+#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+ } mix;
+
+ /* wave stuff */
+ unsigned int rateadc, ratedac;
+ unsigned char fmt, enable;
+
+ spinlock_t lock;
+ struct semaphore open_sem;
+ mode_t open_mode;
+ wait_queue_head_t open_wait;
+
+ struct dmabuf {
+ void *rawbuf;
+ unsigned buforder;
+ unsigned numfrag;
+ unsigned fragshift;
+ unsigned hwptr, swptr;
+ unsigned total_bytes;
+ int count;
+ unsigned error; /* over/underrun */
+ wait_queue_head_t wait;
+ /* redundant, but makes calculations easier */
+ unsigned fragsize;
+ unsigned dmasize;
+ unsigned fragsamples;
+ /* OSS stuff */
+ unsigned mapped:1;
+ unsigned ready:1;
+ unsigned endcleared:1;
+ unsigned ossfragshift;
+ int ossmaxfrags;
+ unsigned subdivision;
+ } dma_dac, dma_adc;
+
+ /* midi stuff */
+ struct {
+ unsigned ird, iwr, icnt;
+ unsigned ord, owr, ocnt;
+ wait_queue_head_t iwait;
+ wait_queue_head_t owait;
+ struct timer_list timer;
+ unsigned char ibuf[MIDIINBUF];
+ unsigned char obuf[MIDIOUTBUF];
+ } midi;
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct cm_state *devs = NULL;
+static unsigned long wavetable_mem = 0;
+
+/* --------------------------------------------------------------------- */
+
+extern __inline__ unsigned ld2(unsigned int x)
+{
+ unsigned r = 0;
+
+ if (x >= 0x10000) {
+ x >>= 16;
+ r += 16;
+ }
+ if (x >= 0x100) {
+ x >>= 8;
+ r += 8;
+ }
+ if (x >= 0x10) {
+ x >>= 4;
+ r += 4;
+ }
+ if (x >= 4) {
+ x >>= 2;
+ r += 2;
+ }
+ if (x >= 2)
+ r++;
+ return r;
+}
+
+/*
+ * hweightN: returns the hamming weight (i.e. the number
+ * of bits set) of a N-bit word
+ */
+
+#ifdef hweight32
+#undef hweight32
+#endif
+
+extern __inline__ unsigned int hweight32(unsigned int w)
+{
+ unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
+ res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+ res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
+ res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
+ return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void set_dmadac(struct cm_state *s, unsigned int addr, unsigned int count)
+{
+ count--;
+ outl(addr, s->iobase + CODEC_CMI_CH0_FRAME1);
+ outw(count, s->iobase + CODEC_CMI_CH0_FRAME2);
+ outb(inb(s->iobase + CODEC_CMI_FUNCTRL0) & ~1, s->iobase + CODEC_CMI_FUNCTRL0);
+ outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 1, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+}
+
+static void set_dmaadc(struct cm_state *s, unsigned int addr, unsigned int count)
+{
+ count--;
+ outl(addr, s->iobase + CODEC_CMI_CH1_FRAME1);
+ outw(count, s->iobase + CODEC_CMI_CH1_FRAME2);
+ outb(inb(s->iobase + CODEC_CMI_FUNCTRL0) | 2, s->iobase + CODEC_CMI_FUNCTRL0);
+ outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 2, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+}
+
+extern __inline__ unsigned get_dmadac(struct cm_state *s)
+{
+ unsigned int curr_addr;
+
+ curr_addr = inl(s->iobase + CODEC_CMI_CH0_FRAME1);
+ curr_addr -= virt_to_bus(s->dma_dac.rawbuf);
+ curr_addr = s->dma_dac.dmasize - curr_addr;
+ curr_addr &= ~(sample_size[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]-1);
+ return curr_addr;
+}
+
+extern __inline__ unsigned get_dmaadc(struct cm_state *s)
+{
+ unsigned int curr_addr;
+
+ curr_addr = inl(s->iobase + CODEC_CMI_CH1_FRAME1);
+ curr_addr -= virt_to_bus(s->dma_adc.rawbuf);
+ curr_addr = s->dma_adc.dmasize - curr_addr;
+ curr_addr &= ~(sample_size[(s->fmt >> CM_CFMT_ADCSHIFT) & CM_CFMT_MASK]-1);
+ return curr_addr;
+}
+
+static void wrmixer(struct cm_state *s, unsigned char idx, unsigned char data)
+{
+ outb(idx, s->iobase + CODEC_SB16_ADDR);
+ udelay(10);
+ outb(data, s->iobase + CODEC_SB16_DATA);
+ udelay(10);
+}
+
+static unsigned char rdmixer(struct cm_state *s, unsigned char idx)
+{
+ unsigned char v;
+
+ outb(idx, s->iobase + CODEC_SB16_ADDR);
+ udelay(10);
+ v = inb(s->iobase + CODEC_SB16_DATA);
+ udelay(10);
+ return v;
+}
+
+static void set_fmt(struct cm_state *s, unsigned char mask, unsigned char data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->lock, flags);
+ if (mask) {
+ s->fmt = inb(s->iobase + CODEC_CMI_CHFORMAT);
+ udelay(10);
+ }
+ s->fmt = (s->fmt & mask) | data;
+ outb(s->fmt, s->iobase + CODEC_CMI_CHFORMAT);
+ spin_unlock_irqrestore(&s->lock, flags);
+ udelay(10);
+}
+
+static void frobindir(struct cm_state *s, unsigned char idx, unsigned char mask, unsigned char data)
+{
+ outb(idx, s->iobase + CODEC_SB16_ADDR);
+ udelay(10);
+ outb((inb(s->iobase + CODEC_SB16_DATA) & mask) | data, s->iobase + CODEC_SB16_DATA);
+ udelay(10);
+}
+
+static struct {
+ unsigned rate;
+ unsigned lower;
+ unsigned upper;
+ unsigned char freq;
+} rate_lookup[] =
+{
+ { 5512, (0 + 5512) / 2, (5512 + 8000) / 2, 0 },
+ { 8000, (5512 + 8000) / 2, (8000 + 11025) / 2, 4 },
+ { 11025, (8000 + 11025) / 2, (11025 + 16000) / 2, 1 },
+ { 16000, (11025 + 16000) / 2, (16000 + 22050) / 2, 5 },
+ { 22050, (16000 + 22050) / 2, (22050 + 32000) / 2, 2 },
+ { 32000, (22050 + 32000) / 2, (32000 + 44100) / 2, 6 },
+ { 44100, (32000 + 44100) / 2, (44100 + 48000) / 2, 3 },
+ { 48000, 48000, 48000, 7 }
+};
+
+static void set_dac_rate(struct cm_state *s, unsigned rate)
+{
+ unsigned long flags;
+ unsigned char freq = 4, val;
+ int i;
+
+ if (rate > 48000)
+ rate = 48000;
+ if (rate < 5512)
+ rate = 5512;
+ for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++)
+ {
+ if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper)
+ {
+ rate = rate_lookup[i].rate;
+ freq = rate_lookup[i].freq;
+ break;
+ }
+ }
+ s->ratedac = rate;
+ freq <<= 2;
+ spin_lock_irqsave(&s->lock, flags);
+ val = inb(s->iobase + CODEC_CMI_FUNCTRL1 + 1) & ~0x1c;
+ outb(val | freq, s->iobase + CODEC_CMI_FUNCTRL1 + 1);
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void set_adc_rate(struct cm_state *s, unsigned rate)
+{
+ unsigned long flags;
+ unsigned char freq = 4, val;
+ int i;
+
+ if (rate > 48000)
+ rate = 48000;
+ if (rate < 5512)
+ rate = 5512;
+ for (i = 0; i < sizeof(rate_lookup) / sizeof(rate_lookup[0]); i++)
+ {
+ if (rate > rate_lookup[i].lower && rate <= rate_lookup[i].upper)
+ {
+ rate = rate_lookup[i].rate;
+ freq = rate_lookup[i].freq;
+ break;
+ }
+ }
+ s->rateadc = rate;
+ freq <<= 5;
+ spin_lock_irqsave(&s->lock, flags);
+ val = inb(s->iobase + CODEC_CMI_FUNCTRL1 + 1) & ~0xe0;
+ outb(val | freq, s->iobase + CODEC_CMI_FUNCTRL1 + 1);
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+extern inline void stop_adc(struct cm_state *s)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->lock, flags);
+ s->enable &= ~CM_CENABLE_RE;
+ /* disable interrupt */
+ outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) & ~2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+ /* disable channel and reset */
+ outb(s->enable | CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+ udelay(10);
+ outb(s->enable & ~CM_CH1_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+extern inline void stop_dac(struct cm_state *s)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->lock, flags);
+ s->enable &= ~CM_CENABLE_PE;
+ /* disable interrupt */
+ outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) & ~1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+ /* disable channel and reset */
+ outb(s->enable | CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+ udelay(10);
+ outb(s->enable & ~CM_CH0_RESET, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void start_dac(struct cm_state *s)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->lock, flags);
+ if ((s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) {
+ s->enable |= CM_CENABLE_PE;
+ outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+ }
+ outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+static void start_adc(struct cm_state *s)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->lock, flags);
+ if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize))
+ && s->dma_adc.ready) {
+ s->enable |= CM_CENABLE_RE;
+ outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+ }
+ outb(inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2) | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+ spin_unlock_irqrestore(&s->lock, flags);
+}
+
+/* --------------------------------------------------------------------- */
+
+#define DMABUF_DEFAULTORDER (17-PAGE_SHIFT)
+#define DMABUF_MINORDER 1
+
+static void dealloc_dmabuf(struct dmabuf *db)
+{
+ unsigned long map, mapend;
+
+ if (db->rawbuf) {
+ /* undo marking the pages as reserved */
+ mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+ for (map = MAP_NR(db->rawbuf); map <= mapend; map++)
+ clear_bit(PG_reserved, &mem_map[map].flags);
+ free_pages((unsigned long)db->rawbuf, db->buforder);
+ }
+ db->rawbuf = NULL;
+ db->mapped = db->ready = 0;
+}
+
+
+/* Ch0 is used for playback, Ch1 is used for recording */
+
+static int prog_dmabuf(struct cm_state *s, unsigned rec)
+{
+ struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac;
+ unsigned rate = rec ? s->rateadc : s->ratedac;
+ int order;
+ unsigned bytepersec;
+ unsigned bufs;
+ unsigned long map, mapend;
+ unsigned char fmt;
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->lock, flags);
+ fmt = s->fmt;
+ if (rec) {
+ s->enable &= ~CM_CENABLE_RE;
+ fmt >>= CM_CFMT_ADCSHIFT;
+ } else {
+ s->enable &= ~CM_CENABLE_PE;
+ fmt >>= CM_CFMT_DACSHIFT;
+ }
+ outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+ spin_unlock_irqrestore(&s->lock, flags);
+ fmt &= CM_CFMT_MASK;
+ db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;
+ if (!db->rawbuf) {
+ db->ready = db->mapped = 0;
+ for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER && !db->rawbuf; order--)
+ db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order);
+ if (!db->rawbuf)
+ return -ENOMEM;
+ db->buforder = order;
+ if ((virt_to_bus(db->rawbuf) ^ (virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff)
+ printk(KERN_DEBUG "cm: DMA buffer crosses 64k boundary: busaddr 0x%lx size %ld\n",
+ virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder);
+ if ((virt_to_bus(db->rawbuf) + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff)
+ printk(KERN_DEBUG "cm: DMA buffer beyond 16MB: busaddr 0x%lx size %ld\n",
+ virt_to_bus(db->rawbuf), PAGE_SIZE << db->buforder);
+ /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */
+ mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);
+ for (map = MAP_NR(db->rawbuf); map <= mapend; map++)
+ set_bit(PG_reserved, &mem_map[map].flags);
+ }
+ bytepersec = rate << sample_shift[fmt];
+ bufs = PAGE_SIZE << db->buforder;
+ if (db->ossfragshift) {
+ if ((1000 << db->ossfragshift) < bytepersec)
+ db->fragshift = ld2(bytepersec/1000);
+ else
+ db->fragshift = db->ossfragshift;
+ } else {
+ db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1));
+ if (db->fragshift < 3)
+ db->fragshift = 3;
+ }
+ db->numfrag = bufs >> db->fragshift;
+ while (db->numfrag < 4 && db->fragshift > 3) {
+ db->fragshift--;
+ db->numfrag = bufs >> db->fragshift;
+ }
+ db->fragsize = 1 << db->fragshift;
+ if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)
+ db->numfrag = db->ossmaxfrags;
+#if 1
+ /* to make fragsize >= 4096 */
+ while (db->fragsize < 4096 && db->numfrag >= 4)
+ {
+ db->fragsize *= 2;
+ db->fragshift++;
+ db->numfrag /= 2;
+ }
+#endif
+ db->fragsamples = db->fragsize >> sample_shift[fmt];
+ db->dmasize = db->numfrag << db->fragshift;
+ memset(db->rawbuf, (fmt & CM_CFMT_16BIT) ? 0 : 0x80, db->dmasize);
+ spin_lock_irqsave(&s->lock, flags);
+ if (rec) {
+ set_dmaadc(s, virt_to_bus(db->rawbuf), db->dmasize >> sample_shift[fmt]);
+ /* program sample counts */
+ outw(db->fragsamples-1, s->iobase + CODEC_CMI_CH1_FRAME2 + 2);
+ } else {
+ set_dmadac(s, virt_to_bus(db->rawbuf), db->dmasize >> sample_shift[fmt]);
+ /* program sample counts */
+ outw(db->fragsamples-1, s->iobase + CODEC_CMI_CH0_FRAME2 + 2);
+ }
+ spin_unlock_irqrestore(&s->lock, flags);
+ db->ready = 1;
+ return 0;
+}
+
+extern __inline__ void clear_advance(struct cm_state *s)
+{
+ unsigned char c = (s->fmt & (CM_CFMT_16BIT << CM_CFMT_DACSHIFT)) ? 0 : 0x80;
+ unsigned char *buf = s->dma_dac.rawbuf;
+ unsigned bsize = s->dma_dac.dmasize;
+ unsigned bptr = s->dma_dac.swptr;
+ unsigned len = s->dma_dac.fragsize;
+
+ if (bptr + len > bsize) {
+ unsigned x = bsize - bptr;
+ memset(buf + bptr, c, x);
+ bptr = 0;
+ len -= x;
+ }
+ memset(buf + bptr, c, len);
+}
+
+/* call with spinlock held! */
+static void cm_update_ptr(struct cm_state *s)
+{
+ unsigned hwptr;
+ int diff;
+
+ /* update ADC pointer */
+ if (s->dma_adc.ready) {
+ hwptr = (s->dma_adc.dmasize - get_dmaadc(s)) % s->dma_adc.dmasize;
+ diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize;
+ s->dma_adc.hwptr = hwptr;
+ s->dma_adc.total_bytes += diff;
+ s->dma_adc.count += diff;
+ if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
+ wake_up(&s->dma_adc.wait);
+ if (!s->dma_adc.mapped) {
+ if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) {
+ s->enable &= ~CM_CENABLE_RE;
+ outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+ s->dma_adc.error++;
+ }
+ }
+ }
+ /* update DAC pointer */
+ if (s->dma_dac.ready) {
+ hwptr = (s->dma_dac.dmasize - get_dmadac(s)) % s->dma_dac.dmasize;
+ diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize;
+ s->dma_dac.hwptr = hwptr;
+ s->dma_dac.total_bytes += diff;
+ if (s->dma_dac.mapped) {
+ s->dma_dac.count += diff;
+ if (s->dma_dac.count >= (signed)s->dma_dac.fragsize)
+ wake_up(&s->dma_dac.wait);
+ } else {
+ s->dma_dac.count -= diff;
+ if (s->dma_dac.count <= 0) {
+ s->enable &= ~CM_CENABLE_PE;
+ outb(s->enable, s->iobase + CODEC_CMI_FUNCTRL0 + 2);
+ s->dma_dac.error++;
+ } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) {
+ clear_advance(s);
+ s->dma_dac.endcleared = 1;
+ }
+ if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize)
+ wake_up(&s->dma_dac.wait);
+ }
+ }
+}
+
+/* hold spinlock for the following! */
+static void cm_handle_midi(struct cm_state *s)
+{
+ unsigned char ch;
+ int wake;
+
+ wake = 0;
+ while (!(inb(s->iomidi+1) & 0x80)) {
+ ch = inb(s->iomidi);
+ if (s->midi.icnt < MIDIINBUF) {
+ s->midi.ibuf[s->midi.iwr] = ch;
+ s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF;
+ s->midi.icnt++;
+ }
+ wake = 1;
+ }
+ if (wake)
+ wake_up(&s->midi.iwait);
+ wake = 0;
+ while (!(inb(s->iomidi+1) & 0x40) && s->midi.ocnt > 0) {
+ outb(s->midi.obuf[s->midi.ord], s->iomidi);
+ s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF;
+ s->midi.ocnt--;
+ if (s->midi.ocnt < MIDIOUTBUF-16)
+ wake = 1;
+ }
+ if (wake)
+ wake_up(&s->midi.owait);
+}
+
+static void cm_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct cm_state *s = (struct cm_state *)dev_id;
+ unsigned int intsrc, intstat;
+
+ /* fastpath out, to ease interrupt sharing */
+ intsrc = inb(s->iobase + CODEC_CMI_INT_STATUS);
+ if (!(intsrc & (CM_INT_CH0 | CM_INT_CH1)))
+ return;
+ spin_lock(&s->lock);
+ intstat = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+ /* disable interrupt */
+ if (intsrc & CM_INT_CH0)
+ outb(intstat & ~1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+ if (intsrc & CM_INT_CH1)
+ outb(intstat & ~2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+ cm_update_ptr(s);
+#ifdef SOUND_CONFIG_CMPCI_MIDI
+ cm_handle_midi(s);
+#endif
+ /* enable interrupt */
+ if (intsrc & CM_INT_CH0)
+ outb(intstat | 1, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+ if (intsrc & CM_INT_CH1)
+ outb(intstat | 2, s->iobase + CODEC_CMI_INT_HLDCLR + 2);
+ spin_unlock(&s->lock);
+}
+
+static void cm_midi_timer(unsigned long data)
+{
+ struct cm_state *s = (struct cm_state *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->lock, flags);
+ cm_handle_midi(s);
+ spin_unlock_irqrestore(&s->lock, flags);
+ s->midi.timer.expires = jiffies+1;
+ add_timer(&s->midi.timer);
+}
+
+/* --------------------------------------------------------------------- */
+
+static const char invalid_magic[] = KERN_CRIT "cm: invalid magic value\n";
+
+#ifdef CONFIG_SOUND_CMPCI /* support multiple chips */
+#define VALIDATE_STATE(s)
+#else
+#define VALIDATE_STATE(s) \
+({ \
+ if (!(s) || (s)->magic != CM_MAGIC) { \
+ printk(invalid_magic); \
+ return -ENXIO; \
+ } \
+})
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#define MT_4 1
+#define MT_5MUTE 2
+#define MT_4MUTEMONO 3
+#define MT_6MUTE 4
+
+static const struct {
+ unsigned left;
+ unsigned right;
+ unsigned type;
+ unsigned rec;
+ unsigned play;
+} mixtable[SOUND_MIXER_NRDEVICES] = {
+ [SOUND_MIXER_CD] = { DSP_MIX_CDVOLIDX_L, DSP_MIX_CDVOLIDX_R, MT_5MUTE, 0x04, 0x02 },
+ [SOUND_MIXER_LINE] = { DSP_MIX_LINEVOLIDX_L, DSP_MIX_LINEVOLIDX_R, MT_5MUTE, 0x10, 0x08 },
+ [SOUND_MIXER_MIC] = { DSP_MIX_MICVOLIDX, CODEC_CMI_MIXER2, MT_4MUTEMONO, 0x01, 0x01 },
+ [SOUND_MIXER_SYNTH] = { DSP_MIX_FMVOLIDX_L, DSP_MIX_FMVOLIDX_R, MT_5MUTE, 0x40, 0x00 },
+ [SOUND_MIXER_VOLUME] = { DSP_MIX_MASTERVOLIDX_L, DSP_MIX_MASTERVOLIDX_R, MT_5MUTE, 0x00, 0x00 },
+ [SOUND_MIXER_PCM] = { DSP_MIX_VOICEVOLIDX_L, DSP_MIX_VOICEVOLIDX_R, MT_5MUTE, 0x00, 0x00 }
+};
+
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+
+static int return_mixval(struct cm_state *s, unsigned i, int *arg)
+{
+ unsigned long flags;
+ unsigned char l, r, rl, rr;
+
+ spin_lock_irqsave(&s->lock, flags);
+ l = rdmixer(s, mixtable[i].left);
+ r = rdmixer(s, mixtable[i].right);
+ spin_unlock_irqrestore(&s->lock, flags);
+ switch (mixtable[i].type) {
+ case MT_4:
+ r &= 0xf;
+ l &= 0xf;
+ rl = 10 + 6 * (l & 15);
+ rr = 10 + 6 * (r & 15);
+ break;
+
+ case MT_4MUTEMONO:
+ rl = 55 - 3 * (l & 15);
+ if (r & 0x10)
+ rl += 45;
+ rr = rl;
+ r = l;
+ break;
+
+ case MT_5MUTE:
+ default:
+ rl = 100 - 3 * (l & 31);
+ rr = 100 - 3 * (r & 31);
+ break;
+
+ case MT_6MUTE:
+ rl = 100 - 3 * (l & 63) / 2;
+ rr = 100 - 3 * (r & 63) / 2;
+ break;
+ }
+ if (l & 0x80)
+ rl = 0;
+ if (r & 0x80)
+ rr = 0;
+ return put_user((rr << 8) | rl, arg);
+}
+
+#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+
+static const unsigned char volidx[SOUND_MIXER_NRDEVICES] =
+{
+ [SOUND_MIXER_CD] = 1,
+ [SOUND_MIXER_LINE] = 2,
+ [SOUND_MIXER_MIC] = 3,
+ [SOUND_MIXER_SYNTH] = 4,
+ [SOUND_MIXER_VOLUME] = 5,
+ [SOUND_MIXER_PCM] = 6
+};
+
+#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+
+static unsigned mixer_recmask(struct cm_state *s)
+{
+ unsigned long flags;
+ int i, j, k;
+
+ spin_lock_irqsave(&s->lock, flags);
+ j = rdmixer(s, DSP_MIX_ADCMIXIDX_L);
+ spin_unlock_irqrestore(&s->lock, flags);
+ j &= 0x7f;
+ for (k = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (j & mixtable[i].rec)
+ k |= 1 << i;
+ return k;
+}
+
+static int mixer_ioctl(struct cm_state *s, unsigned int cmd, unsigned long arg)
+{
+ unsigned long flags;
+ int i, val, j;
+ unsigned char l, r, rl, rr;
+
+ VALIDATE_STATE(s);
+ if (cmd == SOUND_MIXER_INFO) {
+ mixer_info info;
+ strncpy(info.id, "cmpci", sizeof(info.id));
+ strncpy(info.name, "C-Media PCI", sizeof(info.name));
+ info.modify_counter = s->mix.modcnt;
+ if (copy_to_user((void *)arg, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+ }
+ if (cmd == SOUND_OLD_MIXER_INFO) {
+ _old_mixer_info info;
+ strncpy(info.id, "cmpci", sizeof(info.id));
+ strncpy(info.name, "C-Media cmpci", sizeof(info.name));
+ if (copy_to_user((void *)arg, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+ }
+ if (cmd == OSS_GETVERSION)
+ return put_user(SOUND_VERSION, (int *)arg);
+ if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int))
+ return -EINVAL;
+ if (_IOC_DIR(cmd) == _IOC_READ) {
+ switch (_IOC_NR(cmd)) {
+ case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+ return put_user(mixer_recmask(s), (int *)arg);
+
+ case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */
+ return put_user(mixer_recmask(s), (int *)arg);//need fix
+
+ case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */
+ for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (mixtable[i].type)
+ val |= 1 << i;
+ return put_user(val, (int *)arg);
+
+ case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */
+ for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (mixtable[i].rec)
+ val |= 1 << i;
+ return put_user(val, (int *)arg);
+
+ case SOUND_MIXER_OUTMASK: /* Arg contains a bit for each supported recording source */
+ for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (mixtable[i].play)
+ val |= 1 << i;
+ return put_user(val, (int *)arg);
+
+ case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */
+ for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if (mixtable[i].type && mixtable[i].type != MT_4MUTEMONO)
+ val |= 1 << i;
+ return put_user(val, (int *)arg);
+
+ case SOUND_MIXER_CAPS:
+ return put_user(0, (int *)arg);
+
+ default:
+ i = _IOC_NR(cmd);
+ if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type)
+ return -EINVAL;
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+ return return_mixval(s, i, (int *)arg);
+#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+ if (!volidx[i])
+ return -EINVAL;
+ return put_user(s->mix.vol[volidx[i]-1], (int *)arg);
+#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+ }
+ }
+ if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE))
+ return -EINVAL;
+ s->mix.modcnt++;
+ switch (_IOC_NR(cmd)) {
+ case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */
+ get_user_ret(val, (int *)arg, -EFAULT);
+ i = hweight32(val);
+ for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+ if (!(val & (1 << i)))
+ continue;
+ if (!mixtable[i].rec) {
+ val &= ~(1 << i);
+ continue;
+ }
+ j |= mixtable[i].rec;
+ }
+ spin_lock_irqsave(&s->lock, flags);
+ wrmixer(s, DSP_MIX_ADCMIXIDX_L, j);
+ wrmixer(s, DSP_MIX_ADCMIXIDX_R, (j & 1) | j>>1);
+ spin_unlock_irqrestore(&s->lock, flags);
+ return 0;
+
+ case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */
+ get_user_ret(val, (int *)arg, -EFAULT);
+ for (j = i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
+ if (!(val & (1 << i)))
+ continue;
+ if (!mixtable[i].play) {
+ val &= ~(1 << i);
+ continue;
+ }
+ j |= mixtable[i].play;
+ }
+ spin_lock_irqsave(&s->lock, flags);
+ frobindir(s, DSP_MIX_OUTMIXIDX, 0x1f, j);
+ spin_unlock_irqrestore(&s->lock, flags);
+ return 0;
+
+ default:
+ i = _IOC_NR(cmd);
+ if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type)
+ return -EINVAL;
+ get_user_ret(val, (int *)arg, -EFAULT);
+ l = val & 0xff;
+ r = (val >> 8) & 0xff;
+ if (l > 100)
+ l = 100;
+ if (r > 100)
+ r = 100;
+ spin_lock_irqsave(&s->lock, flags);
+ switch (mixtable[i].type) {
+ case MT_4:
+ if (l >= 10)
+ l -= 10;
+ if (r >= 10)
+ r -= 10;
+ frobindir(s, mixtable[i].left, 0xf0, l / 6);
+ frobindir(s, mixtable[i].right, 0xf0, l / 6);
+ break;
+
+ case MT_4MUTEMONO:
+ rl = (l < 4 ? 0 : (l - 5) / 3) & 31;
+ rr = (rl >> 2) & 7;
+ wrmixer(s, mixtable[i].left, rl<<3);
+ outb((inb(s->iobase + CODEC_CMI_MIXER2) & ~0x0e) | rr<<1, s->iobase + CODEC_CMI_MIXER2);
+ break;
+
+ case MT_5MUTE:
+ rl = l < 4 ? 0 : (l - 5) / 3;
+ rr = r < 4 ? 0 : (r - 5) / 3;
+ wrmixer(s, mixtable[i].left, rl<<3);
+ wrmixer(s, mixtable[i].right, rr<<3);
+ break;
+
+ case MT_6MUTE:
+ if (l < 6)
+ rl = 0x00;
+ else
+ rl = l * 2 / 3;
+ if (r < 6)
+ rr = 0x00;
+ else
+ rr = r * 2 / 3;
+ wrmixer(s, mixtable[i].left, rl);
+ wrmixer(s, mixtable[i].right, rr);
+ break;
+ }
+ spin_unlock_irqrestore(&s->lock, flags);
+#ifdef OSS_DOCUMENTED_MIXER_SEMANTICS
+ return return_mixval(s, i, (int *)arg);
+#else /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+ if (!volidx[i])
+ return -EINVAL;
+ s->mix.vol[volidx[i]-1] = val;
+ return put_user(s->mix.vol[volidx[i]-1], (int *)arg);
+#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */
+ }
+}
+
+/* --------------------------------------------------------------------- */
+
+static loff_t cm_llseek(struct file *file, loff_t offset, int origin)
+{
+ return -ESPIPE;
+}
+
+/* --------------------------------------------------------------------- */
+
+static int cm_open_mixdev(struct inode *inode, struct file *file)
+{
+ int minor = MINOR(inode->i_rdev);
+ struct cm_state *s = devs;
+
+ while (s && s->dev_mixer != minor)
+ s = s->next;
+ if (!s)
+ return -ENODEV;
+ VALIDATE_STATE(s);
+ file->private_data = s;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int cm_release_mixdev(struct inode *inode, struct file *file)
+{
+ struct cm_state *s = (struct cm_state *)file->private_data;
+
+ VALIDATE_STATE(s);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int cm_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ return mixer_ioctl((struct cm_state *)file->private_data, cmd, arg);
+}
+
+static /*const*/ struct file_operations cm_mixer_fops = {
+ &cm_llseek,
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* poll */
+ &cm_ioctl_mixdev,
+ NULL, /* mmap */
+ &cm_open_mixdev,
+ NULL, /* flush */
+ &cm_release_mixdev,
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL, /* lock */
+};
+
+/* --------------------------------------------------------------------- */
+
+static int drain_dac(struct cm_state *s, int nonblock)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ unsigned long flags;
+ int count, tmo;
+
+ if (s->dma_dac.mapped || !s->dma_dac.ready)
+ return 0;
+ current->state = TASK_INTERRUPTIBLE;
+ add_wait_queue(&s->dma_dac.wait, &wait);
+ for (;;) {
+ spin_lock_irqsave(&s->lock, flags);
+ count = s->dma_dac.count;
+ spin_unlock_irqrestore(&s->lock, flags);
+ if (count <= 0)
+ break;
+ if (signal_pending(current))
+ break;
+ if (nonblock) {
+ remove_wait_queue(&s->dma_dac.wait, &wait);
+ current->state = TASK_RUNNING;
+ return -EBUSY;
+ }
+ tmo = (count * HZ) / s->ratedac;
+ tmo >>= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK];
+ if (!schedule_timeout(tmo ? : 1) && tmo)
+ printk(KERN_DEBUG "cm: dma timed out??\n");
+ }
+ remove_wait_queue(&s->dma_dac.wait, &wait);
+ current->state = TASK_RUNNING;
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static ssize_t cm_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+ struct cm_state *s = (struct cm_state *)file->private_data;
+ ssize_t ret;
+ unsigned long flags;
+ unsigned swptr;
+ int cnt;
+
+ VALIDATE_STATE(s);
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
+ if (s->dma_adc.mapped)
+ return -ENXIO;
+ if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1)))
+ return ret;
+ if (!access_ok(VERIFY_WRITE, buffer, count))
+ return -EFAULT;
+ ret = 0;
+#if 0
+ spin_lock_irqsave(&s->lock, flags);
+ cm_update_ptr(s);
+ spin_unlock_irqrestore(&s->lock, flags);
+#endif
+ while (count > 0) {
+ spin_lock_irqsave(&s->lock, flags);
+ swptr = s->dma_adc.swptr;
+ cnt = s->dma_adc.dmasize-swptr;
+ if (s->dma_adc.count < cnt)
+ cnt = s->dma_adc.count;
+ spin_unlock_irqrestore(&s->lock, flags);
+ if (cnt > count)
+ cnt = count;
+ if (cnt <= 0) {
+ start_adc(s);
+ if (file->f_flags & O_NONBLOCK)
+ return ret ? ret : -EAGAIN;
+ interruptible_sleep_on(&s->dma_adc.wait);
+ if (signal_pending(current))
+ return ret ? ret : -ERESTARTSYS;
+ continue;
+ }
+ if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt))
+ return ret ? ret : -EFAULT;
+ swptr = (swptr + cnt) % s->dma_adc.dmasize;
+ spin_lock_irqsave(&s->lock, flags);
+ s->dma_adc.swptr = swptr;
+ s->dma_adc.count -= cnt;
+ spin_unlock_irqrestore(&s->lock, flags);
+ count -= cnt;
+ buffer += cnt;
+ ret += cnt;
+ start_adc(s);
+ }
+ return ret;
+}
+
+static ssize_t cm_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+ struct cm_state *s = (struct cm_state *)file->private_data;
+ ssize_t ret;
+ unsigned long flags;
+ unsigned swptr;
+ int cnt;
+
+ VALIDATE_STATE(s);
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
+ if (s->dma_dac.mapped)
+ return -ENXIO;
+ if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+ return ret;
+ if (!access_ok(VERIFY_READ, buffer, count))
+ return -EFAULT;
+ ret = 0;
+#if 0
+ spin_lock_irqsave(&s->lock, flags);
+ cm_update_ptr(s);
+ spin_unlock_irqrestore(&s->lock, flags);
+#endif
+ while (count > 0) {
+ spin_lock_irqsave(&s->lock, flags);
+ if (s->dma_dac.count < 0) {
+ s->dma_dac.count = 0;
+ s->dma_dac.swptr = s->dma_dac.hwptr;
+ }
+ swptr = s->dma_dac.swptr;
+ cnt = s->dma_dac.dmasize-swptr;
+ if (s->dma_dac.count + cnt > s->dma_dac.dmasize)
+ cnt = s->dma_dac.dmasize - s->dma_dac.count;
+ spin_unlock_irqrestore(&s->lock, flags);
+ if (cnt > count)
+ cnt = count;
+ if (cnt <= 0) {
+ start_dac(s);
+ if (file->f_flags & O_NONBLOCK)
+ return ret ? ret : -EAGAIN;
+ interruptible_sleep_on(&s->dma_dac.wait);
+ if (signal_pending(current))
+ return ret ? ret : -ERESTARTSYS;
+ continue;
+ }
+ if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt))
+ return ret ? ret : -EFAULT;
+ swptr = (swptr + cnt) % s->dma_dac.dmasize;
+ spin_lock_irqsave(&s->lock, flags);
+ s->dma_dac.swptr = swptr;
+ s->dma_dac.count += cnt;
+ s->dma_dac.endcleared = 0;
+ spin_unlock_irqrestore(&s->lock, flags);
+ count -= cnt;
+ buffer += cnt;
+ ret += cnt;
+ start_dac(s);
+ }
+ return ret;
+}
+
+static unsigned int cm_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct cm_state *s = (struct cm_state *)file->private_data;
+ unsigned long flags;
+ unsigned int mask = 0;
+
+ VALIDATE_STATE(s);
+ if (file->f_mode & FMODE_WRITE)
+ poll_wait(file, &s->dma_dac.wait, wait);
+ if (file->f_mode & FMODE_READ)
+ poll_wait(file, &s->dma_adc.wait, wait);
+ spin_lock_irqsave(&s->lock, flags);
+ cm_update_ptr(s);
+ if (file->f_mode & FMODE_READ) {
+ if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)
+ mask |= POLLIN | POLLRDNORM;
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ if (s->dma_dac.mapped) {
+ if (s->dma_dac.count >= (signed)s->dma_dac.fragsize)
+ mask |= POLLOUT | POLLWRNORM;
+ } else {
+ if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize)
+ mask |= POLLOUT | POLLWRNORM;
+ }
+ }
+ spin_unlock_irqrestore(&s->lock, flags);
+ return mask;
+}
+
+static int cm_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct cm_state *s = (struct cm_state *)file->private_data;
+ struct dmabuf *db;
+ int ret;
+ unsigned long size;
+
+ VALIDATE_STATE(s);
+ if (vma->vm_flags & VM_WRITE) {
+ if ((ret = prog_dmabuf(s, 1)) != 0)
+ return ret;
+ db = &s->dma_dac;
+ } else if (vma->vm_flags & VM_READ) {
+ if ((ret = prog_dmabuf(s, 0)) != 0)
+ return ret;
+ db = &s->dma_adc;
+ } else
+ return -EINVAL;
+ if (vma->vm_offset != 0)
+ return -EINVAL;
+ size = vma->vm_end - vma->vm_start;
+ if (size > (PAGE_SIZE << db->buforder))
+ return -EINVAL;
+ if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot))
+ return -EAGAIN;
+ db->mapped = 1;
+ return 0;
+}
+
+static int cm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct cm_state *s = (struct cm_state *)file->private_data;
+ unsigned long flags;
+ audio_buf_info abinfo;
+ count_info cinfo;
+ int val, mapped, ret;
+ unsigned char fmtm, fmtd;
+
+ VALIDATE_STATE(s);
+ mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) ||
+ ((file->f_mode & FMODE_READ) && s->dma_adc.mapped);
+ switch (cmd) {
+ case OSS_GETVERSION:
+ return put_user(SOUND_VERSION, (int *)arg);
+
+ case SNDCTL_DSP_SYNC:
+ if (file->f_mode & FMODE_WRITE)
+ return drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/);
+ return 0;
+
+ case SNDCTL_DSP_SETDUPLEX:
+ return 0;
+
+ case SNDCTL_DSP_GETCAPS:
+ return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg);
+
+ case SNDCTL_DSP_RESET:
+ if (file->f_mode & FMODE_WRITE) {
+ stop_dac(s);
+ synchronize_irq();
+ s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0;
+ }
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(s);
+ synchronize_irq();
+ s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0;
+ }
+ return 0;
+
+ case SNDCTL_DSP_SPEED:
+ get_user_ret(val, (int *)arg, -EFAULT);
+ if (val >= 0) {
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(s);
+ s->dma_adc.ready = 0;
+ set_adc_rate(s, val);
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ stop_dac(s);
+ s->dma_dac.ready = 0;
+ set_dac_rate(s, val);
+ }
+ }
+ return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
+
+ case SNDCTL_DSP_STEREO:
+ get_user_ret(val, (int *)arg, -EFAULT);
+ fmtd = 0;
+ fmtm = ~0;
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(s);
+ s->dma_adc.ready = 0;
+ if (val)
+ fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT;
+ else
+ fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT);
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ stop_dac(s);
+ s->dma_dac.ready = 0;
+ if (val)
+ fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT;
+ else
+ fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_DACSHIFT);
+ }
+ set_fmt(s, fmtm, fmtd);
+ return 0;
+
+ case SNDCTL_DSP_CHANNELS:
+ get_user_ret(val, (int *)arg, -EFAULT);
+ if (val != 0) {
+ fmtd = 0;
+ fmtm = ~0;
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(s);
+ s->dma_adc.ready = 0;
+ if (val >= 2)
+ fmtd |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT;
+ else
+ fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_ADCSHIFT);
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ stop_dac(s);
+ s->dma_dac.ready = 0;
+ if (val >= 2)
+ fmtd |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT;
+ else
+ fmtm &= ~(CM_CFMT_STEREO << CM_CFMT_DACSHIFT);
+ }
+ set_fmt(s, fmtm, fmtd);
+ }
+ return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_STEREO << CM_CFMT_ADCSHIFT)
+ : (CM_CFMT_STEREO << CM_CFMT_DACSHIFT))) ? 2 : 1, (int *)arg);
+
+ case SNDCTL_DSP_GETFMTS: /* Returns a mask */
+ return put_user(AFMT_S16_LE|AFMT_U8, (int *)arg);
+
+ case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
+ get_user_ret(val, (int *)arg, -EFAULT);
+ if (val != AFMT_QUERY) {
+ fmtd = 0;
+ fmtm = ~0;
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(s);
+ s->dma_adc.ready = 0;
+ if (val == AFMT_S16_LE)
+ fmtd |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT;
+ else
+ fmtm &= ~(CM_CFMT_16BIT << CM_CFMT_ADCSHIFT);
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ stop_dac(s);
+ s->dma_dac.ready = 0;
+ if (val == AFMT_S16_LE)
+ fmtd |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT;
+ else
+ fmtm &= ~(CM_CFMT_16BIT << CM_CFMT_DACSHIFT);
+ }
+ set_fmt(s, fmtm, fmtd);
+ }
+ return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT)
+ : (CM_CFMT_16BIT << CM_CFMT_DACSHIFT))) ? AFMT_S16_LE : AFMT_U8, (int *)arg);
+
+ case SNDCTL_DSP_POST:
+ return 0;
+
+ case SNDCTL_DSP_GETTRIGGER:
+ val = 0;
+ if (file->f_mode & FMODE_READ && s->enable & CM_CENABLE_RE)
+ val |= PCM_ENABLE_INPUT;
+ if (file->f_mode & FMODE_WRITE && s->enable & CM_CENABLE_PE)
+ val |= PCM_ENABLE_OUTPUT;
+ return put_user(val, (int *)arg);
+
+ case SNDCTL_DSP_SETTRIGGER:
+ get_user_ret(val, (int *)arg, -EFAULT);
+ if (file->f_mode & FMODE_READ) {
+ if (val & PCM_ENABLE_INPUT) {
+ if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1)))
+ return ret;
+ start_adc(s);
+ } else
+ stop_adc(s);
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ if (val & PCM_ENABLE_OUTPUT) {
+ if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0)))
+ return ret;
+ start_dac(s);
+ } else
+ stop_dac(s);
+ }
+ return 0;
+
+ case SNDCTL_DSP_GETOSPACE:
+ if (!(file->f_mode & FMODE_WRITE))
+ return -EINVAL;
+ if (!(s->enable & CM_CENABLE_PE) && (val = prog_dmabuf(s, 0)) != 0)
+ return val;
+ spin_lock_irqsave(&s->lock, flags);
+ cm_update_ptr(s);
+ abinfo.fragsize = s->dma_dac.fragsize;
+ abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count;
+ abinfo.fragstotal = s->dma_dac.numfrag;
+ abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;
+ spin_unlock_irqrestore(&s->lock, flags);
+ return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+ case SNDCTL_DSP_GETISPACE:
+ if (!(file->f_mode & FMODE_READ))
+ return -EINVAL;
+ if (!(s->enable & CM_CENABLE_RE) && (val = prog_dmabuf(s, 1)) != 0)
+ return val;
+ spin_lock_irqsave(&s->lock, flags);
+ cm_update_ptr(s);
+ abinfo.fragsize = s->dma_adc.fragsize;
+ abinfo.bytes = s->dma_adc.count;
+ abinfo.fragstotal = s->dma_adc.numfrag;
+ abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift;
+ spin_unlock_irqrestore(&s->lock, flags);
+ return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
+
+ case SNDCTL_DSP_NONBLOCK:
+ file->f_flags |= O_NONBLOCK;
+ return 0;
+
+ case SNDCTL_DSP_GETODELAY:
+ if (!(file->f_mode & FMODE_WRITE))
+ return -EINVAL;
+ spin_lock_irqsave(&s->lock, flags);
+ cm_update_ptr(s);
+ val = s->dma_dac.count;
+ spin_unlock_irqrestore(&s->lock, flags);
+ return put_user(val, (int *)arg);
+
+ case SNDCTL_DSP_GETIPTR:
+ if (!(file->f_mode & FMODE_READ))
+ return -EINVAL;
+ spin_lock_irqsave(&s->lock, flags);
+ cm_update_ptr(s);
+ cinfo.bytes = s->dma_adc.total_bytes;
+ cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift;
+ cinfo.ptr = s->dma_adc.hwptr;
+ if (s->dma_adc.mapped)
+ s->dma_adc.count &= s->dma_adc.fragsize-1;
+ spin_unlock_irqrestore(&s->lock, flags);
+ return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+ case SNDCTL_DSP_GETOPTR:
+ if (!(file->f_mode & FMODE_WRITE))
+ return -EINVAL;
+ spin_lock_irqsave(&s->lock, flags);
+ cm_update_ptr(s);
+ cinfo.bytes = s->dma_dac.total_bytes;
+ cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift;
+ cinfo.ptr = s->dma_dac.hwptr;
+ if (s->dma_dac.mapped)
+ s->dma_dac.count &= s->dma_dac.fragsize-1;
+ spin_unlock_irqrestore(&s->lock, flags);
+ return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));
+
+ case SNDCTL_DSP_GETBLKSIZE:
+ if (file->f_mode & FMODE_WRITE) {
+ if ((val = prog_dmabuf(s, 0)))
+ return val;
+ return put_user(s->dma_dac.fragsize, (int *)arg);
+ }
+ if ((val = prog_dmabuf(s, 1)))
+ return val;
+ return put_user(s->dma_adc.fragsize, (int *)arg);
+
+ case SNDCTL_DSP_SETFRAGMENT:
+ get_user_ret(val, (int *)arg, -EFAULT);
+ if (file->f_mode & FMODE_READ) {
+ s->dma_adc.ossfragshift = val & 0xffff;
+ s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff;
+ if (s->dma_adc.ossfragshift < 4)
+ s->dma_adc.ossfragshift = 4;
+ if (s->dma_adc.ossfragshift > 15)
+ s->dma_adc.ossfragshift = 15;
+ if (s->dma_adc.ossmaxfrags < 4)
+ s->dma_adc.ossmaxfrags = 4;
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ s->dma_dac.ossfragshift = val & 0xffff;
+ s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff;
+ if (s->dma_dac.ossfragshift < 4)
+ s->dma_dac.ossfragshift = 4;
+ if (s->dma_dac.ossfragshift > 15)
+ s->dma_dac.ossfragshift = 15;
+ if (s->dma_dac.ossmaxfrags < 4)
+ s->dma_dac.ossmaxfrags = 4;
+ }
+ return 0;
+
+ case SNDCTL_DSP_SUBDIVIDE:
+ if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) ||
+ (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision))
+ return -EINVAL;
+ get_user_ret(val, (int *)arg, -EFAULT);
+ if (val != 1 && val != 2 && val != 4)
+ return -EINVAL;
+ if (file->f_mode & FMODE_READ)
+ s->dma_adc.subdivision = val;
+ if (file->f_mode & FMODE_WRITE)
+ s->dma_dac.subdivision = val;
+ return 0;
+
+ case SOUND_PCM_READ_RATE:
+ return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg);
+
+ case SOUND_PCM_READ_CHANNELS:
+ return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_STEREO << CM_CFMT_ADCSHIFT) : (CM_CFMT_STEREO << CM_CFMT_DACSHIFT))) ? 2 : 1, (int *)arg);
+
+ case SOUND_PCM_READ_BITS:
+ return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (CM_CFMT_16BIT << CM_CFMT_ADCSHIFT) : (CM_CFMT_16BIT << CM_CFMT_DACSHIFT))) ? 16 : 8, (int *)arg);
+
+ case SOUND_PCM_WRITE_FILTER:
+ case SNDCTL_DSP_SETSYNCRO:
+ case SOUND_PCM_READ_FILTER:
+ return -EINVAL;
+
+ }
+ return mixer_ioctl(s, cmd, arg);
+}
+
+static int cm_open(struct inode *inode, struct file *file)
+{
+ int minor = MINOR(inode->i_rdev);
+ struct cm_state *s = devs;
+ unsigned char fmtm = ~0, fmts = 0;
+
+ while (s && ((s->dev_audio ^ minor) & ~0xf))
+ s = s->next;
+ if (!s)
+ return -ENODEV;
+ VALIDATE_STATE(s);
+ file->private_data = s;
+ /* wait for device to become free */
+ down(&s->open_sem);
+ while (s->open_mode & file->f_mode) {
+ if (file->f_flags & O_NONBLOCK) {
+ up(&s->open_sem);
+ return -EBUSY;
+ }
+ up(&s->open_sem);
+ interruptible_sleep_on(&s->open_wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ down(&s->open_sem);
+ }
+ if (file->f_mode & FMODE_READ) {
+ fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_ADCSHIFT);
+ if ((minor & 0xf) == SND_DEV_DSP16)
+ fmts |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT;
+ s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0;
+ set_adc_rate(s, 8000);
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ fmtm &= ~((CM_CFMT_STEREO | CM_CFMT_16BIT) << CM_CFMT_DACSHIFT);
+ if ((minor & 0xf) == SND_DEV_DSP16)
+ fmts |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT;
+ s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0;
+ set_dac_rate(s, 8000);
+ }
+ set_fmt(s, fmtm, fmts);
+ s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
+ up(&s->open_sem);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int cm_release(struct inode *inode, struct file *file)
+{
+ struct cm_state *s = (struct cm_state *)file->private_data;
+
+ VALIDATE_STATE(s);
+ if (file->f_mode & FMODE_WRITE)
+ drain_dac(s, file->f_flags & O_NONBLOCK);
+ down(&s->open_sem);
+ if (file->f_mode & FMODE_WRITE) {
+ stop_dac(s);
+ dealloc_dmabuf(&s->dma_dac);
+ }
+ if (file->f_mode & FMODE_READ) {
+ stop_adc(s);
+ dealloc_dmabuf(&s->dma_adc);
+ }
+ s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+ up(&s->open_sem);
+ wake_up(&s->open_wait);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static /*const*/ struct file_operations cm_audio_fops = {
+ &cm_llseek,
+ &cm_read,
+ &cm_write,
+ NULL, /* readdir */
+ &cm_poll,
+ &cm_ioctl,
+ &cm_mmap,
+ &cm_open,
+ NULL, /* flush */
+ &cm_release,
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL, /* lock */
+};
+
+#ifdef CONFIG_SOUND_CMPCI_MIDI
+/* --------------------------------------------------------------------- */
+
+static ssize_t cm_midi_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+ struct cm_state *s = (struct cm_state *)file->private_data;
+ ssize_t ret;
+ unsigned long flags;
+ unsigned ptr;
+ int cnt;
+
+ VALIDATE_STATE(s);
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
+ if (!access_ok(VERIFY_WRITE, buffer, count))
+ return -EFAULT;
+ ret = 0;
+ while (count > 0) {
+ spin_lock_irqsave(&s->lock, flags);
+ ptr = s->midi.ird;
+ cnt = MIDIINBUF - ptr;
+ if (s->midi.icnt < cnt)
+ cnt = s->midi.icnt;
+ spin_unlock_irqrestore(&s->lock, flags);
+ if (cnt > count)
+ cnt = count;
+ if (cnt <= 0) {
+ if (file->f_flags & O_NONBLOCK)
+ return ret ? ret : -EAGAIN;
+ interruptible_sleep_on(&s->midi.iwait);
+ if (signal_pending(current))
+ return ret ? ret : -ERESTARTSYS;
+ continue;
+ }
+ if (copy_to_user(buffer, s->midi.ibuf + ptr, cnt))
+ return ret ? ret : -EFAULT;
+ ptr = (ptr + cnt) % MIDIINBUF;
+ spin_lock_irqsave(&s->lock, flags);
+ s->midi.ird = ptr;
+ s->midi.icnt -= cnt;
+ spin_unlock_irqrestore(&s->lock, flags);
+ count -= cnt;
+ buffer += cnt;
+ ret += cnt;
+ }
+ return ret;
+}
+
+static ssize_t cm_midi_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+ struct cm_state *s = (struct cm_state *)file->private_data;
+ ssize_t ret;
+ unsigned long flags;
+ unsigned ptr;
+ int cnt;
+
+ VALIDATE_STATE(s);
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
+ if (!access_ok(VERIFY_READ, buffer, count))
+ return -EFAULT;
+ ret = 0;
+ while (count > 0) {
+ spin_lock_irqsave(&s->lock, flags);
+ ptr = s->midi.owr;
+ cnt = MIDIOUTBUF - ptr;
+ if (s->midi.ocnt + cnt > MIDIOUTBUF)
+ cnt = MIDIOUTBUF - s->midi.ocnt;
+ if (cnt <= 0)
+ cm_handle_midi(s);
+ spin_unlock_irqrestore(&s->lock, flags);
+ if (cnt > count)
+ cnt = count;
+ if (cnt <= 0) {
+ if (file->f_flags & O_NONBLOCK)
+ return ret ? ret : -EAGAIN;
+ interruptible_sleep_on(&s->midi.owait);
+ if (signal_pending(current))
+ return ret ? ret : -ERESTARTSYS;
+ continue;
+ }
+ if (copy_from_user(s->midi.obuf + ptr, buffer, cnt))
+ return ret ? ret : -EFAULT;
+ ptr = (ptr + cnt) % MIDIOUTBUF;
+ spin_lock_irqsave(&s->lock, flags);
+ s->midi.owr = ptr;
+ s->midi.ocnt += cnt;
+ spin_unlock_irqrestore(&s->lock, flags);
+ count -= cnt;
+ buffer += cnt;
+ ret += cnt;
+ spin_lock_irqsave(&s->lock, flags);
+ cm_handle_midi(s);
+ spin_unlock_irqrestore(&s->lock, flags);
+ }
+ return ret;
+}
+
+static unsigned int cm_midi_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct cm_state *s = (struct cm_state *)file->private_data;
+ unsigned long flags;
+ unsigned int mask = 0;
+
+ VALIDATE_STATE(s);
+ if (file->f_mode & FMODE_WRITE)
+ poll_wait(file, &s->midi.owait, wait);
+ if (file->f_mode & FMODE_READ)
+ poll_wait(file, &s->midi.iwait, wait);
+ spin_lock_irqsave(&s->lock, flags);
+ if (file->f_mode & FMODE_READ) {
+ if (s->midi.icnt > 0)
+ mask |= POLLIN | POLLRDNORM;
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ if (s->midi.ocnt < MIDIOUTBUF)
+ mask |= POLLOUT | POLLWRNORM;
+ }
+ spin_unlock_irqrestore(&s->lock, flags);
+ return mask;
+}
+
+static int cm_midi_open(struct inode *inode, struct file *file)
+{
+ int minor = MINOR(inode->i_rdev);
+ struct cm_state *s = devs;
+ unsigned long flags;
+
+ while (s && s->dev_midi != minor)
+ s = s->next;
+ if (!s)
+ return -ENODEV;
+ VALIDATE_STATE(s);
+ file->private_data = s;
+ /* wait for device to become free */
+ down(&s->open_sem);
+ while (s->open_mode & (file->f_mode << FMODE_MIDI_SHIFT)) {
+ if (file->f_flags & O_NONBLOCK) {
+ up(&s->open_sem);
+ return -EBUSY;
+ }
+ up(&s->open_sem);
+ interruptible_sleep_on(&s->open_wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ down(&s->open_sem);
+ }
+ spin_lock_irqsave(&s->lock, flags);
+ if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+ s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+ s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+ /* enable MPU-401 */
+ outb(inb(s->iobase + CODEC_CMI_FUNCTRL1) | 4, s->iobase + CODEC_CMI_FUNCTRL1);
+ outb(0xff, s->iomidi+1); /* reset command */
+ if (!(inb(s->iomidi+1) & 0x80))
+ inb(s->iomidi);
+ outb(0x3f, s->iomidi+1); /* uart command */
+ if (!(inb(s->iomidi+1) & 0x80))
+ inb(s->iomidi);
+ s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+ init_timer(&s->midi.timer);
+ s->midi.timer.expires = jiffies+1;
+ s->midi.timer.data = (unsigned long)s;
+ s->midi.timer.function = cm_midi_timer;
+ add_timer(&s->midi.timer);
+ }
+ if (file->f_mode & FMODE_READ) {
+ s->midi.ird = s->midi.iwr = s->midi.icnt = 0;
+ }
+ if (file->f_mode & FMODE_WRITE) {
+ s->midi.ord = s->midi.owr = s->midi.ocnt = 0;
+ }
+ spin_unlock_irqrestore(&s->lock, flags);
+ s->open_mode |= (file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ | FMODE_MIDI_WRITE);
+ up(&s->open_sem);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int cm_midi_release(struct inode *inode, struct file *file)
+{
+ struct cm_state *s = (struct cm_state *)file->private_data;
+ DECLARE_WAITQUEUE(wait, current);
+ unsigned long flags;
+ unsigned count, tmo;
+
+ VALIDATE_STATE(s);
+
+ if (file->f_mode & FMODE_WRITE) {
+ current->state = TASK_INTERRUPTIBLE;
+ add_wait_queue(&s->midi.owait, &wait);
+ for (;;) {
+ spin_lock_irqsave(&s->lock, flags);
+ count = s->midi.ocnt;
+ spin_unlock_irqrestore(&s->lock, flags);
+ if (count <= 0)
+ break;
+ if (signal_pending(current))
+ break;
+ if (file->f_flags & O_NONBLOCK) {
+ remove_wait_queue(&s->midi.owait, &wait);
+ current->state = TASK_RUNNING;
+ return -EBUSY;
+ }
+ tmo = (count * HZ) / 3100;
+ if (!schedule_timeout(tmo ? : 1) && tmo)
+ printk(KERN_DEBUG "cm: midi timed out??\n");
+ }
+ remove_wait_queue(&s->midi.owait, &wait);
+ current->state = TASK_RUNNING;
+ }
+ down(&s->open_sem);
+ s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
+ spin_lock_irqsave(&s->lock, flags);
+ if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
+ del_timer(&s->midi.timer);
+ outb(0xff, s->iomidi+1); /* reset command */
+ if (!(inb(s->iomidi+1) & 0x80))
+ inb(s->iomidi);
+ /* disable MPU-401 */
+ outb(inb(s->iobase + CODEC_CMI_FUNCTRL1) & ~4, s->iobase + CODEC_CMI_FUNCTRL1);
+ }
+ spin_unlock_irqrestore(&s->lock, flags);
+ up(&s->open_sem);
+ wake_up(&s->open_wait);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static /*const*/ struct file_operations cm_midi_fops = {
+ &cm_llseek,
+ &cm_midi_read,
+ &cm_midi_write,
+ NULL, /* readdir */
+ &cm_midi_poll,
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ &cm_midi_open,
+ NULL, /* flush */
+ &cm_midi_release,
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL, /* lock */
+};
+#endif
+
+/* --------------------------------------------------------------------- */
+
+#ifdef CONFIG_SOUND_CMPCI_FM
+static int cm_dmfm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ static const unsigned char op_offset[18] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15
+ };
+ struct cm_state *s = (struct cm_state *)file->private_data;
+ struct dm_fm_voice v;
+ struct dm_fm_note n;
+ struct dm_fm_params p;
+ unsigned int io;
+ unsigned int regb;
+
+ switch (cmd) {
+ case FM_IOCTL_RESET:
+ for (regb = 0xb0; regb < 0xb9; regb++) {
+ outb(regb, s->iosynth);
+ outb(0, s->iosynth+1);
+ outb(regb, s->iosynth+2);
+ outb(0, s->iosynth+3);
+ }
+ return 0;
+
+ case FM_IOCTL_PLAY_NOTE:
+ if (copy_from_user(&n, (void *)arg, sizeof(n)))
+ return -EFAULT;
+ if (n.voice >= 18)
+ return -EINVAL;
+ if (n.voice >= 9) {
+ regb = n.voice - 9;
+ io = s->iosynth+2;
+ } else {
+ regb = n.voice;
+ io = s->iosynth;
+ }
+ outb(0xa0 + regb, io);
+ outb(n.fnum & 0xff, io+1);
+ outb(0xb0 + regb, io);
+ outb(((n.fnum >> 8) & 3) | ((n.octave & 7) << 2) | ((n.key_on & 1) << 5), io+1);
+ return 0;
+
+ case FM_IOCTL_SET_VOICE:
+ if (copy_from_user(&v, (void *)arg, sizeof(v)))
+ return -EFAULT;
+ if (v.voice >= 18)
+ return -EINVAL;
+ regb = op_offset[v.voice];
+ io = s->iosynth + ((v.op & 1) << 1);
+ outb(0x20 + regb, io);
+ outb(((v.am & 1) << 7) | ((v.vibrato & 1) << 6) | ((v.do_sustain & 1) << 5) |
+ ((v.kbd_scale & 1) << 4) | (v.harmonic & 0xf), io+1);
+ outb(0x40 + regb, io);
+ outb(((v.scale_level & 0x3) << 6) | (v.volume & 0x3f), io+1);
+ outb(0x60 + regb, io);
+ outb(((v.attack & 0xf) << 4) | (v.decay & 0xf), io+1);
+ outb(0x80 + regb, io);
+ outb(((v.sustain & 0xf) << 4) | (v.release & 0xf), io+1);
+ outb(0xe0 + regb, io);
+ outb(v.waveform & 0x7, io+1);
+ if (n.voice >= 9) {
+ regb = n.voice - 9;
+ io = s->iosynth+2;
+ } else {
+ regb = n.voice;
+ io = s->iosynth;
+ }
+ outb(0xc0 + regb, io);
+ outb(((v.right & 1) << 5) | ((v.left & 1) << 4) | ((v.feedback & 7) << 1) |
+ (v.connection & 1), io+1);
+ return 0;
+
+ case FM_IOCTL_SET_PARAMS:
+ if (copy_from_user(&p, (void *)arg, sizeof(p)))
+ return -EFAULT;
+ outb(0x08, s->iosynth);
+ outb((p.kbd_split & 1) << 6, s->iosynth+1);
+ outb(0xbd, s->iosynth);
+ outb(((p.am_depth & 1) << 7) | ((p.vib_depth & 1) << 6) | ((p.rhythm & 1) << 5) | ((p.bass & 1) << 4) |
+ ((p.snare & 1) << 3) | ((p.tomtom & 1) << 2) | ((p.cymbal & 1) << 1) | (p.hihat & 1), s->iosynth+1);
+ return 0;
+
+ case FM_IOCTL_SET_OPL:
+ outb(4, s->iosynth+2);
+ outb(arg, s->iosynth+3);
+ return 0;
+
+ case FM_IOCTL_SET_MODE:
+ outb(5, s->iosynth+2);
+ outb(arg & 1, s->iosynth+3);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cm_dmfm_open(struct inode *inode, struct file *file)
+{
+ int minor = MINOR(inode->i_rdev);
+ struct cm_state *s = devs;
+
+ while (s && s->dev_dmfm != minor)
+ s = s->next;
+ if (!s)
+ return -ENODEV;
+ VALIDATE_STATE(s);
+ file->private_data = s;
+ /* wait for device to become free */
+ down(&s->open_sem);
+ while (s->open_mode & FMODE_DMFM) {
+ if (file->f_flags & O_NONBLOCK) {
+ up(&s->open_sem);
+ return -EBUSY;
+ }
+ up(&s->open_sem);
+ interruptible_sleep_on(&s->open_wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ down(&s->open_sem);
+ }
+ /* init the stuff */
+ outb(1, s->iosynth);
+ outb(0x20, s->iosynth+1); /* enable waveforms */
+ outb(4, s->iosynth+2);
+ outb(0, s->iosynth+3); /* no 4op enabled */
+ outb(5, s->iosynth+2);
+ outb(1, s->iosynth+3); /* enable OPL3 */
+ s->open_mode |= FMODE_DMFM;
+ up(&s->open_sem);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int cm_dmfm_release(struct inode *inode, struct file *file)
+{
+ struct cm_state *s = (struct cm_state *)file->private_data;
+ unsigned int regb;
+
+ VALIDATE_STATE(s);
+ down(&s->open_sem);
+ s->open_mode &= ~FMODE_DMFM;
+ for (regb = 0xb0; regb < 0xb9; regb++) {
+ outb(regb, s->iosynth);
+ outb(0, s->iosynth+1);
+ outb(regb, s->iosynth+2);
+ outb(0, s->iosynth+3);
+ }
+ up(&s->open_sem);
+ wake_up(&s->open_wait);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static /*const*/ struct file_operations cm_dmfm_fops = {
+ &cm_llseek,
+ NULL, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* poll */
+ &cm_dmfm_ioctl,
+ NULL, /* mmap */
+ &cm_dmfm_open,
+ NULL, /* flush */
+ &cm_dmfm_release,
+ NULL, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL, /* lock */
+};
+#endif /* CONFIG_SOUND_CMPCI_FM */
+
+/* --------------------------------------------------------------------- */
+
+/* maximum number of devices */
+#define NR_DEVICE 5
+
+#if 0
+static int reverb[NR_DEVICE] = { 0, };
+
+static int wavetable[NR_DEVICE] = { 0, };
+#endif
+
+/* --------------------------------------------------------------------- */
+
+static struct initvol {
+ int mixch;
+ int vol;
+} initvol[] __initdata = {
+ { SOUND_MIXER_WRITE_CD, 0x4040 },
+ { SOUND_MIXER_WRITE_LINE, 0x4040 },
+ { SOUND_MIXER_WRITE_MIC, 0x4040 },
+ { SOUND_MIXER_WRITE_SYNTH, 0x4040 },
+ { SOUND_MIXER_WRITE_VOLUME, 0x4040 },
+ { SOUND_MIXER_WRITE_PCM, 0x4040 }
+};
+
+#ifdef MODULE
+__initfunc(int init_module(void))
+#else
+__initfunc(int init_cmpci(void))
+#endif
+{
+ struct cm_state *s;
+ struct pci_dev *pcidev = NULL;
+ mm_segment_t fs;
+ int i, val, index = 0;
+ struct {
+ unsigned short deviceid;
+ char *devicename;
+ } devicetable[] =
+ {
+ { PCI_DEVICE_ID_CMEDIA_CM8338A, "CM8338A" },
+ { PCI_DEVICE_ID_CMEDIA_CM8338B, "CM8338B" },
+ { PCI_DEVICE_ID_CMEDIA_CM8738, "CM8738" },
+ };
+ char *devicename = "unknown";
+
+#ifdef CONFIG_PCI
+ if (!pci_present()) /* No PCI bus in this machine! */
+#endif
+ return -ENODEV;
+ printk(KERN_INFO "cm: version v1.1 time " __TIME__ " " __DATE__ "\n");
+#if 0
+ if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT)))
+ printk(KERN_INFO "cm: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n");
+#endif
+ while (index < NR_DEVICE && pcidev == NULL && (
+ (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A, pcidev)) ||
+ (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B, pcidev)) ||
+ (pcidev = pci_find_device(PCI_VENDOR_ID_CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738, pcidev)))) {
+ if (pcidev->irq == 0)
+ continue;
+ if (!(s = kmalloc(sizeof(struct cm_state), GFP_KERNEL))) {
+ printk(KERN_WARNING "cm: out of memory\n");
+ continue;
+ }
+ /* search device name */
+ for (i = 0; i < sizeof(devicetable) / sizeof(devicetable[0]); i++)
+ {
+ if (devicetable[i].deviceid == pcidev->device)
+ {
+ devicename = devicetable[i].devicename;
+ break;
+ }
+ }
+ memset(s, 0, sizeof(struct cm_state));
+ init_waitqueue_head(&s->dma_adc.wait);
+ init_waitqueue_head(&s->dma_dac.wait);
+ init_waitqueue_head(&s->open_wait);
+ init_waitqueue_head(&s->midi.iwait);
+ init_waitqueue_head(&s->midi.owait);
+ init_MUTEX(&s->open_sem);
+ s->magic = CM_MAGIC;
+ s->iobase = pcidev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;
+#ifdef CONFIG_SOUND_CMPCI_FM
+ s->iosynth = 0x388;
+#endif
+#ifdef CONFIG_SOUND_CMPCI_MIDI
+ s->iomidi = 0x330;
+#endif
+ if (s->iobase == 0)
+ continue;
+ s->irq = pcidev->irq;
+
+ if (check_region(s->iobase, CM_EXTENT_CODEC)) {
+ printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iobase, s->iobase+CM_EXTENT_CODEC-1);
+ goto err_region5;
+ }
+ request_region(s->iobase, CM_EXTENT_CODEC, "cmpci");
+#ifdef CONFIG_SOUND_CMPCI_MIDI
+ if (check_region(s->iomidi, CM_EXTENT_MIDI)) {
+ printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iomidi, s->iomidi+CM_EXTENT_MIDI-1);
+ goto err_region4;
+ }
+ request_region(s->iomidi, CM_EXTENT_MIDI, "cmpci Midi");
+ /* set IO based at 0x330 */
+ outb(inb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3) & ~0x60, s->iobase + CODEC_CMI_LEGACY_CTRL + 3);
+#endif
+#ifdef CONFIG_SOUND_CMPCI_FM
+ if (check_region(s->iosynth, CM_EXTENT_SYNTH)) {
+ printk(KERN_ERR "cm: io ports %#x-%#x in use\n", s->iosynth, s->iosynth+CM_EXTENT_SYNTH-1);
+ goto err_region1;
+ }
+ request_region(s->iosynth, CM_EXTENT_SYNTH, "cmpci FM");
+ /* enable FM */
+ outb(inb(s->iobase + CODEC_CMI_MISC_CTRL + 2) | 8, s->iobase + CODEC_CMI_MISC_CTRL);
+#endif
+ /* initialize codec registers */
+ outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* disable ints */
+ outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* reset channels */
+ /* reset mixer */
+ wrmixer(s, DSP_MIX_DATARESETIDX, 0);
+
+ /* request irq */
+ if (request_irq(s->irq, cm_interrupt, SA_SHIRQ, "cmpci", s)) {
+ printk(KERN_ERR "cm: irq %u in use\n", s->irq);
+ goto err_irq;
+ }
+ printk(KERN_INFO "cm: found %s adapter at io %#06x irq %u\n",
+ devicename, s->iobase, s->irq);
+ /* register devices */
+ if ((s->dev_audio = register_sound_dsp(&cm_audio_fops, -1)) < 0)
+ goto err_dev1;
+ if ((s->dev_mixer = register_sound_mixer(&cm_mixer_fops, -1)) < 0)
+ goto err_dev2;
+#ifdef CONFIG_SOUND_CMPCI_MIDI
+ if ((s->dev_midi = register_sound_midi(&cm_midi_fops, -1)) < 0)
+ goto err_dev3;
+#endif
+#ifdef CONFIG_SOUND_CMPCI_FM
+ if ((s->dev_dmfm = register_sound_special(&cm_dmfm_fops, 15 /* ?? */)) < 0)
+ goto err_dev4;
+#endif
+ /* initialize the chips */
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ /* set mixer output */
+ frobindir(s, DSP_MIX_OUTMIXIDX, 0x1f, 0x1f);
+ /* set mixer input */
+ val = SOUND_MASK_LINE|SOUND_MASK_SYNTH|SOUND_MASK_CD|SOUND_MASK_MIC;
+ mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long)&val);
+ for (i = 0; i < sizeof(initvol)/sizeof(initvol[0]); i++) {
+ val = initvol[i].vol;
+ mixer_ioctl(s, initvol[i].mixch, (unsigned long)&val);
+ }
+ set_fs(fs);
+ /* queue it for later freeing */
+ s->next = devs;
+ devs = s;
+ index++;
+ continue;
+
+ err_dev4:
+ unregister_sound_midi(s->dev_midi);
+ err_dev3:
+ unregister_sound_mixer(s->dev_mixer);
+ err_dev2:
+ unregister_sound_dsp(s->dev_audio);
+ err_dev1:
+ printk(KERN_ERR "cm: cannot register misc device\n");
+ free_irq(s->irq, s);
+ err_irq:
+#ifdef CONFIG_SOUND_CMPCI_FM
+ release_region(s->iosynth, CM_EXTENT_SYNTH);
+ err_region1:
+#endif
+#ifdef CONFIG_SOUND_CMPCI_MIDI
+ release_region(s->iomidi, CM_EXTENT_MIDI);
+#endif
+ err_region4:
+ release_region(s->iobase, CM_EXTENT_CODEC);
+ err_region5:
+ kfree_s(s, sizeof(struct cm_state));
+ }
+ if (!devs) {
+ if (wavetable_mem)
+ free_pages(wavetable_mem, 20-PAGE_SHIFT);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+
+#if 0
+MODULE_PARM(wavetable, "1-" __MODULE_STRING(NR_DEVICE) "i");
+MODULE_PARM_DESC(wavetable, "if 1 the wavetable synth is enabled");
+#endif
+
+MODULE_AUTHOR("ChenLi Tien, cltien@home.com");
+MODULE_DESCRIPTION("CMPCI Audio Driver");
+
+void cleanup_module(void)
+{
+ struct cm_state *s;
+
+ while ((s = devs)) {
+ devs = devs->next;
+ outb(0, s->iobase + CODEC_CMI_INT_HLDCLR + 2); /* disable ints */
+ synchronize_irq();
+ outb(0, s->iobase + CODEC_CMI_FUNCTRL0 + 2); /* reset channels */
+ free_irq(s->irq, s);
+
+ /* reset mixer */
+ wrmixer(s, DSP_MIX_DATARESETIDX, 0);
+
+ release_region(s->iobase, CM_EXTENT_CODEC);
+#ifdef CONFIG_SOUND_CMPCI_MIDI
+ release_region(s->iomidi, CM_EXTENT_MIDI);
+#endif
+#ifdef CONFIG_SOUND_CMPCI_FM
+ release_region(s->iosynth, CM_EXTENT_SYNTH);
+#endif
+ unregister_sound_dsp(s->dev_audio);
+ unregister_sound_mixer(s->dev_mixer);
+#ifdef CONFIG_SOUND_CMPCI_MIDI
+ unregister_sound_midi(s->dev_midi);
+#endif
+#ifdef CONFIG_SOUND_CMPCI_FM
+ unregister_sound_special(s->dev_dmfm);
+#endif
+ kfree_s(s, sizeof(struct cm_state));
+ }
+ if (wavetable_mem)
+ free_pages(wavetable_mem, 20-PAGE_SHIFT);
+ printk(KERN_INFO "cm: unloading\n");
+}
+
+#endif /* MODULE */
diff --git a/drivers/usb/acm.c b/drivers/usb/acm.c
new file mode 100644
index 000000000..d4796e28c
--- /dev/null
+++ b/drivers/usb/acm.c
@@ -0,0 +1,293 @@
+/*
+ * USB Abstract Control Model based on Brad Keryan's USB busmouse driver
+ *
+ * Armin Fuerst 5/8/1999
+ *
+ * version 0.2: Improved Bulk transfer. TX led now flashes every time data is
+ * sent. Send Encapsulated Data is not needed, nor does it do anything.
+ * Why's that ?!? Thanks to Thomas Sailer for his close look at the bulk code.
+ * He told me about some importand bugs. (5/21/99)
+ *
+ * version 0.1: Bulk transfer for uhci seems to work now, no dangling tds any
+ * more. TX led of the ISDN TA flashed the first time. Does this mean it works?
+ * The interrupt of the ctrl endpoint crashes the kernel => no read possible
+ * (5/19/99)
+ *
+ * version 0.0: Driver sets up configuration, sets up data pipes, opens misc
+ * device. No actual data transfer is done, since we don't have bulk transfer,
+ * yet. Purely skeleton for now. (5/8/99)
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <asm/spinlock.h>
+
+#include "usb.h"
+
+#define USB_ACM_MINOR 32
+
+struct acm_state {
+ int present; /* this acm is plugged in */
+ int active; /* someone is has this acm's device open */
+ int serstate; /* Status of the serial port (rate, handshakelines,...) */
+ struct usb_device *dev;
+ unsigned ctrlbuffer; /*buffer for control messages*/
+ unsigned int readendp,writeendp,ctrlendp;
+ unsigned int readpipe,writepipe,ctrlpipe;
+ char buffer;
+};
+
+static struct acm_state static_acm_state;
+
+spinlock_t usb_acm_lock = SPIN_LOCK_UNLOCKED;
+
+static int acm_irq(int state, void *__buffer, void *dev_id)
+{
+// unsigned char *data = __buffer;
+ struct acm_state *acm = &static_acm_state;
+ devrequest *dr;
+
+ dr=__buffer;
+ printk("ACM_USB_IRQ\n");
+ printk("reqtype: %02X\n",dr->requesttype);
+ printk("request: %02X\n",dr->request);
+ printk("wValue: %02X\n",dr->value);
+ printk("wIndex: %02X\n",dr->index);
+ printk("wLength: %02X\n",dr->length);
+
+ switch(dr->request) {
+ //Network connection
+ case 0x00:
+ printk("Network connection: ");
+ if (dr->request==0) printk("disconnected\n");
+ if (dr->request==1) printk("connected\n");
+ break;
+
+ //Response available
+ case 0x01:
+ printk("Response available\n");
+ acm->buffer=1;
+ break;
+
+ //Set serial line state
+ case 0x20:
+ if ((dr->index==1)&&(dr->length==2)) {
+ acm->serstate=acm->ctrlbuffer;
+ printk("Serstate: %02X\n",acm->ctrlbuffer);
+ }
+ break;
+ }
+/*
+ if(!acm->active)
+ return 1;
+*/
+ return 1;
+}
+
+static int release_acm(struct inode * inode, struct file * file)
+{
+ struct acm_state *acm = &static_acm_state;
+ printk("ACM_FILE_RELEASE\n");
+
+// fasync_acm(-1, file, 0);
+ if (--acm->active)
+ return 0;
+ return 0;
+}
+
+static int open_acm(struct inode * inode, struct file * file)
+{
+ struct acm_state *acm = &static_acm_state;
+ printk("USB_FILE_OPEN\n");
+
+ if (!acm->present)
+ return -EINVAL;
+ if (acm->active++)
+ return 0;
+ return 0;
+}
+
+static ssize_t write_acm(struct file * file,
+ const char * buffer, size_t count, loff_t *ppos)
+{
+ devrequest dr;
+ struct acm_state *acm = &static_acm_state;
+ unsigned long retval;
+
+ printk("USB_FILE_WRITE\n");
+//Huh, i seem to got that wrong, we don't need this ?!?
+/*
+ dr.requesttype = USB_TYPE_CLASS | USB_RT_ENDPOINT;
+ dr.request = 0;
+ dr.value = 0;
+ dr.index = acm->writeendp;
+ dr.length = count;
+ acm->dev->bus->op->control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), &dr, NULL, 0);
+*/
+
+ acm->dev->bus->op->bulk_msg(acm->dev,&acm->writepipe,buffer, count, &retval);
+ return -EINVAL;
+}
+
+
+static ssize_t read_acm(struct file * file, const char * buffer, size_t count, loff_t *ppos)
+{
+ devrequest dr;
+ struct acm_state *acm = &static_acm_state;
+ unsigned long retval;
+ printk("USB_FILE_READ\n");
+// if (!acm->buffer) return -1;
+ acm->buffer=0;
+//We don't need this
+/*
+ printk("writing control msg\n");
+ dr.requesttype = USB_TYPE_CLASS | USB_RT_ENDPOINT | 0x80;
+ dr.request = 1;
+ dr.value = 0;
+ dr.index = acm->readendp;
+ dr.length = 0;
+ acm->dev->bus->op->control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), &dr, NULL, 0);
+*/
+ printk("reading:>%s<\n",buffer);
+ acm->dev->bus->op->bulk_msg(acm->dev,&acm->readpipe,buffer, 1,&retval);
+ printk("done:>%s<\n",buffer);
+ return 1;
+}
+
+struct file_operations usb_acm_fops = {
+ NULL, /* acm_seek */
+ read_acm,
+ write_acm,
+ NULL, /* acm_readdir */
+ NULL, /* acm_poll */
+ NULL, /* acm_ioctl */
+ NULL, /* acm_mmap */
+ open_acm,
+ NULL, /* flush */
+ release_acm,
+ NULL,
+ NULL, /*fasync*/
+};
+
+static struct miscdevice usb_acm = {
+ USB_ACM_MINOR, "USB ACM", &usb_acm_fops
+};
+
+static int acm_probe(struct usb_device *dev)
+{
+ struct usb_interface_descriptor *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct acm_state *acm = &static_acm_state;
+ int cfgnum;
+
+ /* Only use CDC */
+ if (dev->descriptor.bDeviceClass != 2 ||
+ dev->descriptor.bDeviceSubClass != 0 ||
+ dev->descriptor.bDeviceProtocol != 0)
+ return -1;
+
+ /*Now scan all configs for a ACM configuration*/
+ for (cfgnum=0;cfgnum<dev->descriptor.bNumConfigurations;cfgnum++) {
+ /* The first one should be Communications interface? */
+ interface = &dev->config[cfgnum].altsetting[0].interface[0];
+ if (interface->bInterfaceClass != 2 ||
+ interface->bInterfaceSubClass != 2 ||
+ interface->bInterfaceProtocol != 1 ||
+ interface->bNumEndpoints != 1)
+ continue;
+
+ /*Which uses an interrupt input */
+ endpoint = &interface->endpoint[0];
+ if ((endpoint->bEndpointAddress & 0x80) != 0x80 ||
+ (endpoint->bmAttributes & 3) != 3)
+ continue;
+
+ /* The second one should be a Data interface? */
+ interface = &dev->config[cfgnum].altsetting[0].interface[1];
+ if (interface->bInterfaceClass != 10 ||
+ interface->bInterfaceSubClass != 0 ||
+ interface->bInterfaceProtocol != 0 ||
+ interface->bNumEndpoints != 2)
+ continue;
+
+ /*With a bulk input */
+ endpoint = &interface->endpoint[0];
+ if ((endpoint->bEndpointAddress & 0x80) != 0x80 ||
+ (endpoint->bmAttributes & 3) != 2)
+ continue;
+
+ /*And a bulk output */
+ endpoint = &interface->endpoint[1];
+ if ((endpoint->bEndpointAddress & 0x80) == 0x80 ||
+ (endpoint->bmAttributes & 3) != 2)
+ continue;
+
+ printk("USB ACM found\n");
+ usb_set_configuration(dev, dev->config[cfgnum].bConfigurationValue);
+ acm->dev=dev;
+ acm->readendp=dev->config[cfgnum].altsetting[0].interface[1].endpoint[0].bEndpointAddress;
+ acm->writeendp=dev->config[cfgnum].altsetting[0].interface[1].endpoint[1].bEndpointAddress;
+ acm->ctrlendp=dev->config[cfgnum].altsetting[0].interface[0].endpoint[0].bEndpointAddress;
+ acm->readpipe=usb_rcvbulkpipe(dev,acm->readendp);
+ acm->writepipe=usb_sndbulkpipe(dev,acm->writeendp);
+ usb_request_irq(dev,acm->ctrlpipe=usb_rcvctrlpipe(dev,acm->ctrlendp), acm_irq, dev->config[cfgnum].altsetting[0].interface[0].endpoint[0].bInterval, &acm->ctrlbuffer);
+ acm->present = 1;
+ acm->buffer=0;
+ return 0;
+ }
+
+ return -1;
+}
+
+static void acm_disconnect(struct usb_device *dev)
+{
+ struct acm_state *acm = &static_acm_state;
+
+ /* this might need work */
+ acm->present = 0;
+}
+
+static struct usb_driver acm_driver = {
+ "acm",
+ acm_probe,
+ acm_disconnect,
+ { NULL, NULL }
+};
+
+int usb_acm_init(void)
+{
+ struct acm_state *acm = &static_acm_state;
+
+ misc_register(&usb_acm);
+
+ acm->present = acm->active = 0;
+
+ usb_register(&acm_driver);
+ printk(KERN_INFO "USB ACM registered.\n");
+ return 0;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+ return usb_acm_init();
+}
+
+void cleanup_module(void)
+{
+ /* this, too, probably needs work */
+ usb_deregister(&acm_driver);
+ misc_deregister(&usb_acm);
+}
+
+#endif
diff --git a/drivers/usb/cpia.c b/drivers/usb/cpia.c
new file mode 100644
index 000000000..87e1e4254
--- /dev/null
+++ b/drivers/usb/cpia.c
@@ -0,0 +1,1267 @@
+/*
+ * USB CPiA Video Camera driver
+ *
+ * Supports CPiA based Video Camera's. Many manufacturers use this chipset.
+ * There's a good chance, if you have a USB video camera, it's a CPiA based
+ * one
+ *
+ * (C) Copyright 1999 Johannes Erdfelt
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/videodev.h>
+#include <linux/vmalloc.h>
+#include <linux/wrapper.h>
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <asm/spinlock.h>
+#include <asm/io.h>
+
+#include "usb.h"
+#include "uhci.h"
+#include "cpia.h"
+
+#define MAX_FRAME_SIZE (384 * 288 * 3)
+
+/*******************************/
+/* Memory management functions */
+/*******************************/
+
+/* convert virtual user memory address to physical address */
+/* (virt_to_phys only works for kmalloced kernel memory) */
+
+static inline unsigned long uvirt_to_phys(unsigned long adr)
+{
+ pgd_t *pgd;
+ pmd_t *pmd;
+ pte_t *ptep, pte;
+
+ pgd = pgd_offset(current->mm, adr);
+ if (pgd_none(*pgd))
+ return 0;
+ pmd = pmd_offset(pgd, adr);
+ if (pmd_none(*pmd))
+ return 0;
+ ptep = pte_offset(pmd, adr/*&(~PGDIR_MASK)*/);
+ pte = *ptep;
+ if(pte_present(pte))
+ return
+ virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1))));
+ return 0;
+}
+
+static inline unsigned long uvirt_to_bus(unsigned long adr)
+{
+ return virt_to_bus(phys_to_virt(uvirt_to_phys(adr)));
+}
+
+/* convert virtual kernel memory address to physical address */
+/* (virt_to_phys only works for kmalloced kernel memory) */
+
+static inline unsigned long kvirt_to_phys(unsigned long adr)
+{
+ return uvirt_to_phys(VMALLOC_VMADDR(adr));
+}
+
+static inline unsigned long kvirt_to_bus(unsigned long adr)
+{
+ return uvirt_to_bus(VMALLOC_VMADDR(adr));
+}
+
+
+static void * rvmalloc(unsigned long size)
+{
+ void * mem;
+ unsigned long adr, page;
+
+ size += (PAGE_SIZE - 1);
+ size &= ~(PAGE_SIZE - 1);
+
+ mem=vmalloc(size);
+ if (mem)
+ {
+ memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+ adr=(unsigned long) mem;
+ while (size > 0)
+ {
+ page = kvirt_to_phys(adr);
+ mem_map_reserve(MAP_NR(phys_to_virt(page)));
+ adr+=PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size-=PAGE_SIZE;
+ else
+ size=0;
+ }
+ }
+ return mem;
+}
+
+static void rvfree(void * mem, unsigned long size)
+{
+ unsigned long adr, page;
+
+ size += (PAGE_SIZE - 1);
+ size &= ~(PAGE_SIZE - 1);
+
+ if (mem)
+ {
+ adr=(unsigned long) mem;
+ while (size > 0)
+ {
+ page = kvirt_to_phys(adr);
+ mem_map_unreserve(MAP_NR(phys_to_virt(page)));
+ adr+=PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size-=PAGE_SIZE;
+ else
+ size=0;
+ }
+ vfree(mem);
+ }
+}
+
+int usb_cpia_get_version(struct usb_device *dev, void *buf)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE) | 0x80;
+ dr.request = USB_REQ_CPIA_GET_VERSION;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 4;
+
+ return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, 4);
+}
+
+int usb_cpia_get_pnp_id(struct usb_device *dev, void *buf)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE) | 0x80;
+ dr.request = USB_REQ_CPIA_GET_PNP_ID;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 6;
+
+ return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, 6);
+}
+
+int usb_cpia_get_camera_status(struct usb_device *dev, void *buf)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE) | 0x80;
+ dr.request = USB_REQ_CPIA_GET_CAMERA_STATUS;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 8;
+
+ return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, 8);
+}
+
+int usb_cpia_goto_hi_power(struct usb_device *dev)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_GOTO_HI_POWER;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_get_vp_version(struct usb_device *dev, void *buf)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_GET_VP_VERSION;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 4;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, buf, 4);
+}
+
+int usb_cpia_set_sensor_fps(struct usb_device *dev, int sensorbaserate, int sensorclkdivisor)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_SET_SENSOR_FPS;
+ dr.value = (sensorclkdivisor << 8) + sensorbaserate;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_grab_frame(struct usb_device *dev, int streamstartline)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_GRAB_FRAME;
+ dr.value = streamstartline << 8;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_upload_frame(struct usb_device *dev, int forceupload)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_UPLOAD_FRAME;
+ dr.value = forceupload;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_set_grab_mode(struct usb_device *dev, int continuousgrab)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_SET_GRAB_MODE;
+ dr.value = continuousgrab;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_set_format(struct usb_device *dev, int size, int subsample, int order)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_SET_FORMAT;
+ dr.value = (subsample << 8) + size;
+ dr.index = order;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_set_compression(struct usb_device *dev, int compmode, int decimation)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_SET_COMPRESSION;
+ dr.value = (decimation << 8) + compmode;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_initstreamcap(struct usb_device *dev, int skipframes, int streamstartline)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_INIT_STREAM_CAP;
+ dr.value = (streamstartline << 8) + skipframes;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_finistreamcap(struct usb_device *dev)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_FINI_STREAM_CAP;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_startstreamcap(struct usb_device *dev)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_START_STREAM_CAP;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+int usb_cpia_endstreamcap(struct usb_device *dev)
+{
+ devrequest dr;
+
+ dr.requesttype = (USB_TYPE_VENDOR | USB_RECIP_DEVICE);
+ dr.request = USB_REQ_CPIA_END_STREAM_CAP;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 0;
+
+ return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+#define scratch_left(x) (cpia->scratchlen - (int)((char *)x - (char *)cpia->scratch))
+
+static void cpia_parse_data(struct usb_cpia *cpia)
+{
+ unsigned char *data = cpia->scratch;
+ int done;
+
+ done = 0;
+ while (!done && scratch_left(data)) {
+ switch (cpia->state) {
+ case STATE_SCANNING:
+ {
+ unsigned char *begin = data;
+
+ /* We need atleast 2 bytes for the magic value */
+ if (scratch_left(data) < 2) {
+ done = 1;
+ break;
+ }
+
+ printk("header: %X\n", (*data << 8) + *(data + 1));
+ if ((*data == 0x19) && (*(data + 1) == 0x68)) {
+ cpia->state = STATE_HEADER;
+ printk("moving to header\n");
+ break;
+ }
+
+ if (scratch_left(data) < 4) {
+ done = 1;
+ break;
+ }
+
+ printk("Scanning for end of frame\n");
+ while (scratch_left(data) >= 4) {
+ if ((*data == 0xFF) &&
+ (*(data + 1) == 0xFF) &&
+ (*(data + 2) == 0xFF) &&
+ (*(data + 3) == 0xFF)) {
+ data += 4;
+ break;
+ }
+ data++;
+ }
+printk("scan: scanned %d bytes\n", data-begin);
+ break;
+ }
+ case STATE_HEADER:
+ /* We need atleast 64 bytes for the header */
+ if (scratch_left(data) < 64) {
+ done = 1;
+ break;
+ }
+
+printk("header: framerate %d\n", data[41]);
+
+ data += 64;
+
+ cpia->state = STATE_LINES;
+
+ break;
+ case STATE_LINES:
+ {
+ unsigned char *begin = data;
+ int found = 0;
+
+ while (scratch_left(data)) {
+ if (*data == 0xFD) {
+ data++;
+ found = 1;
+ break;
+ } else if ((*data == 0xFF) &&
+ (scratch_left(data) >= 3) &&
+ (*(data + 1) == 0xFF) &&
+ (*(data + 2) == 0xFF) &&
+ (*(data + 3) == 0xFF)) {
+ data+=4;
+ cpia->curline = 144;
+ found = 1;
+ break;
+ }
+
+ data++;
+ }
+#if 0
+printk("line %d: scanned %d bytes\n", cpia->curline, data-begin);
+#endif
+if (data-begin == 355 && cpia->frame[cpia->curframe].width != 64) {
+ int i;
+ char *f = cpia->frame[cpia->curframe].data, *b = begin;
+
+#if 0
+printk("copying\n");
+#endif
+
+ b+=2;
+ f+=(cpia->frame[cpia->curframe].width*3)*cpia->curline;
+ for (i = 0; i < 176; i++)
+ f[(i * 3) + 0] =
+ f[(i * 3) + 1] =
+ f[(i * 3) + 2] =
+ b[(i * 2)];
+}
+ if (found) {
+ cpia->curline++;
+ if (cpia->curline >= 144) {
+ wake_up(&cpia->wq);
+ cpia->state = STATE_SCANNING;
+ cpia->curline = 0;
+ cpia->curframe = -1;
+ done = 1;
+ }
+ } else {
+ data = begin;
+ done = 1;
+ }
+
+ break;
+ }
+ }
+ }
+
+ {
+ int l;
+
+ l = scratch_left(data);
+ memmove(cpia->scratch, data, l);
+ cpia->scratchlen = l;
+ }
+}
+
+static int cpia_isoc_irq(int status, void *__buffer, void *dev_id)
+{
+ struct usb_cpia *cpia = dev_id;
+ struct usb_device *dev = cpia->dev;
+ struct cpia_sbuf *sbuf;
+ int i;
+ char *p;
+
+ if (!cpia->streaming) {
+ printk("oops, not streaming, but interrupt\n");
+ return 0;
+ }
+
+ if (cpia->curframe < 0) {
+ if (cpia->frame[0].state == FRAME_READY) {
+ cpia->curframe = 0;
+ cpia->frame[0].state = FRAME_GRABBING;
+printk("capturing to frame 0\n");
+ } else if (cpia->frame[1].state == FRAME_READY) {
+ cpia->curframe = 1;
+ cpia->frame[1].state = FRAME_GRABBING;
+printk("capturing to frame 1\n");
+ } else
+printk("no frame available\n");
+ }
+
+ sbuf = &cpia->sbuf[cpia->receivesbuf];
+
+ uhci_unsched_isochronous(dev, sbuf->isodesc);
+
+ /* Do something to it now */
+ sbuf->len = uhci_compress_isochronous(dev, sbuf->isodesc);
+
+ if (sbuf->len)
+ printk("%d bytes received\n", sbuf->len);
+
+ if (sbuf->len && cpia->curframe >= 0) {
+ if (sbuf->len > (SCRATCH_BUF_SIZE - cpia->scratchlen)) {
+ printk("overflow!\n");
+ return 0;
+ }
+ memcpy(cpia->scratch + cpia->scratchlen, sbuf->data, sbuf->len);
+ cpia->scratchlen += sbuf->len;
+
+ cpia_parse_data(cpia);
+ }
+
+ /* Reschedule this block of Isochronous desc */
+ uhci_sched_isochronous(dev, sbuf->isodesc, cpia->sbuf[(cpia->receivesbuf + 2) % 3].isodesc);
+
+ /* Move to the next one */
+ cpia->receivesbuf = (cpia->receivesbuf + 1) % 3;
+
+ return 1;
+}
+
+int cpia_init_isoc(struct usb_cpia *cpia)
+{
+ struct usb_device *dev = cpia->dev;
+
+ cpia->receivesbuf = 0;
+
+#if 0
+ cpia->parsesbuf = 0;
+ cpia->parsepos = 0;
+#endif
+ cpia->scratchlen = 0;
+ cpia->curline = 0;
+ cpia->state = STATE_SCANNING;
+
+ /* Allocate all of the memory necessary */
+ cpia->sbuf[0].isodesc = uhci_alloc_isochronous(dev, usb_rcvisocpipe(dev,1), cpia->sbuf[0].data, STREAM_BUF_SIZE, 960, cpia_isoc_irq, cpia);
+ cpia->sbuf[1].isodesc = uhci_alloc_isochronous(dev, usb_rcvisocpipe(dev,1), cpia->sbuf[1].data, STREAM_BUF_SIZE, 960, cpia_isoc_irq, cpia);
+ cpia->sbuf[2].isodesc = uhci_alloc_isochronous(dev, usb_rcvisocpipe(dev,1), cpia->sbuf[2].data, STREAM_BUF_SIZE, 960, cpia_isoc_irq, cpia);
+
+ printk("isodesc[0] @ %p\n", cpia->sbuf[0].isodesc);
+ printk("isodesc[1] @ %p\n", cpia->sbuf[1].isodesc);
+ printk("isodesc[2] @ %p\n", cpia->sbuf[2].isodesc);
+
+ /* Schedule the queues */
+ uhci_sched_isochronous(dev, cpia->sbuf[0].isodesc, NULL);
+ uhci_sched_isochronous(dev, cpia->sbuf[1].isodesc, cpia->sbuf[0].isodesc);
+ uhci_sched_isochronous(dev, cpia->sbuf[2].isodesc, cpia->sbuf[1].isodesc);
+
+ if (usb_set_interface(cpia->dev, 1, 3)) {
+ printk("cpia_set_interface error\n");
+ return -EINVAL;
+ }
+
+ usb_cpia_startstreamcap(cpia->dev);
+
+ cpia->streaming = 1;
+
+ return 0;
+}
+
+void cpia_stop_isoc(struct usb_cpia *cpia)
+{
+ struct usb_device *dev = cpia->dev;
+
+ if (!cpia->streaming)
+ return;
+
+ cpia->streaming = 0;
+
+ /* Stop the streaming */
+ usb_cpia_endstreamcap(cpia->dev);
+
+ /* Set packet size to 0 */
+ if (usb_set_interface(cpia->dev, 1, 0)) {
+ printk("cpia_set_interface error\n");
+ return -EINVAL;
+ }
+
+ /* Unschedule all of the iso td's */
+ uhci_unsched_isochronous(dev, cpia->sbuf[2].isodesc);
+ uhci_unsched_isochronous(dev, cpia->sbuf[1].isodesc);
+ uhci_unsched_isochronous(dev, cpia->sbuf[0].isodesc);
+
+ /* Delete them all */
+ uhci_delete_isochronous(dev, cpia->sbuf[2].isodesc);
+ uhci_delete_isochronous(dev, cpia->sbuf[1].isodesc);
+ uhci_delete_isochronous(dev, cpia->sbuf[0].isodesc);
+}
+
+/* Video 4 Linux API */
+static int cpia_open(struct video_device *dev, int flags)
+{
+ struct usb_cpia *cpia = (struct usb_cpia *)dev;
+
+printk("cpia_open\n");
+
+ cpia->fbuf = rvmalloc(2 * MAX_FRAME_SIZE);
+ if (!cpia->fbuf)
+ return -ENOMEM;
+
+ cpia->frame[0].state = FRAME_DONE;
+ cpia->frame[1].state = FRAME_DONE;
+
+ cpia->frame[0].data = cpia->fbuf;
+ cpia->frame[1].data = cpia->fbuf + MAX_FRAME_SIZE;
+ printk("frame [0] @ %p\n", cpia->frame[0].data);
+ printk("frame [1] @ %p\n", cpia->frame[1].data);
+
+ cpia->sbuf[0].data = kmalloc(STREAM_BUF_SIZE, GFP_KERNEL);
+ if (!cpia->sbuf[0].data)
+ return -ENOMEM;
+
+ cpia->sbuf[1].data = kmalloc(STREAM_BUF_SIZE, GFP_KERNEL);
+ if (!cpia->sbuf[1].data)
+ return -ENOMEM;
+
+ cpia->sbuf[2].data = kmalloc(STREAM_BUF_SIZE, GFP_KERNEL);
+ if (!cpia->sbuf[2].data)
+ return -ENOMEM;
+
+ printk("sbuf[0] @ %p\n", cpia->sbuf[0].data);
+ printk("sbuf[1] @ %p\n", cpia->sbuf[1].data);
+ printk("sbuf[2] @ %p\n", cpia->sbuf[2].data);
+
+ cpia->curframe = -1;
+ cpia->receivesbuf = 0;
+
+ usb_cpia_initstreamcap(cpia->dev, 0, 60);
+
+ cpia_init_isoc(cpia);
+
+ return 0;
+}
+
+static void cpia_close(struct video_device *dev)
+{
+ struct usb_cpia *cpia = (struct usb_cpia *)dev;
+
+printk("cpia_close\n");
+
+ cpia_stop_isoc(cpia);
+
+ usb_cpia_finistreamcap(cpia->dev);
+
+ rvfree(cpia->fbuf, 2 * MAX_FRAME_SIZE);
+
+ kfree(cpia->sbuf[2].data);
+ kfree(cpia->sbuf[1].data);
+ kfree(cpia->sbuf[0].data);
+}
+
+static int cpia_init_done(struct video_device *dev)
+{
+ return 0;
+}
+
+static long cpia_write(struct video_device *dev, const char *buf, unsigned long count, int noblock)
+{
+ return -EINVAL;
+}
+
+#if 0
+ if (usb_set_interface(dev, 1, 3)) {
+ printk("cpia_set_interface error\n");
+ return -EINVAL;
+ }
+
+ if (usb_cpia_grab_frame(dev, 0)) {
+ printk("cpia_grab_frame error\n");
+ return -EINVAL;
+ }
+
+ if (usb_cpia_upload_frame(dev, 0)) {
+ printk("cpia_upload_frame error\n");
+ return -EINVAL;
+ }
+
+ buf = cpia->ibuf;
+ uhci_receive_isochronous(dev, usb_rcvisocpipe(dev,1), buf, 176*144*4);
+
+ {
+ printk("header magic: %X\n", (buf[0] << 8) + buf[1]);
+
+ while ((buf[0] != 0x19) || (buf[1] != 0x68)) {
+ int i;
+
+ printk("resync'ing\n");
+ for (i=0;i<(176*144*4)-4;i++, buf++)
+ if (
+ (buf[0] == 0xFF) &&
+ (buf[1] == 0xFF) &&
+ (buf[2] == 0xFF) &&
+ (buf[3] == 0xFF)) {
+ buf+=4;
+ i+=4;
+ break;
+ }
+
+ memmove(cpia->ibuf, buf, (176*144*4) - i);
+ uhci_receive_isochronous(dev, usb_rcvisocpipe(dev,1), cpia->ibuf + (176*144*4) - i, i);
+ buf = cpia->ibuf;
+
+#if 0
+ printk("header magic: %X\n", (buf[0] << 8) + buf[1]);
+#endif
+ }
+
+ printk("size: %d, sample: %d, order: %d\n", buf[16], buf[17], buf[18]);
+ printk("comp: %d, decimation: %d\n", buf[28], buf[29]);
+ printk("roi: top left: %d, %d bottom right: %d, %d\n",
+ buf[26] * 4, buf[24] * 8,
+ buf[27] * 4, buf[25] * 8);
+
+ printk("vm->frame: %d\n", vm->frame);
+
+ {
+ int i, i1;
+ char *b = buf + 64, *fbuf = &cpia->fbuffer[MAX_FRAME_SIZE * (vm->frame & 1)];
+ for (i=0;i<144;i++) {
+#if 0
+ printk("line len: %d\n", (b[1] << 8) + b[0]);
+#endif
+ b += 2;
+ for (i1=0;i1<176;i1++) {
+ fbuf[(i * vm->width * 3) + (i1 * 3)] =
+ fbuf[(i * vm->width * 3) + (i1 * 3) + 1] =
+ fbuf[(i * vm->width * 3) + (i1 * 3) + 2] =
+ b[i1 * 2];
+#if 0
+ *((short *)&fbuf[(i * vm->width * 2) + (i1 * 2)]) =
+ ((b[i1 * 2] >> 3) << 11) + ((b[i1 * 2] >> 2) << 6) + (b[i1 * 2] >> 3);
+#endif
+ }
+ b += (176 * 2) + 1;
+ }
+ }
+
+ }
+
+ if (usb_set_interface(dev, 1, 0)) {
+ printk("cpia_set_interface error\n");
+ return -EINVAL;
+ }
+#endif
+
+static int cpia_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+ struct usb_cpia *cpia = (struct usb_cpia *)dev;
+
+ switch (cmd) {
+ case VIDIOCGCAP:
+ {
+ struct video_capability b;
+
+ strcpy(b.name, "CPiA USB Camera");
+ b.type = VID_TYPE_CAPTURE /* | VID_TYPE_SUBCAPTURE */;
+ b.channels = 1;
+ b.audios = 0;
+ b.maxwidth = 176 /* 352 */;
+ b.maxheight = 144 /* 240 */;
+ b.minwidth = 176 /* (Something small?) */;
+ b.minheight = 144 /* " " */;
+
+ if (copy_to_user(arg, &b, sizeof(b)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCGCHAN:
+ {
+ struct video_channel v;
+
+ if (copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ if (v.channel != 0)
+ return -EINVAL;
+
+ v.flags = 0;
+ v.tuners = 0;
+ v.type = VIDEO_TYPE_CAMERA;
+ strcpy(v.name, "Camera");
+
+ if (copy_to_user(arg, &v, sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSCHAN:
+ {
+ int v;
+
+ if (copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+
+ if (v != 0)
+ return -EINVAL;
+
+ return 0;
+ }
+ case VIDIOCGTUNER:
+ {
+ struct video_tuner v;
+
+ if (copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+
+ if (v.tuner)
+ return -EINVAL;
+
+ strcpy(v.name, "Format");
+
+ v.rangelow = 0;
+ v.rangehigh = 0;
+ v.flags = 0;
+ v.mode = VIDEO_MODE_AUTO;
+
+ if (copy_to_user(arg, &v, sizeof(v)))
+ return -EFAULT;
+
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner v;
+
+ if (copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+
+ if (v.tuner)
+ return -EINVAL;
+
+ if (v.mode != VIDEO_MODE_AUTO)
+ return -EINVAL;
+
+ return 0;
+ }
+ case VIDIOCGPICT:
+ {
+ struct video_picture p;
+
+ p.colour = 0x8000; /* Damn British people :) */
+ p.hue = 0x8000;
+ p.brightness = 180 << 8; /* XXX */
+ p.contrast = 192 << 8; /* XXX */
+ p.whiteness = 105 << 8; /* XXX */
+#if 0
+ p.depth = 24;
+#endif
+ p.depth = 16;
+ p.palette = VIDEO_PALETTE_YUYV;
+
+ if (copy_to_user(arg, &p, sizeof(p)))
+ return -EFAULT;
+
+ return 0;
+ }
+ case VIDIOCSPICT:
+ {
+ struct video_picture p;
+
+ if (copy_from_user(&p, arg, sizeof(p)))
+ return -EFAULT;
+
+printk("Attempting to set palette %d, depth %d\n", p.palette, p.depth);
+
+#if 0
+ if (p.palette != VIDEO_PALETTE_YUYV)
+ return -EINVAL;
+ if (p.depth != 16)
+ return -EINVAL;
+#endif
+
+ return 0;
+ }
+ case VIDIOCSWIN:
+ {
+ struct video_window vw;
+
+printk("VIDIOCSWIN\n");
+ if (copy_from_user(&vw, arg, sizeof(vw)))
+ return -EFAULT;
+ if (vw.flags)
+ return -EINVAL;
+ if (vw.clipcount)
+ return -EINVAL;
+ if (vw.height != 176)
+ return -EINVAL;
+ if (vw.width != 144)
+ return -EINVAL;
+
+ return 0;
+ }
+ case VIDIOCGWIN:
+ {
+ struct video_window vw;
+
+printk("VIDIOCGWIN\n");
+ vw.x = 0;
+ vw.y = 0;
+ vw.width = 176;
+ vw.height = 144;
+ vw.chromakey = 0;
+ vw.flags = 0;
+
+ if (copy_to_user(arg, &vw, sizeof(vw)))
+ return -EFAULT;
+
+ return 0;
+ }
+ case VIDIOCGMBUF:
+ {
+ struct video_mbuf vm;
+
+ memset(&vm, 0, sizeof(vm));
+ vm.size = MAX_FRAME_SIZE * 2;
+ vm.frames = 2;
+ vm.offsets[0] = 0;
+ vm.offsets[1] = MAX_FRAME_SIZE;
+
+ if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
+ return -EFAULT;
+
+ return 0;
+ }
+ case VIDIOCMCAPTURE:
+ {
+ struct video_mmap vm;
+
+ if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm)))
+ return -EFAULT;
+
+printk("MCAPTURE\n");
+printk("frame: %d, size: %dx%d, format: %d\n", vm.frame, vm.width, vm.height, vm.format);
+
+ if (vm.format != VIDEO_PALETTE_RGB24)
+ return -EINVAL;
+
+ if ((vm.frame != 0) && (vm.frame != 1))
+ return -EINVAL;
+
+ cpia->frame[vm.frame].width = vm.width;
+ cpia->frame[vm.frame].height = vm.height;
+
+ /* Mark it as free */
+ cpia->frame[vm.frame].state = FRAME_READY;
+
+ return 0;
+ }
+ case VIDIOCSYNC:
+ {
+ int frame;
+
+ if (copy_from_user((void *)&frame, arg, sizeof(int)))
+ return -EFAULT;
+
+ printk("syncing to frame %d\n", frame);
+ switch (cpia->frame[frame].state) {
+ case FRAME_UNUSED:
+ return -EINVAL;
+ case FRAME_READY:
+ case FRAME_GRABBING:
+ interruptible_sleep_on(&cpia->wq);
+ case FRAME_DONE:
+ cpia->frame[frame].state = FRAME_UNUSED;
+ break;
+ }
+ printk("synced to frame %d\n", frame);
+ return 0;
+ }
+ case VIDIOCCAPTURE:
+ return -EINVAL;
+ case VIDIOCGFBUF:
+ return -EINVAL;
+ case VIDIOCSFBUF:
+ return -EINVAL;
+ case VIDIOCKEY:
+ return 0;
+ case VIDIOCGFREQ:
+ return -EINVAL;
+ case VIDIOCSFREQ:
+ return -EINVAL;
+ case VIDIOCGAUDIO:
+ return -EINVAL;
+ case VIDIOCSAUDIO:
+ return -EINVAL;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static long cpia_read(struct video_device *dev, char *buf, unsigned long count, int noblock)
+{
+ struct usb_cpia *cpia = (struct usb_cpia *)dev;
+ int len;
+
+ printk("cpia_read: %d bytes\n", count);
+#if 0
+ len = cpia_capture(cpia, buf, count);
+
+ return len;
+#endif
+ return 0;
+}
+
+static int cpia_mmap(struct video_device *dev, const char *adr, unsigned long size)
+{
+ struct usb_cpia *cpia = (struct usb_cpia *)dev;
+ unsigned long start = (unsigned long)adr;
+ unsigned long page, pos;
+
+ printk("mmap: %d (%X) bytes\n", size, size);
+ if (size > (((2 * MAX_FRAME_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
+ return -EINVAL;
+
+#if 0
+ if (!cpia->fbuffer) {
+ if ((cpia->fbuffer = rvmalloc(2 * MAX_FRAME_SIZE)) == NULL)
+ return -EINVAL;
+ }
+#endif
+
+ pos = (unsigned long)cpia->fbuf;
+ while (size > 0)
+ {
+ page = kvirt_to_phys(pos);
+ if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED))
+ return -EAGAIN;
+ start+=PAGE_SIZE;
+ pos+=PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size-=PAGE_SIZE;
+ else
+ size=0;
+ }
+
+ return 0;
+}
+
+static struct video_device cpia_template = {
+ "CPiA USB Camera",
+ VID_TYPE_CAPTURE,
+ VID_HARDWARE_CPIA,
+ cpia_open,
+ cpia_close,
+ cpia_read,
+ cpia_write,
+ NULL,
+ cpia_ioctl,
+ cpia_mmap,
+ cpia_init_done,
+ NULL,
+ 0,
+ 0
+};
+
+static void usb_cpia_configure(struct usb_cpia *cpia)
+{
+ struct usb_device *dev = cpia->dev;
+ unsigned char version[4];
+ unsigned char pnpid[6];
+ unsigned char camerastat[8];
+ unsigned char *buf;
+
+ usb_set_configuration(dev, dev->config[0].bConfigurationValue);
+
+ if (usb_cpia_get_version(dev, version)) {
+ printk("cpia_get_version error\n");
+ return;
+ }
+
+ printk("cpia: Firmware v%d.%d, VC Hardware v%d.%d\n",
+ version[0], version[1], version[2], version[3]);
+
+ if (usb_cpia_get_pnp_id(dev, pnpid)) {
+ printk("cpia_get_pnp_id error\n");
+ return;
+ }
+
+ printk("cpia: PnP Id: Vendor: %X, Product: %X, Revision: %X\n",
+ (pnpid[1] << 8) + pnpid[0], (pnpid[3] << 8) + pnpid[2],
+ (pnpid[5] << 8) + pnpid[4]);
+
+ memcpy(&cpia->vdev, &cpia_template, sizeof(cpia_template));
+
+ init_waitqueue_head(&cpia->wq);
+
+ if (video_register_device(&cpia->vdev, VFL_TYPE_GRABBER) == -1) {
+ printk("video_register_device failed\n");
+ return;
+ }
+
+ if (usb_cpia_goto_hi_power(dev)) {
+ printk("cpia_goto_hi_power error\n");
+ return;
+ }
+
+ if (usb_cpia_get_vp_version(dev, version)) {
+ printk("cpia_get_vp_version error\n");
+ return;
+ }
+
+ printk("cpia: VP v%d rev %d\n", version[0], version[1]);
+ printk("cpia: Camera Head ID %04X\n", (version[3] << 8) + version[2]);
+
+ /* Turn off continuous grab */
+ if (usb_cpia_set_grab_mode(dev, 1)) {
+ printk("cpia_set_grab_mode error\n");
+ return;
+ }
+
+ /* Set up the sensor to be 30fps */
+ if (usb_cpia_set_sensor_fps(dev, 1, 0)) {
+ printk("cpia_set_sensor_fps error\n");
+ return;
+ }
+
+ /* Set video into QCIF mode, and order into YUYV mode */
+ if (usb_cpia_set_format(dev, CPIA_QCIF, 1, CPIA_YUYV)) {
+ printk("cpia_set_format error\n");
+ return;
+ }
+
+ /* Turn off compression */
+ if (usb_cpia_set_compression(dev, 0, 0)) {
+ printk("cpia_set_compression error\n");
+ return;
+ }
+
+#if 0
+ if (usb_cpia_grab_frame(dev, 0)) {
+ printk("cpia_grab_frame error\n");
+ return;
+ }
+
+ if (usb_cpia_upload_frame(dev, 1)) {
+ printk("cpia_upload_frame error\n");
+ return;
+ }
+
+ buf = (void *)__get_free_page(GFP_KERNEL);
+
+ {
+ int i;
+ for (i=0;i<448;i++)
+ buf[i]=0;
+ }
+ uhci_receive_isochronous(dev, usb_rcvisocpipe(dev,1), buf, 448);
+
+ {
+ int i;
+ for (i=0;i<448;i++) {
+ printk("%02X ", buf[i]);
+ if ((i % 16) == 15)
+ printk("\n");
+ }
+ printk("\n");
+ }
+
+ free_page((unsigned long)buf);
+#endif
+}
+
+static int cpia_probe(struct usb_device *dev)
+{
+ struct usb_interface_descriptor *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_cpia *cpia;
+
+ /* We don't handle multi-config cameras */
+ if (dev->descriptor.bNumConfigurations != 1)
+ return -1;
+
+#if 0
+ /* We don't handle multi-interface hubs */
+ if (dev->config[0].bNumInterfaces != 1)
+ return -1;
+#endif
+
+ interface = &dev->config[0].altsetting[0].interface[0];
+
+ /* Is it a CPiA? */
+/*
+Apr 24 17:49:04 bjorn kernel: Vendor: 0545
+Apr 24 17:49:04 bjorn kernel: Product: 8080
+*/
+/*
+ if (dev->descriptor.idVendor != 0x0545)
+ return -1;
+ if (dev->descriptor.idProduct != 0x8080)
+ return -1;
+ if (interface->bInterfaceClass != 0xFF)
+ return -1;
+ if (interface->bInterfaceSubClass != 0xFF)
+ return -1;
+*/
+ if (dev->descriptor.idVendor != 0x0553)
+ return -1;
+ if (dev->descriptor.idProduct != 0x0002)
+ return -1;
+ if (interface->bInterfaceClass != 0xFF)
+ return -1;
+ if (interface->bInterfaceSubClass != 0x00)
+ return -1;
+
+#if 0
+ /* Multiple endpoints? What kind of mutant ninja-hub is this? */
+ if (interface->bNumEndpoints != 1)
+ return -1;
+
+ endpoint = &interface->endpoint[0];
+
+ /* Output endpoint? Curiousier and curiousier.. */
+ if (!(endpoint->bEndpointAddress & 0x80))
+ return -1;
+
+ /* If it's not an interrupt endpoint, we'd better punt! */
+ if ((endpoint->bmAttributes & 3) != 3)
+ return -1;
+#endif
+
+ /* We found a CPiA */
+ printk("USB CPiA camera found\n");
+
+ if ((cpia = kmalloc(sizeof(*cpia), GFP_KERNEL)) == NULL) {
+ printk("couldn't kmalloc cpia struct\n");
+ return -1;
+ }
+
+ memset(cpia, 0, sizeof(*cpia));
+
+ dev->private = cpia;
+ cpia->dev = dev;
+
+ usb_cpia_configure(cpia);
+
+#if 0
+ usb_request_irq(dev, usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), pport_irq, endpoint->bInterval, pport);
+#endif
+
+ return 0;
+}
+
+static void cpia_disconnect(struct usb_device *dev)
+{
+ struct usb_cpia *cpia = dev->private;
+
+ video_unregister_device(&cpia->vdev);
+
+ /* Free the memory */
+ kfree(cpia);
+}
+
+static struct usb_driver cpia_driver = {
+ "cpia",
+ cpia_probe,
+ cpia_disconnect,
+ { NULL, NULL }
+};
+
+/*
+ * This should be a separate module.
+ */
+int usb_cpia_init(void)
+{
+ usb_register(&cpia_driver);
+
+ return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ return usb_cpia_init();
+}
+void cleanup_module(void)
+{
+}
+#endif
+
diff --git a/drivers/usb/cpia.h b/drivers/usb/cpia.h
new file mode 100644
index 000000000..51ff43e79
--- /dev/null
+++ b/drivers/usb/cpia.h
@@ -0,0 +1,139 @@
+#ifndef __LINUX_CPIA_H
+#define __LINUX_CPIA_H
+
+#include <linux/list.h>
+
+#define USB_REQ_CPIA_GET_VERSION 0x01
+#define USB_REQ_CPIA_GET_PNP_ID 0x02
+#define USB_REQ_CPIA_GET_CAMERA_STATUS 0x03
+#define USB_REQ_CPIA_GOTO_HI_POWER 0x04
+#define USB_REQ_CPIA_GOTO_LO_POWER 0x05
+/* No 0x06 */
+#define USB_REQ_CPIA_GOTO_SUSPEND 0x07
+#define USB_REQ_CPIA_GOTO_PASS_THROUGH 0x08
+/* No 0x09 */
+#define USB_REQ_CPIA_MODIFY_CAMERA_STATUS 0x0A
+
+#define USB_REQ_CPIA_READ_VC_REGS 0x21
+#define USB_REQ_CPIA_WRITE_BC_REG 0x22
+#define USB_REQ_CPIA_READ_MC_PORTS 0x23
+#define USB_REQ_CPIA_WRITE_MC_PORT 0x24
+#define USB_REQ_CPIA_SET_BAUD_RATE 0x25
+#define USB_REQ_CPIA_SET_ECP_TIMING 0x26
+#define USB_REQ_CPIA_READ_IDATA 0x27
+#define USB_REQ_CPIA_WRITE_IDATA 0x28
+#define USB_REQ_CPIA_GENERIC_CALL 0x29
+#define USB_REQ_CPIA_I2CSTART 0x2A
+#define USB_REQ_CPIA_I2CSTOP 0x2B
+#define USB_REQ_CPIA_I2CWRITE 0x2C
+#define USB_REQ_CPIA_I2CREAD 0x2D
+
+#define USB_REQ_CPIA_GET_VP_VERSION 0xA1
+#define USB_REQ_CPIA_SET_COLOUR_PARAMS 0xA3
+#define USB_REQ_CPIA_SET_EXPOSURE 0xA4
+/* No 0xA5 */
+#define USB_REQ_CPIA_SET_COLOUR_BALANCE 0xA6
+#define USB_REQ_CPIA_SET_SENSOR_FPS 0xA7
+#define USB_REQ_CPIA_SET_VP_DEFAULTS 0xA8
+#define USB_REQ_CPIA_SET_APCOR 0xA9
+#define USB_REQ_CPIA_SET_FLICKER_CTRL 0xAA
+#define USB_REQ_CPIA_SET_VL_OFFSET 0xAB
+
+#define USB_REQ_CPIA_GET_COLOUR_PARAMETERS 0xB0
+#define USB_REQ_CPIA_GET_COLOUR_BALANCE 0xB1
+#define USB_REQ_CPIA_GET_EXPOSURE 0xB2
+#define USB_REQ_CPIA_SET_SENSOR_MATRIX 0xB3
+
+#define USB_REQ_CPIA_COLOUR_BARS 0xBD
+#define USB_REQ_CPIA_READ_VP_REGS 0xBE
+#define USB_REQ_CPIA_WRITE_VP_REGS 0xBF
+
+#define USB_REQ_CPIA_GRAB_FRAME 0xC1
+#define USB_REQ_CPIA_UPLOAD_FRAME 0xC2
+#define USB_REQ_CPIA_SET_GRAB_MODE 0xC3
+#define USB_REQ_CPIA_INIT_STREAM_CAP 0xC4
+#define USB_REQ_CPIA_FINI_STREAM_CAP 0xC5
+#define USB_REQ_CPIA_START_STREAM_CAP 0xC6
+#define USB_REQ_CPIA_END_STREAM_CAP 0xC7
+#define USB_REQ_CPIA_SET_FORMAT 0xC8
+#define USB_REQ_CPIA_SET_ROI 0xC9
+#define USB_REQ_CPIA_SET_COMPRESSION 0xCA
+#define USB_REQ_CPIA_SET_COMPRESSION_TARGET 0xCB
+#define USB_REQ_CPIA_SET_YUV_THRESH 0xCC
+#define USB_REQ_CPIA_SET_COMPRESSION_PARAMS 0xCD
+#define USB_REQ_CPIA_DISCARD_FRAME 0xCE
+
+#define USB_REQ_CPIA_OUTPUT_RS232 0xE1
+#define USB_REQ_CPIA_ABORT_PROCESS 0xE4
+#define USB_REQ_CPIA_SET_DRAM_PAGE 0xE5
+#define USB_REQ_CPIA_START_DRAM_UPLOAD 0xE6
+#define USB_REQ_CPIA_START_DUMMY_STREAM 0xE8
+#define USB_REQ_CPIA_ABORT_STREAM 0xE9
+#define USB_REQ_CPIA_DOWNLOAD_DRAM 0xEA
+/* #define USB_REQ_CPIA_NULL_CMD 0x?? */
+
+#define CPIA_QCIF 0
+#define CPIA_CIF 1
+
+#define CPIA_YUYV 0
+#define CPIA_UYVY 1
+
+#define STREAM_BUF_SIZE (PAGE_SIZE * 4)
+
+#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2)
+
+enum {
+ STATE_SCANNING, /* Scanning for start */
+ STATE_HEADER, /* Parsing header */
+ STATE_LINES, /* Parsing lines */
+};
+
+struct usb_device;
+
+struct cpia_sbuf {
+ char *data;
+ int len;
+ void *isodesc;
+};
+
+enum {
+ FRAME_READY, /* Ready to grab into */
+ FRAME_GRABBING, /* In the process of being grabbed into */
+ FRAME_DONE, /* Finished grabbing, but not been synced yet */
+ FRAME_UNUSED, /* Unused (no MCAPTURE) */
+};
+
+struct cpia_frame {
+ char *data;
+ int width;
+ int height;
+ int state;
+};
+
+struct usb_cpia {
+ struct video_device vdev;
+
+ /* Device structure */
+ struct usb_device *dev;
+
+ int streaming;
+
+ char *fbuf; /* Videodev buffer area */
+
+ int curframe;
+ struct cpia_frame frame[2]; /* Double buffering */
+
+ int receivesbuf; /* Current receiving sbuf */
+ struct cpia_sbuf sbuf[3]; /* Triple buffering */
+
+ int state; /* Current scanning state */
+ int curline;
+
+ char scratch[SCRATCH_BUF_SIZE];
+ int scratchlen;
+
+ wait_queue_head_t wq;
+};
+
+#endif
+
diff --git a/drivers/usb/keymap-mac.c b/drivers/usb/keymap-mac.c
new file mode 100644
index 000000000..48fc25e9b
--- /dev/null
+++ b/drivers/usb/keymap-mac.c
@@ -0,0 +1,50 @@
+unsigned char usb_kbd_map[256] =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x0b, 0x08, 0x02,
+ 0x0e, 0x03, 0x05, 0x04, 0x22, 0x26, 0x28, 0x25,
+
+ 0x2e, 0x2d, 0x1f, 0x23, 0x0c, 0x0f, 0x01, 0x11,
+ 0x20, 0x09, 0x0d, 0x07, 0x10, 0x06, 0x12, 0x13,
+
+ 0x14, 0x15, 0x17, 0x16, 0x1a, 0x1c, 0x19, 0x1d,
+ 0x24, 0x35, 0x33, 0x30, 0x31, 0x1b, 0x18, 0x21,
+
+ 0x1e, 0x2a, 0x00, 0x29, 0x27, 0x32, 0x2b, 0x2f,
+ 0x2c, 0x39, 0x7a, 0x78, 0x63, 0x76, 0x60, 0x61,
+
+ 0x62, 0x64, 0x65, 0x6d, 0x67, 0x6f, 0x69, 0x6b,
+ 0x71, 0x72, 0x73, 0x74, 0x75, 0x77, 0x79, 0x3c,
+
+ 0x3b, 0x3d, 0x3e, 0x47, 0x4b, 0x43, 0x4e, 0x45,
+ 0x4c, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+
+ 0x5b, 0x5c, 0x52, 0x41, 0x00, 0x00, 0x00, 0x00,
+ 0x69, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x36, 0x38, 0x3a, 0x37, 0x7d, 0x7b, 0x7c, 0x37,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
diff --git a/drivers/usb/maps/mac.map b/drivers/usb/maps/mac.map
new file mode 100644
index 000000000..dd601f76d
--- /dev/null
+++ b/drivers/usb/maps/mac.map
@@ -0,0 +1,350 @@
+# Kernel keymap for Macintoshes. This uses 7 modifier combinations.
+keymaps 0-2,4-5,8,12
+#
+# Fixups:
+keycode 0x69 = Print_Screen
+keycode 0x6b = F14
+keycode 0x37 = Window_R
+#
+#keycode 0x00 = a
+# hack!
+keycode 0x80 = a
+ altgr keycode 0x00 = Hex_A
+keycode 0x01 = s
+keycode 0x02 = d
+ altgr keycode 0x02 = Hex_D
+keycode 0x03 = f
+ altgr keycode 0x03 = Hex_F
+keycode 0x04 = h
+keycode 0x05 = g
+keycode 0x06 = z
+keycode 0x07 = x
+keycode 0x08 = c
+ altgr keycode 0x08 = Hex_C
+keycode 0x09 = v
+keycode 0x0a =
+keycode 0x0b = b
+ altgr keycode 0x0b = Hex_B
+keycode 0x0c = q
+keycode 0x0d = w
+keycode 0x0e = e
+ altgr keycode 0x0e = Hex_E
+keycode 0x0f = r
+keycode 0x10 = y
+keycode 0x11 = t
+keycode 0x12 = one exclam
+ alt keycode 0x12 = Meta_one
+keycode 0x13 = two at at
+ control keycode 0x13 = nul
+ shift control keycode 0x13 = nul
+ alt keycode 0x13 = Meta_two
+keycode 0x14 = three numbersign
+ control keycode 0x14 = Escape
+ alt keycode 0x14 = Meta_three
+keycode 0x15 = four dollar dollar
+ control keycode 0x15 = Control_backslash
+ alt keycode 0x15 = Meta_four
+keycode 0x16 = six asciicircum
+ control keycode 0x16 = Control_asciicircum
+ alt keycode 0x16 = Meta_six
+keycode 0x17 = five percent
+ control keycode 0x17 = Control_bracketright
+ alt keycode 0x17 = Meta_five
+keycode 0x18 = equal plus
+ alt keycode 0x18 = Meta_equal
+keycode 0x19 = nine parenleft bracketright
+ alt keycode 0x19 = Meta_nine
+keycode 0x1a = seven ampersand braceleft
+ control keycode 0x1a = Control_underscore
+ alt keycode 0x1a = Meta_seven
+keycode 0x1b = minus underscore backslash
+ control keycode 0x1b = Control_underscore
+ shift control keycode 0x1b = Control_underscore
+ alt keycode 0x1b = Meta_minus
+keycode 0x1c = eight asterisk bracketleft
+ control keycode 0x1c = Delete
+ alt keycode 0x1c = Meta_eight
+keycode 0x1d = zero parenright braceright
+ alt keycode 0x1d = Meta_zero
+keycode 0x1e = bracketright braceright asciitilde
+ control keycode 0x1e = Control_bracketright
+ alt keycode 0x1e = Meta_bracketright
+keycode 0x1f = o
+keycode 0x20 = u
+keycode 0x21 = bracketleft braceleft
+ control keycode 0x21 = Escape
+ alt keycode 0x21 = Meta_bracketleft
+keycode 0x22 = i
+keycode 0x23 = p
+keycode 0x24 = Return
+ alt keycode 0x24 = Meta_Control_m
+keycode 0x25 = l
+keycode 0x26 = j
+keycode 0x27 = apostrophe quotedbl
+ control keycode 0x27 = Control_g
+ alt keycode 0x27 = Meta_apostrophe
+keycode 0x28 = k
+keycode 0x29 = semicolon colon
+ alt keycode 0x29 = Meta_semicolon
+keycode 0x2a = backslash bar
+ control keycode 0x2a = Control_backslash
+ alt keycode 0x2a = Meta_backslash
+keycode 0x2b = comma less
+ alt keycode 0x2b = Meta_comma
+keycode 0x2c = slash question
+ control keycode 0x2c = Delete
+ alt keycode 0x2c = Meta_slash
+keycode 0x2d = n
+keycode 0x2e = m
+keycode 0x2f = period greater
+ control keycode 0x2f = Compose
+ alt keycode 0x2f = Meta_period
+keycode 0x30 = Tab Tab
+ alt keycode 0x30 = Meta_Tab
+keycode 0x31 = space space
+ control keycode 0x31 = nul
+ alt keycode 0x31 = Meta_space
+keycode 0x32 = grave asciitilde
+ control keycode 0x32 = nul
+ alt keycode 0x32 = Meta_grave
+keycode 0x33 = Delete Delete
+ control keycode 0x33 = BackSpace
+ alt keycode 0x33 = Meta_Delete
+keycode 0x34 =
+keycode 0x35 = Escape Escape
+ alt keycode 0x35 = Meta_Escape
+keycode 0x36 = Control
+keycode 0x37 = Window
+keycode 0x38 = Shift
+keycode 0x39 = Caps_Lock
+keycode 0x3a = Alt
+keycode 0x3b = Left
+ alt keycode 0x3b = Decr_Console
+keycode 0x3c = Right
+ alt keycode 0x3c = Incr_Console
+keycode 0x3d = Down
+keycode 0x3e = Up
+keycode 0x3f =
+keycode 0x40 =
+keycode 0x41 = KP_Period
+keycode 0x42 =
+keycode 0x43 = KP_Multiply
+keycode 0x44 =
+keycode 0x45 = KP_Add
+keycode 0x46 =
+keycode 0x47 = Num_Lock
+# shift keycode 0x47 = Bare_Num_Lock
+keycode 0x48 =
+keycode 0x49 =
+keycode 0x4a =
+keycode 0x4b = KP_Divide
+keycode 0x4c = KP_Enter
+keycode 0x4d =
+keycode 0x4e = KP_Subtract
+keycode 0x4f =
+keycode 0x50 =
+keycode 0x51 =
+#keycode 0x51 = KP_Equals
+keycode 0x52 = KP_0
+ alt keycode 0x52 = Ascii_0
+ altgr keycode 0x52 = Hex_0
+keycode 0x53 = KP_1
+ alt keycode 0x53 = Ascii_1
+ altgr keycode 0x53 = Hex_1
+keycode 0x54 = KP_2
+ alt keycode 0x54 = Ascii_2
+ altgr keycode 0x54 = Hex_2
+keycode 0x55 = KP_3
+ alt keycode 0x55 = Ascii_3
+ altgr keycode 0x55 = Hex_3
+keycode 0x56 = KP_4
+ alt keycode 0x56 = Ascii_4
+ altgr keycode 0x56 = Hex_4
+keycode 0x57 = KP_5
+ alt keycode 0x57 = Ascii_5
+ altgr keycode 0x57 = Hex_5
+keycode 0x58 = KP_6
+ alt keycode 0x58 = Ascii_6
+ altgr keycode 0x58 = Hex_6
+keycode 0x59 = KP_7
+ alt keycode 0x59 = Ascii_7
+ altgr keycode 0x59 = Hex_7
+keycode 0x5b = KP_8
+ alt keycode 0x5b = Ascii_8
+ altgr keycode 0x5b = Hex_8
+keycode 0x5c = KP_9
+ alt keycode 0x5c = Ascii_9
+ altgr keycode 0x5c = Hex_9
+keycode 0x5d =
+keycode 0x5e =
+keycode 0x5f =
+keycode 0x60 = F5 F15 Console_17
+ control keycode 0x60 = F5
+ alt keycode 0x60 = Console_5
+ control alt keycode 0x60 = Console_5
+keycode 0x61 = F6 F16 Console_18
+ control keycode 0x61 = F6
+ alt keycode 0x61 = Console_6
+ control alt keycode 0x61 = Console_6
+keycode 0x62 = F7 F17 Console_19
+ control keycode 0x62 = F7
+ alt keycode 0x62 = Console_7
+ control alt keycode 0x62 = Console_7
+keycode 0x63 = F3 F13 Console_15
+ control keycode 0x63 = F3
+ alt keycode 0x63 = Console_3
+ control alt keycode 0x63 = Console_3
+keycode 0x64 = F8 F18 Console_20
+ control keycode 0x64 = F8
+ alt keycode 0x64 = Console_8
+ control alt keycode 0x64 = Console_8
+keycode 0x65 = F9 F19 Console_21
+ control keycode 0x65 = F9
+ alt keycode 0x65 = Console_9
+ control alt keycode 0x65 = Console_9
+keycode 0x66 =
+keycode 0x67 = F11 F11 Console_23
+ control keycode 0x67 = F11
+ alt keycode 0x67 = Console_11
+ control alt keycode 0x67 = Console_11
+keycode 0x68 =
+keycode 0x69 = F13
+keycode 0x6a =
+keycode 0x6b = Scroll_Lock Show_Memory Show_Registers
+ control keycode 0x6b = Show_State
+ alt keycode 0x6b = Scroll_Lock
+keycode 0x6c =
+keycode 0x6d = F10 F20 Console_22
+ control keycode 0x6d = F10
+ alt keycode 0x6d = Console_10
+ control alt keycode 0x6d = Console_10
+keycode 0x6e =
+keycode 0x6f = F12 F12 Console_24
+ control keycode 0x6f = F12
+ alt keycode 0x6f = Console_12
+ control alt keycode 0x6f = Console_12
+keycode 0x70 =
+keycode 0x71 = Pause
+keycode 0x72 = Insert
+keycode 0x73 = Home
+keycode 0x74 = Prior
+ shift keycode 0x74 = Scroll_Backward
+keycode 0x75 = Remove
+keycode 0x76 = F4 F14 Console_16
+ control keycode 0x76 = F4
+ alt keycode 0x76 = Console_4
+ control alt keycode 0x76 = Console_4
+keycode 0x77 = End
+keycode 0x78 = F2 F12 Console_14
+ control keycode 0x78 = F2
+ alt keycode 0x78 = Console_2
+ control alt keycode 0x78 = Console_2
+keycode 0x79 = Next
+ shift keycode 0x79 = Scroll_Forward
+keycode 0x7a = F1 F11 Console_13
+ control keycode 0x7a = F1
+ alt keycode 0x7a = Console_1
+ control alt keycode 0x7a = Console_1
+keycode 0x7b = Shift_R
+keycode 0x7c = Alt_R
+keycode 0x7d = Control_R
+keycode 0x7e =
+keycode 0x7f =
+#keycode 0x7f = Power
+ control shift keycode 0x7f = Boot
+string F1 = "\033[[A"
+string F2 = "\033[[B"
+string F3 = "\033[[C"
+string F4 = "\033[[D"
+string F5 = "\033[[E"
+string F6 = "\033[17~"
+string F7 = "\033[18~"
+string F8 = "\033[19~"
+string F9 = "\033[20~"
+string F10 = "\033[21~"
+string F11 = "\033[23~"
+string F12 = "\033[24~"
+string F13 = "\033[25~"
+string F14 = "\033[26~"
+string F15 = "\033[28~"
+string F16 = "\033[29~"
+string F17 = "\033[31~"
+string F18 = "\033[32~"
+string F19 = "\033[33~"
+string F20 = "\033[34~"
+string Find = "\033[1~"
+string Insert = "\033[2~"
+string Remove = "\033[3~"
+string Select = "\033[4~"
+string Prior = "\033[5~"
+string Next = "\033[6~"
+string Macro = "\033[M"
+string Pause = "\033[P"
+compose '`' 'A' to 'À'
+compose '`' 'a' to 'à'
+compose '\'' 'A' to 'Á'
+compose '\'' 'a' to 'á'
+compose '^' 'A' to 'Â'
+compose '^' 'a' to 'â'
+compose '~' 'A' to 'Ã'
+compose '~' 'a' to 'ã'
+compose '"' 'A' to 'Ä'
+compose '"' 'a' to 'ä'
+compose 'O' 'A' to 'Å'
+compose 'o' 'a' to 'å'
+compose '0' 'A' to 'Å'
+compose '0' 'a' to 'å'
+compose 'A' 'A' to 'Å'
+compose 'a' 'a' to 'å'
+compose 'A' 'E' to 'Æ'
+compose 'a' 'e' to 'æ'
+compose ',' 'C' to 'Ç'
+compose ',' 'c' to 'ç'
+compose '`' 'E' to 'È'
+compose '`' 'e' to 'è'
+compose '\'' 'E' to 'É'
+compose '\'' 'e' to 'é'
+compose '^' 'E' to 'Ê'
+compose '^' 'e' to 'ê'
+compose '"' 'E' to 'Ë'
+compose '"' 'e' to 'ë'
+compose '`' 'I' to 'Ì'
+compose '`' 'i' to 'ì'
+compose '\'' 'I' to 'Í'
+compose '\'' 'i' to 'í'
+compose '^' 'I' to 'Î'
+compose '^' 'i' to 'î'
+compose '"' 'I' to 'Ï'
+compose '"' 'i' to 'ï'
+compose '-' 'D' to 'Ð'
+compose '-' 'd' to 'ð'
+compose '~' 'N' to 'Ñ'
+compose '~' 'n' to 'ñ'
+compose '`' 'O' to 'Ò'
+compose '`' 'o' to 'ò'
+compose '\'' 'O' to 'Ó'
+compose '\'' 'o' to 'ó'
+compose '^' 'O' to 'Ô'
+compose '^' 'o' to 'ô'
+compose '~' 'O' to 'Õ'
+compose '~' 'o' to 'õ'
+compose '"' 'O' to 'Ö'
+compose '"' 'o' to 'ö'
+compose '/' 'O' to 'Ø'
+compose '/' 'o' to 'ø'
+compose '`' 'U' to 'Ù'
+compose '`' 'u' to 'ù'
+compose '\'' 'U' to 'Ú'
+compose '\'' 'u' to 'ú'
+compose '^' 'U' to 'Û'
+compose '^' 'u' to 'û'
+compose '"' 'U' to 'Ü'
+compose '"' 'u' to 'ü'
+compose '\'' 'Y' to 'Ý'
+compose '\'' 'y' to 'ý'
+compose 'T' 'H' to 'Þ'
+compose 't' 'h' to 'þ'
+compose 's' 's' to 'ß'
+compose '"' 'y' to 'ÿ'
+compose 's' 'z' to 'ß'
+compose 'i' 'j' to 'ÿ'
diff --git a/drivers/usb/mkmap.adb b/drivers/usb/mkmap.adb
new file mode 100644
index 000000000..45020d5cc
--- /dev/null
+++ b/drivers/usb/mkmap.adb
@@ -0,0 +1,88 @@
+#!/usr/bin/perl
+
+($ME = $0) =~ s|.*/||;
+
+$file = "maps/mac.map";
+$line = 1;
+open(PC, $file) || die("$!");
+while(<PC>)
+{
+ if(/^\s*keycode\s+(\d+|0x[0-9a-fA-F]+)\s*=\s*(\S+)/)
+ {
+ my($idx) = $1;
+ my($sym) = $2;
+ if ($idx =~ "0x.*") {
+ $idx = hex($idx);
+ } else {
+ $idx = int($idx);
+ }
+ if(defined($map{uc($sym)}))
+ {
+ # print STDERR "$file:$line: warning: `$sym' redefined\n";
+ }
+ $map{uc($sym)} = $idx;
+ }
+ $line++;
+}
+close(PC);
+
+# $file = "maps/fixup.map";
+# $line = 1;
+# open(FIXUP, $file) || die("$!");
+# while(<FIXUP>)
+# {
+# if(/^\s*keycode\s+(\d+)\s*=\s*/)
+# {
+# my($idx) = int($1);
+# for $sym (split(/\s+/, $'))
+# {
+# $map{uc($sym)} = $idx;
+# }
+# }
+# $line++;
+# }
+# close(FIXUP);
+
+$file = "maps/usb.map";
+$line = 1;
+open(USB, $file) || die("$!");
+while(<USB>)
+{
+ if(/^\s*keycode\s+(\d+)\s*=\s*/)
+ {
+ my($idx) = int($1);
+ for $sym (split(/\s+/, $'))
+ {
+ my($val) = $map{uc($sym)};
+ $map[$idx] = $val;
+ if(!defined($val))
+ {
+ print STDERR "$file:$line: warning: `$sym' undefined\n";
+ }
+ else
+ {
+ last;
+ }
+ }
+ }
+ $line++;
+}
+close(USB);
+
+print "unsigned char usb_kbd_map[256] = \n{\n";
+for($x = 0; $x < 32; $x++)
+{
+ if($x && !($x % 2))
+ {
+ print "\n";
+ }
+ print " ";
+ for($y = 0; $y < 8; $y++)
+ {
+ my($idx) = $x * 8 + $y;
+ print sprintf(" 0x%02x,",
+ int(defined($map[$idx]) ? $map[$idx]:0));
+ }
+ print "\n";
+}
+print "};\n";
diff --git a/drivers/usb/printer.c b/drivers/usb/printer.c
new file mode 100644
index 000000000..88723c6ff
--- /dev/null
+++ b/drivers/usb/printer.c
@@ -0,0 +1,413 @@
+
+/* Driver for USB Printers
+ *
+ * (C) Michael Gee (michael@linuxspecific.com) 1999
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/lp.h>
+
+#include <asm/spinlock.h>
+
+#include "usb.h"
+
+#define NAK_TIMEOUT (HZ) /* stall wait for printer */
+#define MAX_RETRY_COUNT ((60*60*HZ)/NAK_TIMEOUT) /* should not take 1 minute a page! */
+
+#ifndef USB_PRINTER_MAJOR
+#define USB_PRINTER_MAJOR 0
+#endif
+
+static int mymajor = USB_PRINTER_MAJOR;
+
+#define MAX_PRINTERS 8
+
+struct pp_usb_data {
+ struct usb_device *pusb_dev;
+ __u8 isopen; /* nz if open */
+ __u8 noinput; /* nz if no input stream */
+ __u8 minor; /* minor number of device */
+ __u8 status; /* last status from device */
+ int maxin, maxout; /* max transfer size in and out */
+ char *obuf; /* transfer buffer (out only) */
+ wait_queue_head_t wait_q; /* for timeouts */
+ unsigned int last_error; /* save for checking */
+};
+
+static struct pp_usb_data *minor_data[MAX_PRINTERS];
+
+#define PPDATA(x) ((struct pp_usb_data *)(x))
+
+unsigned char printer_read_status(struct pp_usb_data *p)
+{
+ __u8 status;
+ devrequest dr;
+ struct usb_device *dev = p->pusb_dev;
+
+ dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE | 0x80;
+ dr.request = 1;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 1;
+ if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, &status, 1)) {
+ return 0;
+ }
+ return status;
+}
+
+static int printer_check_status(struct pp_usb_data *p)
+{
+ unsigned int last = p->last_error;
+ unsigned char status = printer_read_status(p);
+
+ if (status & LP_PERRORP)
+ /* No error. */
+ last = 0;
+ else if ((status & LP_POUTPA)) {
+ if (last != LP_POUTPA) {
+ last = LP_POUTPA;
+ printk(KERN_INFO "usblp%d out of paper\n", p->minor);
+ }
+ } else if (!(status & LP_PSELECD)) {
+ if (last != LP_PSELECD) {
+ last = LP_PSELECD;
+ printk(KERN_INFO "usblp%d off-line\n", p->minor);
+ }
+ } else {
+ if (last != LP_PERRORP) {
+ last = LP_PERRORP;
+ printk(KERN_INFO "usblp%d on fire\n", p->minor);
+ }
+ }
+
+ p->last_error = last;
+
+ return status;
+}
+
+void printer_reset(struct pp_usb_data *p)
+{
+ devrequest dr;
+ struct usb_device *dev = p->pusb_dev;
+
+ dr.requesttype = USB_TYPE_CLASS | USB_RECIP_OTHER;
+ dr.request = 2;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = 0;
+ dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0);
+}
+
+static int open_printer(struct inode * inode, struct file * file)
+{
+ struct pp_usb_data *p;
+
+ if(MINOR(inode->i_rdev) >= MAX_PRINTERS ||
+ !minor_data[MINOR(inode->i_rdev)]) {
+ return -ENODEV;
+ }
+
+ p = minor_data[MINOR(inode->i_rdev)];
+ p->minor = MINOR(inode->i_rdev);
+
+ if (p->isopen++) {
+ return -EBUSY;
+ }
+ if (!(p->obuf = (char *)__get_free_page(GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ printer_check_status(p);
+
+
+ file->private_data = p;
+// printer_reset(p);
+ init_waitqueue_head(&p->wait_q);
+ return 0;
+}
+
+static int close_printer(struct inode * inode, struct file * file)
+{
+ struct pp_usb_data *p = file->private_data;
+
+ free_page((unsigned long)p->obuf);
+ p->isopen = 0;
+ file->private_data = NULL;
+ if(!p->pusb_dev) {
+ minor_data[p->minor] = NULL;
+ kfree(p);
+
+ MOD_DEC_USE_COUNT;
+
+ }
+ return 0;
+}
+
+static ssize_t write_printer(struct file * file,
+ const char * buffer, size_t count, loff_t *ppos)
+{
+ struct pp_usb_data *p = file->private_data;
+ unsigned long copy_size;
+ unsigned long bytes_written = 0;
+ unsigned long partial;
+ int result;
+ int maxretry;
+
+ do {
+ char *obuf = p->obuf;
+ unsigned long thistime;
+
+ thistime = copy_size = (count > p->maxout) ? p->maxout : count;
+ if (copy_from_user(p->obuf, buffer, copy_size))
+ return -EFAULT;
+ maxretry = MAX_RETRY_COUNT;
+ while (thistime) {
+ if (!p->pusb_dev)
+ return -ENODEV;
+ if (signal_pending(current)) {
+ return bytes_written ? bytes_written : -EINTR;
+ }
+ result = p->pusb_dev->bus->op->bulk_msg(p->pusb_dev,
+ usb_sndbulkpipe(p->pusb_dev, 1), obuf, thistime, &partial);
+ if (result == USB_ST_TIMEOUT) { /* NAK - so hold for a while */
+ if(!maxretry--)
+ return -ETIME;
+ interruptible_sleep_on_timeout(&p->wait_q, NAK_TIMEOUT);
+ continue;
+ } else if (!result & partial) {
+ obuf += partial;
+ thistime -= partial;
+ } else
+ break;
+ };
+ if (result) {
+ /* whoops - let's reset and fail the request */
+// printk("Whoops - %x\n", result);
+ printer_reset(p);
+ interruptible_sleep_on_timeout(&p->wait_q, 5*HZ); /* let reset do its stuff */
+ return -EIO;
+ }
+ bytes_written += copy_size;
+ count -= copy_size;
+ buffer += copy_size;
+ } while ( count > 0 );
+
+ return bytes_written ? bytes_written : -EIO;
+}
+
+static ssize_t read_printer(struct file * file,
+ char * buffer, size_t count, loff_t *ppos)
+{
+ struct pp_usb_data *p = file->private_data;
+ int read_count;
+ int this_read;
+ char buf[64];
+ unsigned long partial;
+ int result;
+
+ if (p->noinput)
+ return -EINVAL;
+
+ read_count = 0;
+ while (count) {
+ if (signal_pending(current)) {
+ return read_count ? read_count : -EINTR;
+ }
+ if (!p->pusb_dev)
+ return -ENODEV;
+ this_read = (count > sizeof(buf)) ? sizeof(buf) : count;
+
+ result = p->pusb_dev->bus->op->bulk_msg(p->pusb_dev,
+ usb_rcvbulkpipe(p->pusb_dev, 2), buf, this_read, &partial);
+
+ /* unlike writes, we don't retry a NAK, just stop now */
+ if (!result & partial)
+ count = this_read = partial;
+ else if (result)
+ return -EIO;
+
+ if (this_read) {
+ if (copy_to_user(buffer, p->obuf, this_read))
+ return -EFAULT;
+ count -= this_read;
+ read_count += this_read;
+ buffer += this_read;
+ }
+ }
+ return read_count;
+}
+
+static int printer_probe(struct usb_device *dev)
+{
+ struct usb_interface_descriptor *interface;
+ int i;
+
+ /*
+ * FIXME - this will not cope with combined printer/scanners
+ */
+ if (dev->descriptor.bDeviceClass != 7 ||
+ dev->descriptor.bNumConfigurations != 1 ||
+ dev->config[0].bNumInterfaces != 1) {
+ return -1;
+ }
+
+ interface = dev->config->altsetting->interface;
+
+ /* Lets be paranoid (for the moment)*/
+ if (interface->bInterfaceClass != 7 ||
+ interface->bInterfaceSubClass != 1 ||
+ (interface->bInterfaceProtocol != 2 && interface->bInterfaceProtocol != 1)||
+ interface->bNumEndpoints > 2) {
+ return -1;
+ }
+
+ if (interface->endpoint[0].bEndpointAddress != 0x01 ||
+ interface->endpoint[0].bmAttributes != 0x02 ||
+ (interface->bNumEndpoints > 1 && (
+ interface->endpoint[1].bEndpointAddress != 0x82 ||
+ interface->endpoint[1].bmAttributes != 0x02))) {
+ return -1;
+ }
+
+ for (i=0; i<MAX_PRINTERS; i++) {
+ if (!minor_data[i])
+ break;
+ }
+ if (i >= MAX_PRINTERS) {
+ return -1;
+ }
+
+ printk(KERN_INFO "USB Printer found at address %d\n", dev->devnum);
+
+ if (!(dev->private = kmalloc(sizeof(struct pp_usb_data), GFP_KERNEL))) {
+ printk( KERN_DEBUG "usb_printer: no memory!\n");
+ return -1;
+ }
+
+ memset(dev->private, 0, sizeof(struct pp_usb_data));
+ minor_data[i] = PPDATA(dev->private);
+ minor_data[i]->minor = i;
+ minor_data[i]->pusb_dev = dev;
+ minor_data[i]->maxout = interface->endpoint[0].wMaxPacketSize * 16;
+ if (minor_data[i]->maxout > PAGE_SIZE) {
+ minor_data[i]->maxout = PAGE_SIZE;
+ }
+ if (interface->bInterfaceProtocol != 2)
+ minor_data[i]->noinput = 1;
+ else {
+ minor_data[i]->maxin = interface->endpoint[1].wMaxPacketSize;
+ }
+
+ if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
+ printk(KERN_INFO " Failed to set configuration\n");
+ return -1;
+ }
+#if 0
+ {
+ __u8 status;
+ __u8 ieee_id[64];
+ devrequest dr;
+
+ /* Lets get the device id if possible */
+ dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE | 0x80;
+ dr.request = 0;
+ dr.value = 0;
+ dr.index = 0;
+ dr.length = sizeof(ieee_id) - 1;
+ if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, ieee_id, sizeof(ieee_id)-1) == 0) {
+ if (ieee_id[1] < sizeof(ieee_id) - 1)
+ ieee_id[ieee_id[1]+2] = '\0';
+ else
+ ieee_id[sizeof(ieee_id)-1] = '\0';
+ printk(KERN_INFO " Printer ID is %s\n", &ieee_id[2]);
+ }
+ status = printer_read_status(PPDATA(dev->private));
+ printk(KERN_INFO " Status is %s,%s,%s\n",
+ (status & 0x10) ? "Selected" : "Not Selected",
+ (status & 0x20) ? "No Paper" : "Paper",
+ (status & 0x08) ? "No Error" : "Error");
+ }
+#endif
+ return 0;
+}
+
+static void printer_disconnect(struct usb_device *dev)
+{
+ struct pp_usb_data *pp = dev->private;
+
+ if (pp->isopen) {
+ /* better let it finish - the release will do whats needed */
+ pp->pusb_dev = NULL;
+ return;
+ }
+ minor_data[pp->minor] = NULL;
+ kfree(pp);
+ dev->private = NULL; /* just in case */
+ MOD_DEC_USE_COUNT;
+}
+
+static struct usb_driver printer_driver = {
+ "printer",
+ printer_probe,
+ printer_disconnect,
+ { NULL, NULL }
+};
+
+static struct file_operations usb_printer_fops = {
+ NULL, /* seek */
+ read_printer,
+ write_printer,
+ NULL, /* readdir */
+ NULL, /* poll - out for the moment */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ open_printer,
+ NULL, /* flush ? */
+ close_printer,
+ NULL,
+ NULL
+};
+
+int usb_printer_init(void)
+{
+ int result;
+
+ MOD_INC_USE_COUNT;
+
+ if ((result = register_chrdev(USB_PRINTER_MAJOR, "usblp", &usb_printer_fops)) < 0) {
+ printk(KERN_WARNING "usbprinter: Cannot register device\n");
+ return result;
+ }
+ if (mymajor == 0) {
+ mymajor = result;
+ }
+ usb_register(&printer_driver);
+ printk(KERN_INFO "USB Printer support registered.\n");
+ return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+
+ return usb_printer_init();
+}
+
+void cleanup_module(void)
+{
+ unsigned int offset;
+
+ usb_deregister(&printer_driver);
+ unregister_chrdev(mymajor, "usblplp");
+}
+#endif
diff --git a/drivers/usb/usb-core.c b/drivers/usb/usb-core.c
new file mode 100644
index 000000000..f9f73a051
--- /dev/null
+++ b/drivers/usb/usb-core.c
@@ -0,0 +1,96 @@
+/*
+ * driver/usb/usb-core.c
+ *
+ * (C) Copyright David Waite 1999
+ * based on code from usb.c, by Linus Torvolds
+ *
+ * The purpose of this file is to pull any and all generic modular code from
+ * usb.c and put it in a separate file. This way usb.c is kept as a generic
+ * library, while this file handles starting drivers, etc.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include "inits.h"
+#include "usb.h"
+
+#ifndef CONFIG_USB_MODULE
+# ifdef CONFIG_USB_UHCI
+ int uhci_init(void);
+# endif
+# ifdef CONFIG_USB_OHCI
+ int ohci_init(void);
+# endif
+# ifdef CONFIG_USB_OHCI_HCD
+ int ohci_hcd_init(void);
+# endif
+#endif
+
+int usb_init(void)
+{
+#ifndef CONFIG_USB_MODULE
+# ifdef CONFIG_USB_UHCI
+ uhci_init();
+# endif
+# ifdef CONFIG_USB_OHCI
+ ohci_init();
+# endif
+# ifdef CONFIG_USB_OHCI_HCD
+ ohci_hcd_init();
+# endif
+# ifdef CONFIG_USB_MOUSE
+ usb_mouse_init();
+# endif
+# ifdef CONFIG_USB_KBD
+ usb_kbd_init();
+# endif
+# ifdef CONFIG_USB_AUDIO
+ usb_audio_init();
+# endif
+# ifdef CONFIG_USB_ACM
+ usb_acm_init();
+# endif
+# ifdef CONFIG_USB_PRINTER
+ usb_print_init();
+# endif
+# ifdef CONFIG_USB_CPIA
+ usb_cpia_init();
+# endif
+# ifdef CONFIG_USB_HUB
+ usb_hub_init();
+# endif
+# ifdef CONFIG_USB_SCSI
+ usb_scsi_init();
+# endif
+#endif
+ return 0;
+}
+/*
+ * Clean up when unloading the module
+ */
+void cleanup_drivers(void)
+{
+#ifndef MODULE
+# ifdef CONFIG_USB_HUB
+ usb_hub_cleanup();
+# endif
+# ifdef CONFIG_USB_MOUSE
+ usb_mouse_cleanup();
+# endif
+#endif
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ return usb_init();
+}
+void cleanup_module(void)
+{
+ cleanup_drivers();
+}
+#endif
+
+
diff --git a/drivers/usb/usb_scsi.c b/drivers/usb/usb_scsi.c
new file mode 100644
index 000000000..655045bea
--- /dev/null
+++ b/drivers/usb/usb_scsi.c
@@ -0,0 +1,1098 @@
+
+/* Driver for USB scsi like devices
+ *
+ * (C) Michael Gee (michael@linuxspecific.com) 1999
+ *
+ * This driver is scitzoid - it makes a USB device appear as both a SCSI device
+ * and a character device. The latter is only available if the device has an
+ * interrupt endpoint, and is used specifically to receive interrupt events.
+ *
+ * In order to support various 'strange' devices, this module supports plug in
+ * device specific filter modules, which can do their own thing when required.
+ *
+ * Further reference.
+ * This driver is based on the 'USB Mass Storage Class' document. This
+ * describes in detail the transformation of SCSI command blocks to the
+ * equivalent USB control and data transfer required.
+ * It is important to note that in a number of cases this class exhibits
+ * class-specific exemptions from the USB specification. Notably the
+ * usage of NAK, STALL and ACK differs from the norm, in that they are
+ * used to communicate wait, failed and OK on SCSI commands.
+ * Also, for certain devices, the interrupt endpoint is used to convey
+ * status of a command.
+ *
+ * Basically, this stuff is WIERD!!
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+
+#include <asm/spinlock.h>
+#include <linux/smp_lock.h>
+
+#include <linux/blk.h>
+#include "../scsi/scsi.h"
+#include "../scsi/hosts.h"
+#include "../scsi/sd.h"
+
+#include "usb.h"
+#include "usb_scsi.h"
+
+/* direction table (what a pain) */
+
+unsigned char us_direction[256/8] = {
+
+#include "usb_scsi_dt.c"
+
+};
+
+/*
+ * Per device data
+ */
+
+static int my_host_number;
+
+int usbscsi_debug = 1;
+
+struct us_data {
+ struct us_data *next; /* next device */
+ struct usb_device *pusb_dev;
+ struct usb_scsi_filter *filter; /* filter driver */
+ void *fdata; /* filter data */
+ unsigned int flags; /* from filter initially*/
+ __u8 ep_in; /* in endpoint */
+ __u8 ep_out; /* out ....... */
+ __u8 ep_int; /* interrupt . */
+ __u8 subclass; /* as in overview */
+ __u8 protocol; /* .............. */
+ int (*pop)(Scsi_Cmnd *); /* protocol specific do cmd */
+ GUID(guid); /* unique dev id */
+ struct Scsi_Host *host; /* our dummy host data */
+ Scsi_Host_Template *htmplt; /* own host template */
+ int host_number; /* to find us */
+ int host_no; /* allocated by scsi */
+ int fixedlength; /* expand commands */
+ Scsi_Cmnd *srb; /* current srb */
+ int action; /* what to do */
+ wait_queue_head_t waitq; /* thread waits */
+ wait_queue_head_t ip_waitq; /* for CBI interrupts */
+ __u16 ip_data; /* interrupt data */
+ int ip_wanted; /* needed */
+ int pid; /* control thread */
+ struct semaphore *notify; /* wait for thread to begin */
+};
+
+/*
+ * kernel thread actions
+ */
+
+#define US_ACT_COMMAND 1
+#define US_ACT_ABORT 2
+#define US_ACT_DEVICE_RESET 3
+#define US_ACT_BUS_RESET 4
+#define US_ACT_HOST_RESET 5
+
+static struct proc_dir_entry proc_usb_scsi =
+{
+ PROC_SCSI_USB_SCSI,
+ 0,
+ NULL,
+ S_IFDIR | S_IRUGO | S_IXUGO,
+ 2
+};
+
+static struct us_data *us_list;
+
+static struct usb_scsi_filter *filters;
+
+static int scsi_probe(struct usb_device *dev);
+static void scsi_disconnect(struct usb_device *dev);
+static struct usb_driver scsi_driver = {
+ "usb_scsi",
+ scsi_probe,
+ scsi_disconnect,
+ { NULL, NULL }
+};
+
+/* Data handling, using SG if required */
+
+static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length)
+{
+ int max_size = usb_maxpacket(us->pusb_dev, pipe) * 16;
+ int this_xfer;
+ int result;
+ unsigned long partial;
+ int maxtry = 100;
+ while (length) {
+ this_xfer = length > max_size ? max_size : length;
+ length -= this_xfer;
+ do {
+ US_DEBUGP("Bulk xfer %x(%d)\n", (unsigned int)buf, this_xfer);
+ result = us->pusb_dev->bus->op->bulk_msg(us->pusb_dev, pipe, buf,
+ this_xfer, &partial);
+
+ /* we want to retry if the device reported NAK */
+ if (result == USB_ST_TIMEOUT) {
+ if (!maxtry--)
+ break;
+ this_xfer -= partial;
+ buf += partial;
+ } else if (!result && partial != this_xfer) {
+ /* short data - assume end */
+ result = USB_ST_DATAUNDERRUN;
+ break;
+ } else
+ break;
+ } while ( this_xfer );
+ if (result)
+ return result;
+ buf += this_xfer;
+ }
+ return 0;
+
+}
+static int us_transfer(Scsi_Cmnd *srb, int dir_in)
+{
+ struct us_data *us = (struct us_data *)srb->host_scribble;
+ int i;
+ int result = -1;
+
+ if (srb->use_sg) {
+ struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
+
+ for (i = 0; i < srb->use_sg; i++) {
+ result = us_one_transfer(us, dir_in ? usb_rcvbulkpipe(us->pusb_dev, us->ep_in) :
+ usb_sndbulkpipe(us->pusb_dev, us->ep_out),
+ sg[i].address, sg[i].length);
+ if (result)
+ break;
+ }
+ return result;
+ }
+ else
+ return us_one_transfer(us, dir_in ? usb_rcvbulkpipe(us->pusb_dev, us->ep_in) :
+ usb_sndbulkpipe(us->pusb_dev, us->ep_out),
+ srb->request_buffer, srb->request_bufflen);
+}
+
+static unsigned int us_transfer_length(Scsi_Cmnd *srb)
+{
+ int i;
+ unsigned int total = 0;
+
+ /* always zero for some commands */
+ switch (srb->cmnd[0]) {
+ case SEEK_6:
+ case SEEK_10:
+ case REZERO_UNIT:
+ case ALLOW_MEDIUM_REMOVAL:
+ case START_STOP:
+ case TEST_UNIT_READY:
+ return 0;
+
+ default:
+ break;
+ }
+
+ if (srb->use_sg) {
+ struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
+
+ for (i = 0; i < srb->use_sg; i++) {
+ total += sg[i].length;
+ }
+ return total;
+ }
+ else
+ return srb->request_bufflen;
+
+}
+
+static int pop_CBI_irq(int state, void *buffer, void *dev_id)
+{
+ struct us_data *us = (struct us_data *)dev_id;
+
+ if (state != USB_ST_REMOVED) {
+ us->ip_data = *(__u16 *)buffer;
+ us->ip_wanted = 0;
+ }
+ wake_up(&us->ip_waitq);
+
+ /* we dont want another interrupt */
+
+ return 0;
+}
+static int pop_CB_command(Scsi_Cmnd *srb)
+{
+ struct us_data *us = (struct us_data *)srb->host_scribble;
+ devrequest dr;
+ unsigned char cmd[16];
+ int result;
+ int retry = 1;
+ int done_start = 0;
+
+ while (retry--) {
+ dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE;
+ dr.request = US_CBI_ADSC;
+ dr.value = 0;
+ dr.index = us->pusb_dev->ifnum;
+ dr.length = srb->cmd_len;
+
+ if (us->flags & US_FL_FIXED_COMMAND) {
+ dr.length = us->fixedlength;
+ memset(cmd, 0, us->fixedlength);
+
+ /* fix some commands */
+
+ switch (srb->cmnd[0]) {
+ case WRITE_6:
+ case READ_6:
+ cmd[0] = srb->cmnd[0] | 0x20;
+ cmd[1] = srb->cmnd[1] & 0xE0;
+ cmd[2] = 0;
+ cmd[3] = srb->cmnd[1] & 0x1F;
+ cmd[4] = srb->cmnd[2];
+ cmd[5] = srb->cmnd[3];
+ cmd[8] = srb->cmnd[4];
+ break;
+
+ case MODE_SENSE:
+ case MODE_SELECT:
+ cmd[0] = srb->cmnd[0] | 0x40;
+ cmd[1] = srb->cmnd[1];
+ cmd[2] = srb->cmnd[2];
+ cmd[8] = srb->cmnd[4];
+ break;
+
+ default:
+ memcpy(cmd, srb->cmnd, srb->cmd_len);
+ break;
+ }
+ result = us->pusb_dev->bus->op->control_msg(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,0),
+ &dr, cmd, us->fixedlength);
+ if (!done_start && us->subclass == US_SC_UFI && cmd[0] == TEST_UNIT_READY && result) {
+ /* as per spec try a start command, wait and retry */
+
+ done_start++;
+ cmd[0] = START_STOP;
+ cmd[4] = 1; /* start */
+ result = us->pusb_dev->bus->op->control_msg(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,0),
+ &dr, cmd, us->fixedlength);
+ wait_ms(100);
+ retry++;
+ continue;
+ }
+ } else
+ result = us->pusb_dev->bus->op->control_msg(us->pusb_dev,
+ usb_sndctrlpipe(us->pusb_dev,0),
+ &dr, srb->cmnd, srb->cmd_len);
+ if (result != USB_ST_STALL && result != USB_ST_TIMEOUT)
+ return result;
+ }
+ return result;
+}
+
+/* Protocol command handlers */
+
+static int pop_CBI(Scsi_Cmnd *srb)
+{
+ struct us_data *us = (struct us_data *)srb->host_scribble;
+ int result;
+
+ /* run the command */
+
+ if ((result = pop_CB_command(srb))) {
+ US_DEBUGP("CBI command %x\n", result);
+ if (result == USB_ST_STALL || result == USB_ST_TIMEOUT)
+ return (DID_OK << 16) | 2;
+ return DID_ABORT << 16;
+ }
+
+ /* transfer the data */
+
+ if (us_transfer_length(srb)) {
+ result = us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
+ if (result && result != USB_ST_DATAUNDERRUN) {
+ US_DEBUGP("CBI transfer %x\n", result);
+ return DID_ABORT << 16;
+ }
+ }
+
+ /* get status */
+
+ if (us->protocol == US_PR_CBI) {
+ /* get from interrupt pipe */
+
+ /* add interrupt transfer, marked for removal */
+ us->ip_wanted = 1;
+ result = us->pusb_dev->bus->op->request_irq(us->pusb_dev,
+ usb_rcvctrlpipe(us->pusb_dev, us->ep_int),
+ pop_CBI_irq, 0, (void *)us);
+ if (result) {
+ US_DEBUGP("No interrupt for CBI %x\n", result);
+ return DID_ABORT << 16;
+ }
+ sleep_on(&us->ip_waitq);
+ if (us->ip_wanted) {
+ US_DEBUGP("Did not get interrupt on CBI\n");
+ us->ip_wanted = 0;
+ return DID_ABORT << 16;
+ }
+
+ US_DEBUGP("Got interrupt data %x\n", us->ip_data);
+
+ /* sort out what it means */
+
+ if (us->subclass == US_SC_UFI) {
+ /* gives us asc and ascq, as per request sense */
+
+ if (srb->cmnd[0] == REQUEST_SENSE ||
+ srb->cmnd[0] == INQUIRY)
+ return DID_OK << 16;
+ else
+ return (DID_OK << 16) + ((us->ip_data & 0xff) ? 2 : 0);
+ }
+ if (us->ip_data & 0xff) {
+ US_DEBUGP("Bad CBI interrupt data %x\n", us->ip_data);
+ return DID_ABORT << 16;
+ }
+ return (DID_OK << 16) + ((us->ip_data & 0x300) ? 2 : 0);
+ } else {
+ /* get from where? */
+ }
+ return DID_ERROR << 16;
+}
+
+static int pop_Bulk_reset(struct us_data *us)
+{
+ devrequest dr;
+ int result;
+
+ dr.requesttype = USB_TYPE_CLASS | USB_RT_INTERFACE;
+ dr.request = US_BULK_RESET;
+ dr.value = US_BULK_RESET_SOFT;
+ dr.index = 0;
+ dr.length = 0;
+
+ US_DEBUGP("Bulk soft reset\n");
+ result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), &dr, NULL, 0);
+ if (result) {
+ US_DEBUGP("Bulk soft reset failed %d\n", result);
+ dr.value = US_BULK_RESET_HARD;
+ result = us->pusb_dev->bus->op->control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), &dr, NULL, 0);
+ if (result)
+ US_DEBUGP("Bulk hard reset failed %d\n", result);
+ }
+ usb_clear_halt(us->pusb_dev, us->ep_in | 0x80);
+ usb_clear_halt(us->pusb_dev, us->ep_out);
+ return result;
+}
+/*
+ * The bulk only protocol handler.
+ * Uses the in and out endpoints to transfer commands and data (nasty)
+ */
+static int pop_Bulk(Scsi_Cmnd *srb)
+{
+ struct us_data *us = (struct us_data *)srb->host_scribble;
+ struct bulk_cb_wrap bcb;
+ struct bulk_cs_wrap bcs;
+ int result;
+ unsigned long partial;
+ int stall;
+
+ /* set up the command wrapper */
+
+ bcb.Signature = US_BULK_CB_SIGN;
+ bcb.DataTransferLength = us_transfer_length(srb);;
+ bcb.Flags = US_DIRECTION(srb->cmnd[0]) << 7;
+ bcb.Tag = srb->serial_number;
+ bcb.Lun = 0;
+ memset(bcb.CDB, 0, sizeof(bcb.CDB));
+ memcpy(bcb.CDB, srb->cmnd, srb->cmd_len);
+ if (us->flags & US_FL_FIXED_COMMAND) {
+ bcb.Length = us->fixedlength;
+ } else {
+ bcb.Length = srb->cmd_len;
+ }
+
+ /* send it to out endpoint */
+
+ US_DEBUGP("Bulk command S %x T %x L %d F %d CL %d\n", bcb.Signature,
+ bcb.Tag, bcb.DataTransferLength, bcb.Flags, bcb.Length);
+ result = us->pusb_dev->bus->op->bulk_msg(us->pusb_dev,
+ usb_sndbulkpipe(us->pusb_dev, us->ep_out), &bcb,
+ US_BULK_CB_WRAP_LEN, &partial);
+ if (result) {
+ US_DEBUGP("Bulk command result %x\n", result);
+ return DID_ABORT << 16;
+ }
+
+ //return DID_BAD_TARGET << 16;
+ /* send/receive data */
+
+ if (bcb.DataTransferLength) {
+ result = us_transfer(srb, bcb.Flags);
+ if (result && result != USB_ST_DATAUNDERRUN && result != USB_ST_STALL) {
+ US_DEBUGP("Bulk transfer result %x\n", result);
+ return DID_ABORT << 16;
+ }
+ }
+
+ /* get status */
+
+
+ stall = 0;
+ do {
+ //usb_settoggle(us->pusb_dev, us->ep_in, 0); /* AAARgh!! */
+ US_DEBUGP("Toggle is %d\n", usb_gettoggle(us->pusb_dev, us->ep_in));
+ result = us->pusb_dev->bus->op->bulk_msg(us->pusb_dev,
+ usb_rcvbulkpipe(us->pusb_dev, us->ep_in), &bcs,
+ US_BULK_CS_WRAP_LEN, &partial);
+ if (result == USB_ST_STALL || result == USB_ST_TIMEOUT)
+ stall++;
+ else
+ break;
+ } while ( stall < 3);
+ if (result && result != USB_ST_DATAUNDERRUN) {
+ US_DEBUGP("Bulk status result = %x\n", result);
+ return DID_ABORT << 16;
+ }
+
+ /* check bulk status */
+
+ US_DEBUGP("Bulk status S %x T %x R %d V %x\n", bcs.Signature, bcs.Tag,
+ bcs.Residue, bcs.Status);
+ if (bcs.Signature != US_BULK_CS_SIGN || bcs.Tag != bcb.Tag ||
+ bcs.Status > US_BULK_STAT_PHASE) {
+ US_DEBUGP("Bulk logical error\n");
+ return DID_ABORT << 16;
+ }
+ switch (bcs.Status) {
+ case US_BULK_STAT_OK:
+ return DID_OK << 16;
+
+ case US_BULK_STAT_FAIL:
+ /* check for underrun - dont report */
+ if (bcs.Residue)
+ return DID_OK << 16;
+ //pop_Bulk_reset(us);
+ break;
+
+ case US_BULK_STAT_PHASE:
+ return DID_ERROR << 16;
+ }
+ return (DID_OK << 16) | 2; /* check sense required */
+
+}
+
+/* Host functions */
+
+/* detect adapter (always true ) */
+static int us_detect(struct SHT *sht)
+{
+ /* FIXME - not nice at all, but how else ? */
+ struct us_data *us = (struct us_data *)sht->proc_dir;
+ char name[32];
+
+ sprintf(name, "usbscsi%d", us->host_number);
+ proc_usb_scsi.namelen = strlen(name);
+ proc_usb_scsi.name = kmalloc(proc_usb_scsi.namelen+1, GFP_KERNEL);
+ if (!proc_usb_scsi.name)
+ return 0;
+ strcpy((char *)proc_usb_scsi.name, name);
+ sht->proc_dir = kmalloc(sizeof(*sht->proc_dir), GFP_KERNEL);
+ if (!sht->proc_dir) {
+ kfree(proc_usb_scsi.name);
+ return 0;
+ }
+ *sht->proc_dir = proc_usb_scsi;
+ sht->name = proc_usb_scsi.name;
+ us->host = scsi_register(sht, sizeof(us));
+ if (us->host) {
+ us->host->hostdata[0] = (unsigned long)us;
+ us->host_no = us->host->host_no;
+ return 1;
+ }
+ kfree(proc_usb_scsi.name);
+ kfree(sht->proc_dir);
+ return 0;
+}
+
+/* release - must be here to stop scsi
+ * from trying to release IRQ etc.
+ * Kill off our data
+ */
+static int us_release(struct Scsi_Host *psh)
+{
+ struct us_data *us = (struct us_data *)psh->hostdata[0];
+ struct us_data *prev = (struct us_data *)&us_list;
+
+ if (us->filter)
+ us->filter->release(us->fdata);
+ if (us->pusb_dev)
+ usb_deregister(&scsi_driver);
+
+ /* FIXME - leaves hanging host template copy */
+ /* (bacause scsi layer uses it after removal !!!) */
+ while(prev->next != us)
+ prev = prev->next;
+ prev->next = us->next;
+ return 0;
+}
+
+/* run command */
+static int us_command( Scsi_Cmnd *srb )
+{
+ US_DEBUGP("Bad use of us_command\n");
+
+ return DID_BAD_TARGET << 16;
+}
+
+/* run command */
+static int us_queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
+{
+ struct us_data *us = (struct us_data *)srb->host->hostdata[0];
+
+ US_DEBUGP("Command wakeup\n");
+ srb->host_scribble = (unsigned char *)us;
+ us->srb = srb;
+ srb->scsi_done = done;
+ us->action = US_ACT_COMMAND;
+
+ /* wake up the process task */
+
+ wake_up_interruptible(&us->waitq);
+
+ return 0;
+}
+
+static int us_abort( Scsi_Cmnd *srb )
+{
+ return 0;
+}
+
+static int us_device_reset( Scsi_Cmnd *srb )
+{
+ return 0;
+}
+
+static int us_host_reset( Scsi_Cmnd *srb )
+{
+ return 0;
+}
+
+static int us_bus_reset( Scsi_Cmnd *srb )
+{
+ return 0;
+}
+
+#undef SPRINTF
+#define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); }
+
+int usb_scsi_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout)
+{
+ struct us_data *us = us_list;
+ char *pos = buffer;
+ char *vendor;
+ char *product;
+ char *style = "";
+
+ /* find our data from hostno */
+
+ while (us) {
+ if (us->host_no == hostno)
+ break;
+ us = us->next;
+ }
+
+ if (!us)
+ return -ESRCH;
+
+ /* null on outward */
+
+ if (inout)
+ return length;
+
+ if (!(vendor = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iManufacturer)))
+ vendor = "?";
+ if (!(product = usb_string(us->pusb_dev, us->pusb_dev->descriptor.iProduct)))
+ product = "?";
+
+ switch (us->protocol) {
+ case US_PR_CB:
+ style = "Control/Bulk";
+ break;
+
+ case US_PR_CBI:
+ style = "Control/Bulk/Interrupt";
+ break;
+
+ case US_PR_ZIP:
+ style = "Bulk only";
+ break;
+
+ }
+ SPRINTF ("Host scsi%d: usb-scsi\n", hostno);
+ SPRINTF ("Device: %s %s - GUID " GUID_FORMAT "\n", vendor, product, GUID_ARGS(us->guid) );
+ SPRINTF ("Style: %s\n", style);
+
+ /*
+ * Calculate start of next buffer, and return value.
+ */
+ *start = buffer + offset;
+
+ if ((pos - buffer) < offset)
+ return (0);
+ else if ((pos - buffer - offset) < length)
+ return (pos - buffer - offset);
+ else
+ return (length);
+}
+
+/*
+ * this defines our 'host'
+ */
+
+static Scsi_Host_Template my_host_template = {
+ NULL, /* next */
+ NULL, /* module */
+ NULL, /* proc_dir */
+ usb_scsi_proc_info,
+ NULL, /* name - points to unique */
+ us_detect,
+ us_release,
+ NULL, /* info */
+ NULL, /* ioctl */
+ us_command,
+ us_queuecommand,
+ NULL, /* eh_strategy */
+ us_abort,
+ us_device_reset,
+ us_bus_reset,
+ us_host_reset,
+ NULL, /* abort */
+ NULL, /* reset */
+ NULL, /* slave_attach */
+ NULL, /* bios_param */
+ 1, /* can_queue */
+ -1, /* this_id */
+ SG_ALL, /* sg_tablesize */
+ 1, /* cmd_per_lun */
+ 0, /* present */
+ FALSE, /* unchecked_isa_dma */
+ FALSE, /* use_clustering */
+ TRUE, /* use_new_eh_code */
+ TRUE /* emulated */
+};
+
+static int usbscsi_control_thread(void * __us)
+{
+ struct us_data *us = (struct us_data *)__us;
+ int action;
+
+ lock_kernel();
+
+ /*
+ * This thread doesn't need any user-level access,
+ * so get rid of all our resources..
+ */
+ exit_mm(current);
+ exit_files(current);
+ //exit_fs(current);
+
+ sprintf(current->comm, "usbscsi%d", us->host_no);
+
+ unlock_kernel();
+
+ up(us->notify);
+
+ for(;;) {
+ siginfo_t info;
+ int unsigned long signr;
+
+ interruptible_sleep_on(&us->waitq);
+
+ action = us->action;
+ us->action = 0;
+
+ switch (action) {
+ case US_ACT_COMMAND :
+ if (!us->pusb_dev || us->srb->target || us->srb->lun) {
+ /* bad device */
+ US_DEBUGP( "Bad device number (%d/%d) or dev %x\n", us->srb->target, us->srb->lun, (unsigned int)us->pusb_dev);
+ us->srb->result = DID_BAD_TARGET << 16;
+ } else {
+ US_DEBUG(us_show_command(us->srb));
+ if (us->filter && us->filter->command)
+ us->srb->result = us->filter->command(us->fdata, us->srb);
+ else
+ us->srb->result = us->pop(us->srb);
+ }
+ us->srb->scsi_done(us->srb);
+ break;
+
+ case US_ACT_ABORT :
+ break;
+
+ case US_ACT_DEVICE_RESET :
+ break;
+
+ case US_ACT_BUS_RESET :
+ break;
+
+ case US_ACT_HOST_RESET :
+ break;
+
+ }
+
+ if(signal_pending(current)) {
+ /* sending SIGUSR1 makes us print out some info */
+ spin_lock_irq(&current->sigmask_lock);
+ signr = dequeue_signal(&current->blocked, &info);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ if (signr == SIGUSR2) {
+ printk("USBSCSI debug toggle\n");
+ usbscsi_debug = !usbscsi_debug;
+ } else {
+ break;
+ }
+ }
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ printk("usbscsi_control_thread exiting\n");
+
+ return 0;
+}
+
+static int scsi_probe(struct usb_device *dev)
+{
+ struct usb_interface_descriptor *interface;
+ int i;
+ char *mf; /* manufacturer */
+ char *prod; /* product */
+ char *serial; /* serial number */
+ struct us_data *ss = NULL;
+ struct usb_scsi_filter *filter = filters;
+ void *fdata = NULL;
+ unsigned int flags = 0;
+ GUID(guid);
+ struct us_data *prev;
+ Scsi_Host_Template *htmplt;
+ int protocol = 0;
+ int subclass = 0;
+
+ GUID_CLEAR(guid);
+ mf = usb_string(dev, dev->descriptor.iManufacturer);
+ prod = usb_string(dev, dev->descriptor.iProduct);
+ serial = usb_string(dev, dev->descriptor.iSerialNumber);
+
+ /* probe with filters first */
+
+ if (mf && prod) {
+ while (filter) {
+ if ((fdata = filter->probe(dev, mf, prod, serial)) != NULL) {
+ flags = filter->flags;
+ printk(KERN_INFO "USB Scsi filter %s\n", filter->name);
+ break;
+ }
+ filter = filter->next;
+ }
+ }
+
+ /* generic devices next */
+
+ if (fdata == NULL) {
+
+ /* some exceptions */
+ if (dev->descriptor.idVendor == 0x04e6 &&
+ dev->descriptor.idProduct == 0x0001) {
+ /* shuttle E-USB */
+ protocol = US_PR_ZIP;
+ subclass = US_SC_8070; /* an assumption */
+ } else if (dev->descriptor.bDeviceClass != 0 ||
+ dev->config->altsetting->interface->bInterfaceClass != 8 ||
+ dev->config->altsetting->interface->bInterfaceSubClass < US_SC_MIN ||
+ dev->config->altsetting->interface->bInterfaceSubClass > US_SC_MAX) {
+ return -1;
+ }
+
+ /* now check if we have seen it before */
+
+ if (dev->descriptor.iSerialNumber &&
+ usb_string(dev, dev->descriptor.iSerialNumber) ) {
+ make_guid(guid, dev->descriptor.idVendor, dev->descriptor.idProduct,
+ usb_string(dev, dev->descriptor.iSerialNumber));
+ for (ss = us_list; ss; ss = ss->next) {
+ if (GUID_EQUAL(guid, ss->guid)) {
+ US_DEBUGP("Found existing GUID " GUID_FORMAT "\n", GUID_ARGS(guid));
+ break;
+ }
+ }
+ }
+ }
+
+ if (!ss) {
+ if ((ss = (struct us_data *)kmalloc(sizeof(*ss), GFP_KERNEL)) == NULL) {
+ printk(KERN_WARNING USB_SCSI "Out of memory\n");
+ if (filter)
+ filter->release(fdata);
+ return -1;
+ }
+ memset(ss, 0, sizeof(struct us_data));
+ }
+
+ interface = dev->config->altsetting->interface;
+ ss->filter = filter;
+ ss->fdata = fdata;
+ ss->flags = flags;
+ if (subclass) {
+ ss->subclass = subclass;
+ ss->protocol = protocol;
+ } else {
+ ss->subclass = interface->bInterfaceSubClass;
+ ss->protocol = interface->bInterfaceProtocol;
+ }
+
+ /* set the protocol op */
+
+ US_DEBUGP("Protocol ");
+ switch (ss->protocol) {
+ case US_PR_CB:
+ US_DEBUGPX("Control/Bulk\n");
+ ss->pop = pop_CBI;
+ break;
+
+ case US_PR_CBI:
+ US_DEBUGPX("Control/Bulk/Interrupt\n");
+ ss->pop = pop_CBI;
+ break;
+
+ default:
+ US_DEBUGPX("Bulk\n");
+ ss->pop = pop_Bulk;
+ break;
+ }
+
+ /*
+ * we are expecting a minimum of 2 endpoints - in and out (bulk)
+ * an optional interrupt is OK (necessary for CBI protocol)
+ * we will ignore any others
+ */
+
+ for (i = 0; i < interface->bNumEndpoints; i++) {
+ if (interface->endpoint[i].bmAttributes == 0x02) {
+ if (interface->endpoint[i].bEndpointAddress & 0x80)
+ ss->ep_in = interface->endpoint[i].bEndpointAddress & 0x0f;
+ else
+ ss->ep_out = interface->endpoint[i].bEndpointAddress & 0x0f;
+ } else if (interface->endpoint[i].bmAttributes == 0x03) {
+ ss->ep_int = interface->endpoint[i].bEndpointAddress & 0x0f;
+ }
+ }
+ US_DEBUGP("Endpoints In %d Out %d Int %d\n", ss->ep_in, ss->ep_out, ss->ep_int);
+
+ /* exit if strange looking */
+
+ if (usb_set_configuration(dev, dev->config[0].bConfigurationValue) ||
+ !ss->ep_in || !ss->ep_out || (ss->protocol == US_PR_CBI && ss->ep_int == 0)) {
+ US_DEBUGP("Problems with device\n");
+ if (ss->host) {
+ scsi_unregister_module(MODULE_SCSI_HA, ss->htmplt);
+ kfree(ss->htmplt->name);
+ kfree(ss->htmplt);
+ }
+ if (filter)
+ filter->release(fdata);
+ kfree(ss);
+ return -1; /* no endpoints */
+ }
+
+ if (dev->config[0].iConfiguration && usb_string(dev, dev->config[0].iConfiguration))
+ US_DEBUGP("Configuration %s\n", usb_string(dev, dev->config[0].iConfiguration));
+ if (interface->iInterface && usb_string(dev, interface->iInterface))
+ US_DEBUGP("Interface %s\n", usb_string(dev, interface->iInterface));
+
+ ss->pusb_dev = dev;
+
+ /* Now generate a scsi host definition, and register with scsi above us */
+
+ if (!ss->host) {
+
+ /* make unique id if possible */
+
+ if (dev->descriptor.iSerialNumber &&
+ usb_string(dev, dev->descriptor.iSerialNumber) ) {
+ make_guid(ss->guid, dev->descriptor.idVendor, dev->descriptor.idProduct,
+ usb_string(dev, dev->descriptor.iSerialNumber));
+ }
+
+ US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid));
+
+ /* set class specific stuff */
+
+ US_DEBUGP("SubClass ");
+ switch (ss->subclass) {
+ case US_SC_RBC:
+ US_DEBUGPX("Reduced Block Commands\n");
+ break;
+ case US_SC_8020:
+ US_DEBUGPX("8020\n");
+ break;
+ case US_SC_QIC:
+ US_DEBUGPX("QIC157\n");
+ break;
+ case US_SC_8070:
+ US_DEBUGPX("8070\n");
+ ss->flags |= US_FL_FIXED_COMMAND;
+ ss->fixedlength = 12;
+ break;
+ case US_SC_SCSI:
+ US_DEBUGPX("Transparent SCSI\n");
+ break;
+ case US_SC_UFI:
+ US_DEBUGPX(" UFF\n");
+ ss->flags |= US_FL_FIXED_COMMAND;
+ ss->fixedlength = 12;
+ break;
+
+ default:
+ break;
+ }
+
+ /* create unique host template */
+
+ if ((htmplt = (Scsi_Host_Template *)kmalloc(sizeof(*ss->htmplt), GFP_KERNEL)) == NULL ) {
+ printk(KERN_WARNING USB_SCSI "Out of memory\n");
+ if (filter)
+ filter->release(fdata);
+ kfree(ss);
+ return -1;
+ }
+ memcpy(htmplt, &my_host_template, sizeof(my_host_template));
+ ss->host_number = my_host_number++;
+
+
+ (struct us_data *)htmplt->proc_dir = ss;
+ if (ss->protocol == US_PR_CBI)
+ init_waitqueue_head(&ss->ip_waitq);
+
+ /* start up our thread */
+
+ {
+ DECLARE_MUTEX_LOCKED(sem);
+
+ init_waitqueue_head(&ss->waitq);
+
+ ss->notify = &sem;
+ ss->pid = kernel_thread(usbscsi_control_thread, ss,
+ CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ if (ss->pid < 0) {
+ printk(KERN_WARNING USB_SCSI "Unable to start control thread\n");
+ kfree(htmplt);
+ if (filter)
+ filter->release(fdata);
+ kfree(ss);
+ return -1;
+ }
+
+ /* wait for it to start */
+
+ down(&sem);
+ }
+
+ /* now register - our detect function will be called */
+
+ scsi_register_module(MODULE_SCSI_HA, htmplt);
+
+ /* put us in the list */
+
+ prev = (struct us_data *)&us_list;
+ while (prev->next)
+ prev = prev->next;
+ prev->next = ss;
+
+ }
+
+
+ printk(KERN_INFO "USB SCSI device found at address %d\n", dev->devnum);
+
+ dev->private = ss;
+ return 0;
+}
+
+static void scsi_disconnect(struct usb_device *dev)
+{
+ struct us_data *ss = dev->private;
+
+ if (!ss)
+ return;
+ if (ss->filter)
+ ss->filter->release(ss->fdata);
+ ss->pusb_dev = NULL;
+ dev->private = NULL; /* just in case */
+ MOD_DEC_USE_COUNT;
+}
+
+int usb_scsi_init(void)
+{
+
+ MOD_INC_USE_COUNT;
+#ifdef CONFIG_USB_HP4100
+ hp4100_init();
+#endif
+#ifdef CONFIG_USB_ZIP
+ usb_zip_init();
+#endif
+ usb_register(&scsi_driver);
+ printk(KERN_INFO "USB SCSI support registered.\n");
+ return 0;
+}
+
+
+int usb_scsi_register(struct usb_scsi_filter *filter)
+{
+ struct usb_scsi_filter *prev = (struct usb_scsi_filter *)&filters;
+
+ while (prev->next)
+ prev = prev->next;
+ prev->next = filter;
+ return 0;
+}
+
+void usb_scsi_deregister(struct usb_scsi_filter *filter)
+{
+ struct usb_scsi_filter *prev = (struct usb_scsi_filter *)&filters;
+
+ while (prev->next && prev->next != filter)
+ prev = prev->next;
+ if (prev->next)
+ prev->next = filter->next;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+
+ return usb_scsi_init();
+}
+
+void cleanup_module(void)
+{
+ unsigned int offset;
+
+ usb_deregister(&scsi_driver);
+}
+#endif
diff --git a/drivers/usb/usb_scsi.h b/drivers/usb/usb_scsi.h
new file mode 100644
index 000000000..58367d852
--- /dev/null
+++ b/drivers/usb/usb_scsi.h
@@ -0,0 +1,145 @@
+/* Driver for USB scsi - include file
+ *
+ * (C) Michael Gee (michael@linuxspecific.com) 1999
+ *
+ * This driver is scitzoid - it make a USB scanner appear as both a SCSI device
+ * and a character device. The latter is only available if the device has an
+ * interrupt endpoint, and is used specifically to receive interrupt events.
+ *
+ * In order to support various 'strange' scanners, this module supports plug in
+ * device specific filter modules, which can do their own thing when required.
+ *
+ */
+
+#define USB_SCSI "usbscsi: "
+
+extern int usbscsi_debug;
+
+#ifdef CONFIG_USB_SCSI_DEBUG
+void us_show_command(Scsi_Cmnd *srb);
+#define US_DEBUGP(x...) { if(usbscsi_debug) printk( KERN_DEBUG USB_SCSI ## x ); }
+#define US_DEBUGPX(x...) { if(usbscsi_debug) printk( ## x ); }
+#define US_DEBUG(x) { if(usbscsi_debug) x; }
+#else
+#define US_DEBUGP(x...)
+#define US_DEBUGPX(x...)
+#define US_DEBUG(x)
+#endif
+
+/* bit set if input */
+extern unsigned char us_direction[256/8];
+#define US_DIRECTION(x) ((us_direction[x>>3] >> (x & 7)) & 1)
+
+/* Sub Classes */
+
+#define US_SC_RBC 1 /* Typically, flash devices */
+#define US_SC_8020 2 /* CD-ROM */
+#define US_SC_QIC 3 /* QIC-157 Tapes */
+#define US_SC_UFI 4 /* Floppy */
+#define US_SC_8070 5 /* Removable media */
+#define US_SC_SCSI 6 /* Transparent */
+#define US_SC_MIN US_SC_RBC
+#define US_SC_MAX US_SC_SCSI
+
+/* Protocols */
+
+#define US_PR_CB 1 /* Control/Bulk w/o interrupt */
+#define US_PR_CBI 0 /* Control/Bulk/Interrupt */
+#define US_PR_ZIP 0x50 /* bulk only */
+/* #define US_PR_BULK ?? */
+
+/*
+ * Bulk only data structures (Zip 100, for example)
+ */
+
+struct bulk_cb_wrap {
+ __u32 Signature; /* contains 'USBC' */
+ __u32 Tag; /* unique per command id */
+ __u32 DataTransferLength; /* size of data */
+ __u8 Flags; /* direction in bit 0 */
+ __u8 Lun; /* LUN normally 0 */
+ __u8 Length; /* of of the CDB */
+ __u8 CDB[16]; /* max command */
+};
+
+#define US_BULK_CB_WRAP_LEN 31
+#define US_BULK_CB_SIGN 0x43425355
+#define US_BULK_FLAG_IN 1
+#define US_BULK_FLAG_OUT 0
+
+struct bulk_cs_wrap {
+ __u32 Signature; /* should = 'USBS' */
+ __u32 Tag; /* same as original command */
+ __u32 Residue; /* amount not transferred */
+ __u8 Status; /* see below */
+ __u8 Filler[18];
+};
+
+#define US_BULK_CS_WRAP_LEN 31
+#define US_BULK_CS_SIGN 0x53425355
+#define US_BULK_STAT_OK 0
+#define US_BULK_STAT_FAIL 1
+#define US_BULK_STAT_PHASE 2
+
+#define US_BULK_RESET 0xff
+#define US_BULK_RESET_SOFT 1
+#define US_BULK_RESET_HARD 0
+
+/*
+ * CBI style
+ */
+
+#define US_CBI_ADSC 0
+
+/*
+ * Filter device definitions
+ */
+struct usb_scsi_filter {
+
+ struct usb_scsi_filter * next; /* usb_scsi driver only */
+ char *name; /* not really required */
+
+ unsigned int flags; /* Filter flags */
+ void * (* probe) (struct usb_device *, char *, char *, char *); /* probe device */
+ void (* release)(void *); /* device gone */
+ int (* command)(void *, Scsi_Cmnd *); /* all commands */
+};
+
+#define GUID(x) __u32 x[3]
+#define GUID_EQUAL(x, y) (x[0] == y[0] && x[1] == y[1] && x[2] == y[2])
+#define GUID_CLEAR(x) x[0] = x[1] = x[2] = 0;
+#define GUID_NONE(x) (!x[0] && !x[1] && !x[2])
+#define GUID_FORMAT "%08x%08x%08x"
+#define GUID_ARGS(x) x[0], x[1], x[2]
+
+static inline void make_guid( __u32 *pg, __u16 vendor, __u16 product, char *serial)
+{
+ pg[0] = (vendor << 16) | product;
+ pg[1] = pg[2] = 0;
+ while (*serial) {
+ pg[1] <<= 4;
+ pg[1] |= pg[2] >> 28;
+ pg[2] <<= 4;
+ if (*serial >= 'a')
+ *serial -= 'a' - 'A';
+ pg[2] |= (*serial <= '9' && *serial >= '0') ? *serial - '0'
+ : *serial - 'A' + 10;
+ serial++;
+ }
+}
+
+/* Flag definitions */
+#define US_FL_IP_STATUS 0x00000001 /* status uses interrupt */
+#define US_FL_FIXED_COMMAND 0x00000002 /* expand commands to fixed size */
+
+/*
+ * Called by filters to register/unregister the mini driver
+ *
+ * WARNING - the supplied probe function may be called before exiting this fn
+ */
+int usb_scsi_register(struct usb_scsi_filter *);
+void usb_scsi_deregister(struct usb_scsi_filter *);
+
+#ifdef CONFIG_USB_HP4100
+int hp4100_init(void);
+#endif
diff --git a/drivers/usb/usb_scsi_debug.c b/drivers/usb/usb_scsi_debug.c
new file mode 100644
index 000000000..2ca847c08
--- /dev/null
+++ b/drivers/usb/usb_scsi_debug.c
@@ -0,0 +1,104 @@
+
+/* Driver for USB scsi like devices
+ *
+ * (C) Michael Gee (michael@linuxspecific.com) 1999
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/malloc.h>
+
+#include <asm/spinlock.h>
+
+#include <linux/blk.h>
+#include "../scsi/scsi.h"
+#include "../scsi/hosts.h"
+#include "../scsi/sd.h"
+
+#include "usb.h"
+#include "usb_scsi.h"
+
+void us_show_command(Scsi_Cmnd *srb)
+{
+ char *what;
+
+ switch (srb->cmnd[0]) {
+ case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break;
+ case REZERO_UNIT: what = "REZERO_UNIT"; break;
+ case REQUEST_SENSE: what = "REQUEST_SENSE"; break;
+ case FORMAT_UNIT: what = "FORMAT_UNIT"; break;
+ case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break;
+ case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break;
+ case READ_6: what = "READ_6"; break;
+ case WRITE_6: what = "WRITE_6"; break;
+ case SEEK_6: what = "SEEK_6"; break;
+ case READ_REVERSE: what = "READ_REVERSE"; break;
+ case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break;
+ case SPACE: what = "SPACE"; break;
+ case INQUIRY: what = "INQUIRY"; break;
+ case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break;
+ case MODE_SELECT: what = "MODE_SELECT"; break;
+ case RESERVE: what = "RESERVE"; break;
+ case RELEASE: what = "RELEASE"; break;
+ case COPY: what = "COPY"; break;
+ case ERASE: what = "ERASE"; break;
+ case MODE_SENSE: what = "MODE_SENSE"; break;
+ case START_STOP: what = "START_STOP"; break;
+ case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break;
+ case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break;
+ case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break;
+ case SET_WINDOW: what = "SET_WINDOW"; break;
+ case READ_CAPACITY: what = "READ_CAPACITY"; break;
+ case READ_10: what = "READ_10"; break;
+ case WRITE_10: what = "WRITE_10"; break;
+ case SEEK_10: what = "SEEK_10"; break;
+ case WRITE_VERIFY: what = "WRITE_VERIFY"; break;
+ case VERIFY: what = "VERIFY"; break;
+ case SEARCH_HIGH: what = "SEARCH_HIGH"; break;
+ case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break;
+ case SEARCH_LOW: what = "SEARCH_LOW"; break;
+ case SET_LIMITS: what = "SET_LIMITS"; break;
+ case READ_POSITION: what = "READ_POSITION"; break;
+ case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break;
+ case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break;
+ case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break;
+ case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break;
+ case COMPARE: what = "COMPARE"; break;
+ case COPY_VERIFY: what = "COPY_VERIFY"; break;
+ case WRITE_BUFFER: what = "WRITE_BUFFER"; break;
+ case READ_BUFFER: what = "READ_BUFFER"; break;
+ case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break;
+ case READ_LONG: what = "READ_LONG"; break;
+ case WRITE_LONG: what = "WRITE_LONG"; break;
+ case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break;
+ case WRITE_SAME: what = "WRITE_SAME"; break;
+ case READ_TOC: what = "READ_TOC"; break;
+ case LOG_SELECT: what = "LOG_SELECT"; break;
+ case LOG_SENSE: what = "LOG_SENSE"; break;
+ case MODE_SELECT_10: what = "MODE_SELECT_10"; break;
+ case MODE_SENSE_10: what = "MODE_SENSE_10"; break;
+ case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break;
+ case READ_12: what = "READ_12"; break;
+ case WRITE_12: what = "WRITE_12"; break;
+ case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break;
+ case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break;
+ case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break;
+ case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break;
+ case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break;
+ case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break;
+ case WRITE_LONG_2: what = "WRITE_LONG_2"; break;
+ default: what = "??"; break;
+ }
+ printk(KERN_DEBUG USB_SCSI "Command %s (%d bytes)\n", what, srb->cmd_len);
+ printk(KERN_DEBUG USB_SCSI " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5],
+ srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]);
+}
diff --git a/drivers/usb/usb_scsi_dt.c b/drivers/usb/usb_scsi_dt.c
new file mode 100644
index 000000000..5d615bdff
--- /dev/null
+++ b/drivers/usb/usb_scsi_dt.c
@@ -0,0 +1,4 @@
+0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
+0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
diff --git a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c
new file mode 100644
index 000000000..c24288124
--- /dev/null
+++ b/drivers/video/cyber2000fb.c
@@ -0,0 +1,1057 @@
+/*
+ * linux/drivers/video/cyber2000fb.c
+ *
+ * Integraphics Cyber2000 frame buffer device
+ *
+ * Based on cyberfb.c
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <video/fbcon.h>
+#include <video/fbcon-cfb8.h>
+#include <video/fbcon-cfb16.h>
+#include <video/fbcon-cfb24.h>
+
+/*
+ * Some defaults
+ */
+#define DEFAULT_XRES 640
+#define DEFAULT_YRES 480
+#define DEFAULT_BPP 8
+
+static volatile unsigned char *CyberRegs;
+
+#include "cyber2000fb.h"
+
+static struct display global_disp;
+static struct fb_info fb_info;
+static struct cyber2000fb_par current_par;
+static struct display_switch *dispsw;
+static struct fb_var_screeninfo __initdata init_var = {};
+
+#ifdef DEBUG
+static void debug_printf(char *fmt, ...)
+{
+ char buffer[128];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsprintf(buffer, fmt, ap);
+ va_end(ap);
+
+ printascii(buffer);
+}
+#else
+#define debug_printf(x...) do { } while (0)
+#endif
+
+/*
+ * Predefined Video Modes
+ */
+static const struct res cyber2000_res[] = {
+ {
+ 640, 480,
+ {
+ 0x5f, 0x4f, 0x50, 0x80, 0x52, 0x9d, 0x0b, 0x3e,
+ 0x00, 0x40,
+ 0xe9, 0x8b, 0xdf, 0x50, 0x00, 0xe6, 0x04, 0xc3
+ },
+ 0x00,
+ { 0xd2, 0xce, 0xdb, 0x54 }
+ },
+
+ {
+ 800, 600,
+ {
+ 0x7f, 0x63, 0x64, 0x00, 0x66, 0x10, 0x6f, 0xf0,
+ 0x00, 0x60,
+ 0x5b, 0x8f, 0x57, 0x64, 0x00, 0x59, 0x6e, 0xe3
+ },
+ 0x00,
+ { 0x52, 0x85, 0xdb, 0x54 }
+ },
+
+ {
+ 1024, 768,
+ {
+ 0x9f, 0x7f, 0x80, 0x80, 0x8b, 0x94, 0x1e, 0xfd,
+ 0x00, 0x60,
+ 0x03, 0x86, 0xff, 0x80, 0x0f, 0x00, 0x1e, 0xe3
+ },
+ 0x00,
+ { 0xd0, 0x52, 0xdb, 0x54 }
+ },
+#if 0
+ {
+ 1152, 886,
+ {
+ },
+ {
+ }
+ },
+#endif
+ {
+ 1280, 1024,
+ {
+ 0xce, 0x9f, 0xa0, 0x8f, 0xa2, 0x1f, 0x28, 0x52,
+ 0x00, 0x40,
+ 0x08, 0x8f, 0xff, 0xa0, 0x00, 0x03, 0x27, 0xe3
+ },
+ 0x1d,
+ { 0xb4, 0x4b, 0xdb, 0x54 }
+ },
+
+ {
+ 1600, 1200,
+ {
+ 0xff, 0xc7, 0xc9, 0x9f, 0xcf, 0xa0, 0xfe, 0x10,
+ 0x00, 0x40,
+ 0xcf, 0x89, 0xaf, 0xc8, 0x00, 0xbc, 0xf1, 0xe3
+ },
+ 0x1f,
+ { 0xbd, 0x10, 0xdb, 0x54 }
+ }
+};
+
+#define NUM_TOTAL_MODES arraysize(cyber2000_res)
+
+static const char igs_regs[] = {
+ 0x10, 0x10, 0x12, 0x00, 0x13, 0x00,
+ 0x30, 0x21, 0x31, 0x00, 0x32, 0x00, 0x33, 0x01,
+ 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00,
+ 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x01,
+ 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00,
+ 0x70, 0x0b, 0x71, 0x10, 0x72, 0x45, 0x73, 0x30,
+ 0x74, 0x1b, 0x75, 0x1e, 0x76, 0x00, 0x7a, 0xc8
+};
+
+static const char crtc_idx[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
+};
+
+static void cyber2000_init_hw(const struct res *res)
+{
+ int i;
+
+ debug_printf("init vga hw for %dx%d\n", res->xres, res->yres);
+
+ cyber2000_outb(0xef, 0x3c2);
+ cyber2000_crtcw(0x0b, 0x11);
+ cyber2000_attrw(0x00, 0x11);
+
+ cyber2000_seqw(0x01, 0x00);
+ cyber2000_seqw(0x01, 0x01);
+ cyber2000_seqw(0x0f, 0x02);
+ cyber2000_seqw(0x00, 0x03);
+ cyber2000_seqw(0x0e, 0x04);
+ cyber2000_seqw(0x03, 0x00);
+
+ for (i = 0; i < sizeof(crtc_idx); i++)
+ cyber2000_crtcw(res->crtc_regs[i], crtc_idx[i]);
+
+ for (i = 0x0a; i < 0x10; i++)
+ cyber2000_crtcw(0, i);
+
+ cyber2000_crtcw(0xff, 0x18);
+
+ cyber2000_grphw(0x00, 0x00);
+ cyber2000_grphw(0x00, 0x01);
+ cyber2000_grphw(0x00, 0x02);
+ cyber2000_grphw(0x00, 0x03);
+ cyber2000_grphw(0x00, 0x04);
+ cyber2000_grphw(0x60, 0x05);
+ cyber2000_grphw(0x05, 0x06);
+ cyber2000_grphw(0x0f, 0x07);
+ cyber2000_grphw(0xff, 0x08);
+
+ for (i = 0; i < 16; i++)
+ cyber2000_attrw(i, i);
+
+ cyber2000_attrw(0x01, 0x10);
+ cyber2000_attrw(0x00, 0x11);
+ cyber2000_attrw(0x0f, 0x12);
+ cyber2000_attrw(0x00, 0x13);
+ cyber2000_attrw(0x00, 0x14);
+
+ for (i = 0; i < sizeof(igs_regs); i += 2)
+ cyber2000_grphw(igs_regs[i+1], igs_regs[i]);
+
+ cyber2000_grphw(res->crtc_ofl, 0x11);
+
+ for (i = 0; i < 4; i += 1)
+ cyber2000_grphw(res->clk_regs[i], 0xb0 + i);
+
+ cyber2000_grphw(0x01, 0x90);
+ cyber2000_grphw(0x80, 0xb9);
+ cyber2000_grphw(0x00, 0xb9);
+
+ cyber2000_outb(0x56, 0x3ce);
+ i = cyber2000_inb(0x3cf);
+ cyber2000_outb(i | 4, 0x3cf);
+ cyber2000_outb(0x04, 0x3c6);
+ cyber2000_outb(i, 0x3cf);
+
+ cyber2000_outb(0x20, 0x3c0);
+ cyber2000_outb(0xff, 0x3c6);
+
+ for (i = 0; i < 256; i++) {
+ cyber2000_outb(i, 0x3c8);
+ cyber2000_outb(0, 0x3c9);
+ cyber2000_outb(0, 0x3c9);
+ cyber2000_outb(0, 0x3c9);
+ }
+}
+
+
+static struct fb_ops cyber2000fb_ops;
+
+/* -------------------- Hardware specific routines ------------------------- */
+
+/*
+ * Hardware Cyber2000 Acceleration
+ */
+static void cyber2000_accel_wait(void)
+{
+ int count = 10000;
+
+ while (cyber2000_inb(0xbf011) & 0x80) {
+ if (!count--) {
+ debug_printf("accel_wait timed out\n");
+ cyber2000_outb(0, 0xbf011);
+ return;
+ }
+ udelay(10);
+ }
+}
+
+static void
+cyber2000_accel_setup(struct display *p)
+{
+ dispsw->setup(p);
+}
+
+static void
+cyber2000_accel_bmove(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width)
+{
+ unsigned long src, dst, chwidth = p->var.xres_virtual * fontheight(p);
+ int v = 0x8000;
+
+ if (sx < dx) {
+ sx += width - 1;
+ dx += width - 1;
+ v |= 4;
+ }
+
+ if (sy < dy) {
+ sy += height - 1;
+ dy += height - 1;
+ v |= 2;
+ }
+
+ sx *= fontwidth(p);
+ dx *= fontwidth(p);
+ src = sx + sy * chwidth;
+ dst = dx + dy * chwidth;
+ width = width * fontwidth(p) - 1;
+ height = height * fontheight(p) - 1;
+
+ cyber2000_accel_wait();
+ cyber2000_outb(0x00, 0xbf011);
+ cyber2000_outb(0x03, 0xbf048);
+ cyber2000_outw(width, 0xbf060);
+
+ if (p->var.bits_per_pixel != 24) {
+ cyber2000_outl(dst, 0xbf178);
+ cyber2000_outl(src, 0xbf170);
+ } else {
+ cyber2000_outl(dst * 3, 0xbf178);
+ cyber2000_outb(dst, 0xbf078);
+ cyber2000_outl(src * 3, 0xbf170);
+ }
+
+ cyber2000_outw(height, 0xbf062);
+ cyber2000_outw(v, 0xbf07c);
+ cyber2000_outw(0x2800, 0xbf07e);
+}
+
+static void
+cyber2000_accel_clear(struct vc_data *conp, struct display *p, int sy, int sx,
+ int height, int width)
+{
+ unsigned long dst;
+ u32 bgx = attr_bgcol_ec(p, conp);
+
+ dst = sx * fontwidth(p) + sy * p->var.xres_virtual * fontheight(p);
+ width = width * fontwidth(p) - 1;
+ height = height * fontheight(p) - 1;
+
+ cyber2000_accel_wait();
+ cyber2000_outb(0x00, 0xbf011);
+ cyber2000_outb(0x03, 0xbf048);
+ cyber2000_outw(width, 0xbf060);
+ cyber2000_outw(height, 0xbf062);
+
+ switch (p->var.bits_per_pixel) {
+ case 16:
+ bgx = ((u16 *)p->dispsw_data)[bgx];
+ case 8:
+ cyber2000_outl(dst, 0xbf178);
+ break;
+
+ case 24:
+ cyber2000_outl(dst * 3, 0xbf178);
+ cyber2000_outb(dst, 0xbf078);
+ bgx = ((u32 *)p->dispsw_data)[bgx];
+ break;
+ }
+
+ cyber2000_outl(bgx, 0xbf058);
+ cyber2000_outw(0x8000, 0xbf07c);
+ cyber2000_outw(0x0800, 0xbf07e);
+}
+
+static void
+cyber2000_accel_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx)
+{
+ cyber2000_accel_wait();
+ dispsw->putc(conp, p, c, yy, xx);
+}
+
+static void
+cyber2000_accel_putcs(struct vc_data *conp, struct display *p,
+ const unsigned short *s, int count, int yy, int xx)
+{
+ cyber2000_accel_wait();
+ dispsw->putcs(conp, p, s, count, yy, xx);
+}
+
+static void
+cyber2000_accel_revc(struct display *p, int xx, int yy)
+{
+ cyber2000_accel_wait();
+ dispsw->revc(p, xx, yy);
+}
+
+static void
+cyber2000_accel_clear_margins(struct vc_data *conp, struct display *p, int bottom_only)
+{
+ dispsw->clear_margins(conp, p, bottom_only);
+}
+
+static struct display_switch fbcon_cyber_accel = {
+ cyber2000_accel_setup,
+ cyber2000_accel_bmove,
+ cyber2000_accel_clear,
+ cyber2000_accel_putc,
+ cyber2000_accel_putcs,
+ cyber2000_accel_revc,
+ NULL,
+ NULL,
+ cyber2000_accel_clear_margins,
+ FONTWIDTH(8)|FONTWIDTH(16)
+};
+
+/*
+ * Palette
+ */
+static int
+cyber2000_getcolreg(u_int regno, u_int * red, u_int * green, u_int * blue,
+ u_int * transp, struct fb_info *fb_info)
+{
+ int t;
+
+ if (regno >= 256)
+ return 1;
+
+ t = current_par.palette[regno].red;
+ *red = t << 10 | t << 4 | t >> 2;
+
+ t = current_par.palette[regno].green;
+ *green = t << 10 | t << 4 | t >> 2;
+
+ t = current_par.palette[regno].blue;
+ *blue = t << 10 | t << 4 | t >> 2;
+
+ *transp = 0;
+
+ return 0;
+}
+
+/*
+ * Set a single color register. Return != 0 for invalid regno.
+ */
+static int
+cyber2000_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *fb_info)
+{
+ if (regno > 255)
+ return 1;
+
+ red >>= 10;
+ green >>= 10;
+ blue >>= 10;
+
+ current_par.palette[regno].red = red;
+ current_par.palette[regno].green = green;
+ current_par.palette[regno].blue = blue;
+
+ switch (fb_display[current_par.currcon].var.bits_per_pixel) {
+ case 8:
+ cyber2000_outb(regno, 0x3c8);
+ cyber2000_outb(red, 0x3c9);
+ cyber2000_outb(green, 0x3c9);
+ cyber2000_outb(blue, 0x3c9);
+ break;
+
+#ifdef FBCON_HAS_CFB16
+ case 16:
+ if (regno < 64) {
+ /* write green */
+ cyber2000_outb(regno << 2, 0x3c8);
+ cyber2000_outb(current_par.palette[regno >> 1].red, 0x3c9);
+ cyber2000_outb(green, 0x3c9);
+ cyber2000_outb(current_par.palette[regno >> 1].blue, 0x3c9);
+ }
+
+ if (regno < 32) {
+ /* write red,blue */
+ cyber2000_outb(regno << 3, 0x3c8);
+ cyber2000_outb(red, 0x3c9);
+ cyber2000_outb(current_par.palette[regno << 1].green, 0x3c9);
+ cyber2000_outb(blue, 0x3c9);
+ }
+
+ if (regno < 16)
+ current_par.c_table.cfb16[regno] = regno | regno << 5 | regno << 11;
+ break;
+#endif
+
+#ifdef FBCON_HAS_CFB24
+ case 24:
+ cyber2000_outb(regno, 0x3c8);
+ cyber2000_outb(red, 0x3c9);
+ cyber2000_outb(green, 0x3c9);
+ cyber2000_outb(blue, 0x3c9);
+
+ if (regno < 16)
+ current_par.c_table.cfb24[regno] = regno | regno << 8 | regno << 16;
+ break;
+#endif
+
+ default:
+ return 1;
+ }
+
+ return 0;
+}
+
+static int cyber2000fb_set_timing(struct fb_var_screeninfo *var)
+{
+ int width = var->xres_virtual;
+ int scr_pitch, fetchrow;
+ int i;
+ char b, col;
+
+ switch (var->bits_per_pixel) {
+ case 8: /* PSEUDOCOLOUR, 256 */
+ b = 0;
+ col = 1;
+ scr_pitch = var->xres_virtual / 8;
+ break;
+
+ case 16:/* DIRECTCOLOUR, 64k */
+ b = 1;
+ col = 2;
+ scr_pitch = var->xres_virtual / 8 * 2;
+ break;
+ case 24:/* TRUECOLOUR, 16m */
+ b = 2;
+ col = 4;
+ scr_pitch = var->xres_virtual / 8 * 3;
+ width *= 3;
+ break;
+
+ default:
+ return 1;
+ }
+
+ for (i = 0; i < NUM_TOTAL_MODES; i++)
+ if (var->xres == cyber2000_res[i].xres &&
+ var->yres == cyber2000_res[i].yres)
+ break;
+
+ if (i < NUM_TOTAL_MODES)
+ cyber2000_init_hw(cyber2000_res + i);
+
+ fetchrow = scr_pitch + 1;
+
+ debug_printf("Setting regs: pitch=%X, fetchrow=%X, col=%X, b=%X\n",
+ scr_pitch, fetchrow, col, b);
+
+ cyber2000_outb(0x13, 0x3d4);
+ cyber2000_outb(scr_pitch, 0x3d5);
+ cyber2000_outb(0x14, 0x3ce);
+ cyber2000_outb(fetchrow, 0x3cf);
+ cyber2000_outb(0x15, 0x3ce);
+ /* FIXME: is this the right way round? */
+ cyber2000_outb(((fetchrow >> 4) & 0xf0) | ((scr_pitch >> 8) & 0x0f), 0x3cf);
+ cyber2000_outb(0x77, 0x3ce);
+ cyber2000_outb(col, 0x3cf);
+
+
+ cyber2000_outb(0x33, 0x3ce);
+ cyber2000_outb(0x1c, 0x3cf);
+
+ cyber2000_outw(width - 1, 0xbf018);
+ cyber2000_outw(width - 1, 0xbf218);
+ cyber2000_outb(b, 0xbf01c);
+
+ return 0;
+}
+
+static inline void
+cyber2000fb_update_start(struct fb_var_screeninfo *var)
+{
+#if 0
+ unsigned int base;
+
+ base = var->yoffset * var->xres_virtual + var->xoffset;
+
+ cyber2000_outb(0x0c, 0x3d4);
+ cyber2000_outb(base, 0x3d5);
+ cyber2000_outb(0x0d, 0x3d4);
+ cyber2000_outb(base >> 8, 0x3d5);
+ /* FIXME: need the upper bits of the start offset */
+/* cyber2000_outb(0x??, 0x3d4);
+ cyber2000_outb(base >> 16, 0x3d5);*/
+#endif
+}
+
+/*
+ * Open/Release the frame buffer device
+ */
+static int cyber2000fb_open(struct fb_info *info, int user)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int cyber2000fb_release(struct fb_info *info, int user)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * Get the Colormap
+ */
+static int
+cyber2000fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info)
+{
+ int err = 0;
+
+ if (con == current_par.currcon) /* current console? */
+ err = fb_get_cmap(cmap, kspc, cyber2000_getcolreg, info);
+ else if (fb_display[con].cmap.len) /* non default colormap? */
+ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+ else
+ fb_copy_cmap(fb_default_cmap(1 << fb_display[con].var.bits_per_pixel),
+ cmap, kspc ? 0 : 2);
+ return err;
+}
+
+
+/*
+ * Set the Colormap
+ */
+static int
+cyber2000fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info)
+{
+ struct display *disp = &fb_display[con];
+ int err = 0;
+
+ if (!disp->cmap.len) { /* no colormap allocated? */
+ int size;
+
+ if (disp->var.bits_per_pixel == 16)
+ size = 32;
+ else
+ size = 256;
+
+ err = fb_alloc_cmap(&disp->cmap, size, 0);
+ }
+ if (!err) {
+ if (con == current_par.currcon) /* current console? */
+ err = fb_set_cmap(cmap, kspc, cyber2000_setcolreg,
+ info);
+ else
+ fb_copy_cmap(cmap, &disp->cmap, kspc ? 0 : 1);
+ }
+
+ return err;
+}
+
+static int
+cyber2000fb_decode_var(struct fb_var_screeninfo *var, int con, int *visual)
+{
+ switch (var->bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
+ case 8:
+ *visual = FB_VISUAL_PSEUDOCOLOR;
+ break;
+#endif
+#ifdef FBCON_HAS_CFB16
+ case 16:
+ *visual = FB_VISUAL_DIRECTCOLOR;
+ break;
+#endif
+#ifdef FBCON_HAS_CFB24
+ case 24:
+ *visual = FB_VISUAL_TRUECOLOR;
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Get the Fixed Part of the Display
+ */
+static int
+cyber2000fb_get_fix(struct fb_fix_screeninfo *fix, int con,
+ struct fb_info *fb_info)
+{
+ struct display *display;
+
+ memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+ strcpy(fix->id, "Cyber2000");
+
+ if (con >= 0)
+ display = fb_display + con;
+ else
+ display = &global_disp;
+
+ fix->smem_start = (char *)current_par.screen_base_p;
+ fix->smem_len = current_par.screen_size;
+ fix->mmio_start = (char *)current_par.regs_base_p;
+ fix->mmio_len = 0x000c0000;
+ fix->type = display->type;
+ fix->type_aux = display->type_aux;
+ fix->xpanstep = 0;
+ fix->ypanstep = display->ypanstep;
+ fix->ywrapstep = display->ywrapstep;
+ fix->visual = display->visual;
+ fix->line_length = display->line_length;
+ fix->accel = 22; /*FB_ACCEL_IGS_CYBER2000*/
+
+ return 0;
+}
+
+
+/*
+ * Get the User Defined Part of the Display
+ */
+static int
+cyber2000fb_get_var(struct fb_var_screeninfo *var, int con,
+ struct fb_info *fb_info)
+{
+ if (con == -1)
+ *var = global_disp.var;
+ else
+ *var = fb_display[con].var;
+
+ return 0;
+}
+
+/*
+ * Set the User Defined Part of the Display
+ */
+static int
+cyber2000fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+{
+ struct display *display;
+ int err, chgvar = 0, visual;
+
+ if (con >= 0)
+ display = fb_display + con;
+ else
+ display = &global_disp;
+
+ err = cyber2000fb_decode_var(var, con, &visual);
+ if (err)
+ return err;
+
+ switch (var->activate & FB_ACTIVATE_MASK) {
+ case FB_ACTIVATE_TEST:
+ return 0;
+
+ case FB_ACTIVATE_NXTOPEN:
+ case FB_ACTIVATE_NOW:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (con >= 0) {
+ if (display->var.xres != var->xres)
+ chgvar = 1;
+ if (display->var.yres != var->yres)
+ chgvar = 1;
+ if (display->var.xres_virtual != var->xres_virtual)
+ chgvar = 1;
+ if (display->var.yres_virtual != var->yres_virtual)
+ chgvar = 1;
+ if (display->var.accel_flags != var->accel_flags)
+ chgvar = 1;
+ if (memcmp(&display->var.red, &var->red, sizeof(var->red)))
+ chgvar = 1;
+ if (memcmp(&display->var.green, &var->green, sizeof(var->green)))
+ chgvar = 1;
+ if (memcmp(&display->var.blue, &var->blue, sizeof(var->green)))
+ chgvar = 1;
+ }
+
+ display->var = *var;
+
+ display->screen_base = (char *)current_par.screen_base;
+ display->visual = visual;
+ display->type = FB_TYPE_PACKED_PIXELS;
+ display->type_aux = 0;
+ display->ypanstep = 0;
+ display->ywrapstep = 0;
+ display->line_length =
+ display->next_line = (var->xres_virtual * var->bits_per_pixel) / 8;
+ display->can_soft_blank = 1;
+ display->inverse = 0;
+
+ switch (display->var.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
+ case 8:
+ dispsw = &fbcon_cfb8;
+ display->dispsw_data = NULL;
+ break;
+#endif
+#ifdef FBCON_HAS_CFB16
+ case 16:
+ dispsw = &fbcon_cfb16;
+ display->dispsw_data = current_par.c_table.cfb16;
+ break;
+#endif
+#ifdef FBCON_HAS_CFB24
+ case 24:
+ dispsw = &fbcon_cfb24;
+ display->dispsw_data = current_par.c_table.cfb24;
+ break;
+#endif
+ default:
+ printk(KERN_WARNING "cyber2000: no support for %dbpp\n",
+ display->var.bits_per_pixel);
+ dispsw = &fbcon_dummy;
+ break;
+ }
+
+ if (display->var.accel_flags & FB_ACCELF_TEXT &&
+ dispsw != &fbcon_dummy)
+ display->dispsw = &fbcon_cyber_accel;
+ else
+ display->dispsw = dispsw;
+
+ if (chgvar && info && info->changevar)
+ info->changevar(con);
+
+ if (con == current_par.currcon) {
+ struct fb_cmap *cmap;
+
+ cyber2000fb_update_start(var);
+ cyber2000fb_set_timing(var);
+
+ if (display->cmap.len)
+ cmap = &display->cmap;
+ else
+ cmap = fb_default_cmap(current_par.palette_size);
+
+ fb_set_cmap(cmap, 1, cyber2000_setcolreg, info);
+ }
+ return 0;
+}
+
+
+/*
+ * Pan or Wrap the Display
+ */
+static int cyber2000fb_pan_display(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info)
+{
+ u_int y_bottom;
+
+ y_bottom = var->yoffset;
+
+ if (!(var->vmode & FB_VMODE_YWRAP))
+ y_bottom += var->yres;
+
+ if (var->xoffset > (var->xres_virtual - var->xres))
+ return -EINVAL;
+ if (y_bottom > fb_display[con].var.yres_virtual)
+ return -EINVAL;
+return -EINVAL;
+
+ cyber2000fb_update_start(var);
+
+ fb_display[con].var.xoffset = var->xoffset;
+ fb_display[con].var.yoffset = var->yoffset;
+ if (var->vmode & FB_VMODE_YWRAP)
+ fb_display[con].var.vmode |= FB_VMODE_YWRAP;
+ else
+ fb_display[con].var.vmode &= ~FB_VMODE_YWRAP;
+
+ return 0;
+}
+
+
+static int cyber2000fb_ioctl(struct inode *inode, struct file *file,
+ u_int cmd, u_long arg, int con, struct fb_info *info)
+{
+ return -EINVAL;
+}
+
+
+/*
+ * Update the `var' structure (called by fbcon.c)
+ *
+ * This call looks only at yoffset and the FB_VMODE_YWRAP flag in `var'.
+ * Since it's called by a kernel driver, no range checking is done.
+ */
+static int
+cyber2000fb_updatevar(int con, struct fb_info *info)
+{
+ if (con == current_par.currcon)
+ cyber2000fb_update_start(&fb_display[con].var);
+ return 0;
+}
+
+static int
+cyber2000fb_switch(int con, struct fb_info *info)
+{
+ struct fb_cmap *cmap;
+
+ if (current_par.currcon >= 0) {
+ cmap = &fb_display[current_par.currcon].cmap;
+
+ if (cmap->len)
+ fb_get_cmap(cmap, 1, cyber2000_getcolreg, info);
+ }
+
+ current_par.currcon = con;
+
+ fb_display[con].var.activate = FB_ACTIVATE_NOW;
+
+ cyber2000fb_set_var(&fb_display[con].var, con, info);
+
+ return 0;
+}
+
+/*
+ * (Un)Blank the display.
+ */
+static void cyber2000fb_blank(int blank, struct fb_info *fb_info)
+{
+ int i;
+
+ if (blank) {
+ for (i = 0; i < 256; i++) {
+ cyber2000_outb(i, 0x3c8);
+ cyber2000_outb(0, 0x3c9);
+ cyber2000_outb(0, 0x3c9);
+ cyber2000_outb(0, 0x3c9);
+ }
+ } else {
+ for (i = 0; i < 256; i++) {
+ cyber2000_outb(i, 0x3c8);
+ cyber2000_outb(current_par.palette[i].red, 0x3c9);
+ cyber2000_outb(current_par.palette[i].green, 0x3c9);
+ cyber2000_outb(current_par.palette[i].blue, 0x3c9);
+ }
+ }
+}
+
+__initfunc(void cyber2000fb_setup(char *options, int *ints))
+{
+}
+
+static struct fb_ops cyber2000fb_ops =
+{
+ cyber2000fb_open,
+ cyber2000fb_release,
+ cyber2000fb_get_fix,
+ cyber2000fb_get_var,
+ cyber2000fb_set_var,
+ cyber2000fb_get_cmap,
+ cyber2000fb_set_cmap,
+ cyber2000fb_pan_display,
+ cyber2000fb_ioctl
+};
+
+__initfunc(static void
+cyber2000fb_init_fbinfo(void))
+{
+ static int first = 1;
+
+ if (!first)
+ return;
+ first = 0;
+
+ strcpy(fb_info.modename, "Cyber2000");
+ strcpy(fb_info.fontname, "Acorn8x8");
+
+ fb_info.node = -1;
+ fb_info.fbops = &cyber2000fb_ops;
+ fb_info.disp = &global_disp;
+ fb_info.changevar = NULL;
+ fb_info.switch_con = cyber2000fb_switch;
+ fb_info.updatevar = cyber2000fb_updatevar;
+ fb_info.blank = cyber2000fb_blank;
+ fb_info.flags = FBINFO_FLAG_DEFAULT;
+
+ /*
+ * setup initial parameters
+ */
+ memset(&init_var, 0, sizeof(init_var));
+ init_var.xres_virtual =
+ init_var.xres = DEFAULT_XRES;
+ init_var.yres_virtual =
+ init_var.yres = DEFAULT_YRES;
+ init_var.bits_per_pixel = DEFAULT_BPP;
+
+ init_var.red.msb_right = 0;
+ init_var.green.msb_right = 0;
+ init_var.blue.msb_right = 0;
+
+ switch(init_var.bits_per_pixel) {
+ case 8:
+ init_var.bits_per_pixel = 8;
+ init_var.red.offset = 0;
+ init_var.red.length = 8;
+ init_var.green.offset = 0;
+ init_var.green.length = 8;
+ init_var.blue.offset = 0;
+ init_var.blue.length = 8;
+ break;
+
+ case 16:
+ init_var.bits_per_pixel = 16;
+ init_var.red.offset = 11;
+ init_var.red.length = 5;
+ init_var.green.offset = 5;
+ init_var.green.length = 6;
+ init_var.blue.offset = 0;
+ init_var.blue.length = 5;
+ break;
+
+ case 24:
+ init_var.bits_per_pixel = 24;
+ init_var.red.offset = 16;
+ init_var.red.length = 8;
+ init_var.green.offset = 8;
+ init_var.green.length = 8;
+ init_var.blue.offset = 0;
+ init_var.blue.length = 8;
+ break;
+ }
+
+ init_var.nonstd = 0;
+ init_var.activate = FB_ACTIVATE_NOW;
+ init_var.height = -1;
+ init_var.width = -1;
+ init_var.accel_flags = FB_ACCELF_TEXT;
+ init_var.sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT;
+ init_var.vmode = FB_VMODE_NONINTERLACED;
+}
+
+/*
+ * Initialization
+ */
+__initfunc(void cyber2000fb_init(void))
+{
+ struct pci_dev *dev;
+ u_int h_sync, v_sync;
+
+ dev = pci_find_device(PCI_VENDOR_ID_INTERG, 0x2000, NULL);
+ if (!dev)
+ return;
+
+ CyberRegs = bus_to_virt(dev->base_address[0]) + 0x00800000;/*FIXME*/
+
+ cyber2000_outb(0x18, 0x46e8);
+ cyber2000_outb(0x01, 0x102);
+ cyber2000_outb(0x08, 0x46e8);
+
+ cyber2000fb_init_fbinfo();
+
+ current_par.currcon = -1;
+ current_par.screen_base_p = 0x80000000 + dev->base_address[0];
+ current_par.screen_base = (u_int)bus_to_virt(dev->base_address[0]);
+ current_par.screen_size = 0x00200000;
+ current_par.regs_base_p = 0x80800000 + dev->base_address[0];
+
+ cyber2000fb_set_var(&init_var, -1, &fb_info);
+
+ h_sync = 1953125000 / init_var.pixclock;
+ h_sync = h_sync * 512 / (init_var.xres + init_var.left_margin +
+ init_var.right_margin + init_var.hsync_len);
+ v_sync = h_sync / (init_var.yres + init_var.upper_margin +
+ init_var.lower_margin + init_var.vsync_len);
+
+ printk("Cyber2000: %ldkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n",
+ current_par.screen_size >> 10,
+ init_var.xres, init_var.yres,
+ h_sync / 1000, h_sync % 1000, v_sync);
+
+ if (register_framebuffer(&fb_info) < 0)
+ return;
+
+ MOD_INC_USE_COUNT; /* TODO: This driver cannot be unloaded yet */
+}
+
+
+
+#ifdef MODULE
+int init_module(void)
+{
+ cyber2000fb_init();
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ /* Not reached because the usecount will never be
+ decremented to zero */
+ unregister_framebuffer(&fb_info);
+ /* TODO: clean up ... */
+}
+
+#endif /* MODULE */
diff --git a/drivers/video/cyber2000fb.h b/drivers/video/cyber2000fb.h
new file mode 100644
index 000000000..f1e81dfa0
--- /dev/null
+++ b/drivers/video/cyber2000fb.h
@@ -0,0 +1,78 @@
+/*
+ * linux/drivers/video/cyber2000fb.h
+ *
+ * Integraphics Cyber2000 frame buffer device
+ */
+
+#define arraysize(x) (sizeof(x)/sizeof(*(x)))
+#define cyber2000_outb(dat,reg) (CyberRegs[reg] = dat)
+#define cyber2000_outw(dat,reg) (*(unsigned short *)&CyberRegs[reg] = dat)
+#define cyber2000_outl(dat,reg) (*(unsigned long *)&CyberRegs[reg] = dat)
+
+#define cyber2000_inb(reg) (CyberRegs[reg])
+#define cyber2000_inw(reg) (*(unsigned short *)&CyberRegs[reg])
+#define cyber2000_inl(reg) (*(unsigned long *)&CyberRegs[reg])
+
+static inline void cyber2000_crtcw(int val, int reg)
+{
+ cyber2000_outb(reg, 0x3d4);
+ cyber2000_outb(val, 0x3d5);
+}
+
+static inline void cyber2000_grphw(int val, int reg)
+{
+ cyber2000_outb(reg, 0x3ce);
+ cyber2000_outb(val, 0x3cf);
+}
+
+static inline void cyber2000_attrw(int val, int reg)
+{
+ cyber2000_inb(0x3da);
+ cyber2000_outb(reg, 0x3c0);
+ cyber2000_inb(0x3c1);
+ cyber2000_outb(val, 0x3c0);
+}
+
+static inline void cyber2000_seqw(int val, int reg)
+{
+ cyber2000_outb(reg, 0x3c4);
+ cyber2000_outb(val, 0x3c5);
+}
+
+struct cyber2000fb_par {
+ unsigned long screen_base;
+ unsigned long screen_base_p;
+ unsigned long regs_base;
+ unsigned long regs_base_p;
+ unsigned long screen_end;
+ unsigned long screen_size;
+ unsigned int palette_size;
+ signed int currcon;
+ /*
+ * palette
+ */
+ struct {
+ u8 red;
+ u8 green;
+ u8 blue;
+ } palette[256];
+ /*
+ * colour mapping table
+ */
+ union {
+#ifdef FBCON_HAS_CFB16
+ u16 cfb16[16];
+#endif
+#ifdef FBCON_HAS_CFB24
+ u32 cfb24[16];
+#endif
+ } c_table;
+};
+
+struct res {
+ int xres;
+ int yres;
+ unsigned char crtc_regs[18];
+ unsigned char crtc_ofl;
+ unsigned char clk_regs[4];
+};
diff --git a/drivers/video/fbcon-vga-planes.c b/drivers/video/fbcon-vga-planes.c
new file mode 100644
index 000000000..391ceb22e
--- /dev/null
+++ b/drivers/video/fbcon-vga-planes.c
@@ -0,0 +1,364 @@
+/*
+ * linux/drivers/video/fbcon-vga-planes.c -- Low level frame buffer operations
+ * for VGA 4-plane modes
+ *
+ * Copyright 1999 Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
+ * Based on code by Michael Schmitz
+ * Based on the old macfb.c 4bpp code by Alan Cox
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file COPYING in the main directory of this
+ * archive for more details. */
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/string.h>
+#include <linux/fb.h>
+#include <linux/vt_buffer.h>
+
+#include <asm/io.h>
+
+#include <video/fbcon.h>
+#include <video/fbcon-vga-planes.h>
+
+#define GRAPHICS_ADDR_REG 0x3ce /* Graphics address register. */
+#define GRAPHICS_DATA_REG 0x3cf /* Graphics data register. */
+
+#define SET_RESET_INDEX 0 /* Set/Reset Register index. */
+#define ENABLE_SET_RESET_INDEX 1 /* Enable Set/Reset Register index. */
+#define DATA_ROTATE_INDEX 3 /* Data Rotate Register index. */
+#define GRAPHICS_MODE_INDEX 5 /* Graphics Mode Register index. */
+#define BIT_MASK_INDEX 8 /* Bit Mask Register index. */
+
+/* The VGA's weird architecture often requires that we read a byte and
+ write a byte to the same location. It doesn't matter *what* byte
+ we write, however. This is because all the action goes on behind
+ the scenes in the VGA's 32-bit latch register, and reading and writing
+ video memory just invokes latch behavior.
+
+ To avoid race conditions (is this necessary?), reading and writing
+ the memory byte should be done with a single instruction. One
+ suitable instruction is the x86 bitwise OR. The following
+ read-modify-write routine should optimize to one such bitwise
+ OR. */
+static inline void rmw(volatile char *p)
+{
+ *p |= 1;
+}
+
+/* Set the Graphics Mode Register. Bits 0-1 are write mode, bit 3 is
+ read mode. */
+static inline void setmode(int mode)
+{
+ outb(GRAPHICS_MODE_INDEX, GRAPHICS_ADDR_REG);
+ outb(mode, GRAPHICS_DATA_REG);
+}
+
+/* Select the Bit Mask Register. */
+static inline void selectmask(void)
+{
+ outb(BIT_MASK_INDEX, GRAPHICS_ADDR_REG);
+}
+
+/* Set the value of the Bit Mask Register. It must already have been
+ selected with selectmask(). */
+static inline void setmask(int mask)
+{
+ outb(mask, GRAPHICS_DATA_REG);
+}
+
+/* Set the Data Rotate Register. Bits 0-2 are rotate count, bits 3-4
+ are logical operation (0=NOP, 1=AND, 2=OR, 3=XOR). */
+static inline void setop(int op)
+{
+ outb(DATA_ROTATE_INDEX, GRAPHICS_ADDR_REG);
+ outb(op, GRAPHICS_DATA_REG);
+}
+
+/* Set the Enable Set/Reset Register. The code here always uses value
+ 0xf for this register. */
+static inline void setsr(int sr)
+{
+ outb(ENABLE_SET_RESET_INDEX, GRAPHICS_ADDR_REG);
+ outb(sr, GRAPHICS_DATA_REG);
+}
+
+/* Set the Set/Reset Register. */
+static inline void setcolor(int color)
+{
+ outb(SET_RESET_INDEX, GRAPHICS_ADDR_REG);
+ outb(color, GRAPHICS_DATA_REG);
+}
+
+/* Set the value in the Graphics Address Register. */
+static inline void setindex(int index)
+{
+ outb(index, GRAPHICS_ADDR_REG);
+}
+
+void fbcon_vga_planes_setup(struct display *p)
+{
+}
+
+void fbcon_vga_planes_bmove(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width)
+{
+ char *src;
+ char *dest;
+ int line_ofs;
+ int x;
+
+ setmode(1);
+ setop(0);
+ setsr(0xf);
+
+ sy *= fontheight(p);
+ dy *= fontheight(p);
+ height *= fontheight(p);
+
+ if (dy < sy || (dy == sy && dx < sx)) {
+ line_ofs = p->line_length - width;
+ dest = p->screen_base + dx + dy * p->line_length;
+ src = p->screen_base + sx + sy * p->line_length;
+ while (height--) {
+ for (x = 0; x < width; x++)
+ *dest++ = *src++;
+ src += line_ofs;
+ dest += line_ofs;
+ }
+ } else {
+ line_ofs = p->line_length - width;
+ dest = p->screen_base + dx + width + (dy + height - 1) * p->line_length;
+ src = p->screen_base + sx + width + (sy + height - 1) * p->line_length;
+ while (height--) {
+ for (x = 0; x < width; x++)
+ *--dest = *--src;
+ src -= line_ofs;
+ dest -= line_ofs;
+ }
+ }
+}
+
+void fbcon_vga_planes_clear(struct vc_data *conp, struct display *p, int sy, int sx,
+ int height, int width)
+{
+ int line_ofs = p->line_length - width;
+ char *where;
+ int x;
+
+ setmode(0);
+ setop(0);
+ setsr(0xf);
+ setcolor(attr_bgcol_ec(p, conp));
+ selectmask();
+
+ setmask(0xff);
+
+ sy *= fontheight(p);
+ height *= fontheight(p);
+
+ where = p->screen_base + sx + sy * p->line_length;
+ while (height--) {
+ for (x = 0; x < width; x++)
+ *where++ = 0;
+ where += line_ofs;
+ }
+}
+
+void fbcon_ega_planes_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx)
+{
+ int fg = attr_fgcol(p,c);
+ int bg = attr_bgcol(p,c);
+
+ int y;
+ u8 *cdat = p->fontdata + (c & p->charmask) * fontheight(p);
+ char *where = p->screen_base + xx + yy * p->line_length * fontheight(p);
+
+ setmode(0);
+ setop(0);
+ setsr(0xf);
+ setcolor(bg);
+ selectmask();
+
+ setmask(0xff);
+ for (y = 0; y < fontheight(p); y++, where += p->line_length)
+ rmw(where);
+
+ where -= p->line_length * y;
+ setcolor(fg);
+ selectmask();
+ for (y = 0; y < fontheight(p); y++, where += p->line_length)
+ if (cdat[y]) {
+ setmask(cdat[y]);
+ rmw(where);
+ }
+}
+
+void fbcon_vga_planes_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx)
+{
+ int fg = attr_fgcol(p,c);
+ int bg = attr_bgcol(p,c);
+
+ int y;
+ u8 *cdat = p->fontdata + (c & p->charmask) * fontheight(p);
+ char *where = p->screen_base + xx + yy * p->line_length * fontheight(p);
+
+ setmode(2);
+ setop(0);
+ setsr(0xf);
+ setcolor(fg);
+ selectmask();
+
+ setmask(0xff);
+ *where = bg;
+ rmb();
+ *(volatile char*)where; /* fill latches */
+ setmode(3);
+ wmb();
+ for (y = 0; y < fontheight(p); y++, where += p->line_length)
+ *where = cdat[y];
+ wmb();
+}
+
+/* 28.50 in my test */
+void fbcon_ega_planes_putcs(struct vc_data *conp, struct display *p, const unsigned short *s,
+ int count, int yy, int xx)
+{
+ int fg = attr_fgcol(p,scr_readw(s));
+ int bg = attr_bgcol(p,scr_readw(s));
+
+ char *where;
+ int n;
+
+ setmode(2);
+ setop(0);
+ selectmask();
+
+ setmask(0xff);
+ where = p->screen_base + xx + yy * p->line_length * fontheight(p);
+ *where = bg;
+ rmb();
+ *(volatile char*)where;
+ wmb();
+ selectmask();
+ for (n = 0; n < count; n++) {
+ int c = scr_readw(s++) & p->charmask;
+ u8 *cdat = p->fontdata + c * fontheight(p);
+ u8 *end = cdat + fontheight(p);
+
+ while (cdat < end) {
+ outb(*cdat++, GRAPHICS_DATA_REG);
+ wmb();
+ *where = fg;
+ where += p->line_length;
+ }
+ where += 1 - p->line_length * fontheight(p);
+ }
+
+ wmb();
+}
+
+/* 6.96 in my test */
+void fbcon_vga_planes_putcs(struct vc_data *conp, struct display *p, const unsigned short *s,
+ int count, int yy, int xx)
+{
+ int fg = attr_fgcol(p,*s);
+ int bg = attr_bgcol(p,*s);
+
+ char *where;
+ int n;
+
+ setmode(2);
+ setop(0);
+ setsr(0xf);
+ setcolor(fg);
+ selectmask();
+
+ setmask(0xff);
+ where = p->screen_base + xx + yy * p->line_length * fontheight(p);
+ *where = bg;
+ rmb();
+ *(volatile char*)where; /* fill latches with background */
+ setmode(3);
+ wmb();
+ for (n = 0; n < count; n++) {
+ int y;
+ int c = *s++ & p->charmask;
+ u8 *cdat = p->fontdata + (c & p->charmask) * fontheight(p);
+
+ for (y = 0; y < fontheight(p); y++, cdat++) {
+ *where = *cdat;
+ where += p->line_length;
+ }
+ where += 1 - p->line_length * fontheight(p);
+ }
+
+ wmb();
+}
+
+void fbcon_vga_planes_revc(struct display *p, int xx, int yy)
+{
+ char *where = p->screen_base + xx + yy * p->line_length * fontheight(p);
+ int y;
+
+ setmode(0);
+ setop(0x18);
+ setsr(0xf);
+ setcolor(0xf);
+ selectmask();
+
+ setmask(0xff);
+ for (y = 0; y < fontheight(p); y++) {
+ rmw(where);
+ where += p->line_length;
+ }
+}
+
+struct display_switch fbcon_vga_planes = {
+ fbcon_vga_planes_setup, fbcon_vga_planes_bmove, fbcon_vga_planes_clear,
+ fbcon_vga_planes_putc, fbcon_vga_planes_putcs, fbcon_vga_planes_revc,
+ NULL, NULL, NULL, FONTWIDTH(8)
+};
+
+struct display_switch fbcon_ega_planes = {
+ fbcon_vga_planes_setup, fbcon_vga_planes_bmove, fbcon_vga_planes_clear,
+ fbcon_ega_planes_putc, fbcon_ega_planes_putcs, fbcon_vga_planes_revc,
+ NULL, NULL, NULL, FONTWIDTH(8)
+};
+
+#ifdef MODULE
+int init_module(void)
+{
+ return 0;
+}
+
+void cleanup_module(void)
+{}
+#endif /* MODULE */
+
+
+ /*
+ * Visible symbols for modules
+ */
+
+EXPORT_SYMBOL(fbcon_vga_planes);
+EXPORT_SYMBOL(fbcon_vga_planes_setup);
+EXPORT_SYMBOL(fbcon_vga_planes_bmove);
+EXPORT_SYMBOL(fbcon_vga_planes_clear);
+EXPORT_SYMBOL(fbcon_vga_planes_putc);
+EXPORT_SYMBOL(fbcon_vga_planes_putcs);
+EXPORT_SYMBOL(fbcon_vga_planes_revc);
+
+EXPORT_SYMBOL(fbcon_ega_planes);
+EXPORT_SYMBOL(fbcon_ega_planes_putc);
+EXPORT_SYMBOL(fbcon_ega_planes_putcs);
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
+
diff --git a/drivers/video/vga16fb.c b/drivers/video/vga16fb.c
new file mode 100644
index 000000000..7f7b1af4d
--- /dev/null
+++ b/drivers/video/vga16fb.c
@@ -0,0 +1,1064 @@
+/*
+ * linux/drivers/video/vga16.c -- VGA 16-color framebuffer driver
+ *
+ * Copyright 1999 Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
+ * Based on VGA info at http://www.goodnet.com/~tinara/FreeVGA/home.htm
+ * Based on VESA framebuffer (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file COPYING in the main directory of this
+ * archive for more details. */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/console.h>
+#include <linux/selection.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+
+#include <video/fbcon.h>
+#include <video/fbcon-vga-planes.h>
+
+#define dac_reg (0x3c8)
+#define dac_val (0x3c9)
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * card parameters
+ */
+
+static struct vga16fb_info {
+ struct fb_info fb_info;
+ char *video_vbase; /* 0xa0000 map address */
+ int isVGA;
+
+ /* structure holding original VGA register settings when the
+ screen is blanked */
+ struct {
+ unsigned char SeqCtrlIndex; /* Sequencer Index reg. */
+ unsigned char CrtCtrlIndex; /* CRT-Contr. Index reg. */
+ unsigned char CrtMiscIO; /* Miscellaneous register */
+ unsigned char HorizontalTotal; /* CRT-Controller:00h */
+ unsigned char HorizDisplayEnd; /* CRT-Controller:01h */
+ unsigned char StartHorizRetrace; /* CRT-Controller:04h */
+ unsigned char EndHorizRetrace; /* CRT-Controller:05h */
+ unsigned char Overflow; /* CRT-Controller:07h */
+ unsigned char StartVertRetrace; /* CRT-Controller:10h */
+ unsigned char EndVertRetrace; /* CRT-Controller:11h */
+ unsigned char ModeControl; /* CRT-Controller:17h */
+ unsigned char ClockingMode; /* Seq-Controller:01h */
+ } vga_state;
+
+ int palette_blanked;
+ int vesa_blanked;
+} vga16fb;
+
+/* Some of the code below is taken from SVGAlib. The original,
+ unmodified copyright notice for that code is below. */
+/* VGAlib version 1.2 - (c) 1993 Tommy Frandsen */
+/* */
+/* This library is free software; you can redistribute it and/or */
+/* modify it without any restrictions. This library is distributed */
+/* in the hope that it will be useful, but without any warranty. */
+
+/* Multi-chipset support Copyright 1993 Harm Hanemaayer */
+/* partially copyrighted (C) 1993 by Hartmut Schirmer */
+
+/* VGA data register ports */
+#define CRT_DC 0x3D5 /* CRT Controller Data Register - color emulation */
+#define CRT_DM 0x3B5 /* CRT Controller Data Register - mono emulation */
+#define ATT_R 0x3C1 /* Attribute Controller Data Read Register */
+#define GRA_D 0x3CF /* Graphics Controller Data Register */
+#define SEQ_D 0x3C5 /* Sequencer Data Register */
+#define MIS_R 0x3CC /* Misc Output Read Register */
+#define MIS_W 0x3C2 /* Misc Output Write Register */
+#define IS1_RC 0x3DA /* Input Status Register 1 - color emulation */
+#define IS1_RM 0x3BA /* Input Status Register 1 - mono emulation */
+#define PEL_D 0x3C9 /* PEL Data Register */
+#define PEL_MSK 0x3C6 /* PEL mask register */
+
+/* EGA-specific registers */
+#define GRA_E0 0x3CC /* Graphics enable processor 0 */
+#define GRA_E1 0x3CA /* Graphics enable processor 1 */
+
+
+/* VGA index register ports */
+#define CRT_IC 0x3D4 /* CRT Controller Index - color emulation */
+#define CRT_IM 0x3B4 /* CRT Controller Index - mono emulation */
+#define ATT_IW 0x3C0 /* Attribute Controller Index & Data Write Register */
+#define GRA_I 0x3CE /* Graphics Controller Index */
+#define SEQ_I 0x3C4 /* Sequencer Index */
+#define PEL_IW 0x3C8 /* PEL Write Index */
+#define PEL_IR 0x3C7 /* PEL Read Index */
+
+/* standard VGA indexes max counts */
+#define CRT_C 24 /* 24 CRT Controller Registers */
+#define ATT_C 21 /* 21 Attribute Controller Registers */
+#define GRA_C 9 /* 9 Graphics Controller Registers */
+#define SEQ_C 5 /* 5 Sequencer Registers */
+#define MIS_C 1 /* 1 Misc Output Register */
+
+#define CRTC_H_TOTAL 0
+#define CRTC_H_DISP 1
+#define CRTC_H_BLANK_START 2
+#define CRTC_H_BLANK_END 3
+#define CRTC_H_SYNC_START 4
+#define CRTC_H_SYNC_END 5
+#define CRTC_V_TOTAL 6
+#define CRTC_OVERFLOW 7
+#define CRTC_PRESET_ROW 8
+#define CRTC_MAX_SCAN 9
+#define CRTC_CURSOR_START 0x0A
+#define CRTC_CURSOR_END 0x0B
+#define CRTC_START_HI 0x0C
+#define CRTC_START_LO 0x0D
+#define CRTC_CURSOR_HI 0x0E
+#define CRTC_CURSOR_LO 0x0F
+#define CRTC_V_SYNC_START 0x10
+#define CRTC_V_SYNC_END 0x11
+#define CRTC_V_DISP_END 0x12
+#define CRTC_OFFSET 0x13
+#define CRTC_UNDERLINE 0x14
+#define CRTC_V_BLANK_START 0x15
+#define CRTC_V_BLANK_END 0x16
+#define CRTC_MODE 0x17
+#define CRTC_LINE_COMPARE 0x18
+#define CRTC_REGS 0x19
+
+#define ATC_MODE 0x10
+#define ATC_OVERSCAN 0x11
+#define ATC_PLANE_ENABLE 0x12
+#define ATC_PEL 0x13
+#define ATC_COLOR_PAGE 0x14
+
+#define SEQ_CLOCK_MODE 0x01
+#define SEQ_PLANE_WRITE 0x02
+#define SEQ_CHARACTER_MAP 0x03
+#define SEQ_MEMORY_MODE 0x04
+
+#define GDC_SR_VALUE 0x00
+#define GDC_SR_ENABLE 0x01
+#define GDC_COMPARE_VALUE 0x02
+#define GDC_DATA_ROTATE 0x03
+#define GDC_PLANE_READ 0x04
+#define GDC_MODE 0x05
+#define GDC_MISC 0x06
+#define GDC_COMPARE_MASK 0x07
+#define GDC_BIT_MASK 0x08
+
+struct vga16fb_par {
+ u8 crtc[CRTC_REGS];
+ u8 atc[ATT_C];
+ u8 gdc[GRA_C];
+ u8 seq[SEQ_C];
+ u8 misc;
+ u8 vss;
+ struct fb_var_screeninfo var;
+};
+
+/* --------------------------------------------------------------------- */
+
+static struct fb_var_screeninfo vga16fb_defined = {
+ 640,480,640,480,/* W,H, W, H (virtual) load xres,xres_virtual*/
+ 0,0, /* virtual -> visible no offset */
+ 4, /* depth -> load bits_per_pixel */
+ 0, /* greyscale ? */
+ {0,0,0}, /* R */
+ {0,0,0}, /* G */
+ {0,0,0}, /* B */
+ {0,0,0}, /* transparency */
+ 0, /* standard pixel format */
+ FB_ACTIVATE_NOW,
+ -1,-1,
+ 0,
+ 39721, 48, 16, 39, 8,
+ 96, 2, 0, /* No sync info */
+ FB_VMODE_NONINTERLACED,
+ {0,0,0,0,0,0}
+};
+
+static struct display disp;
+static struct { u_short blue, green, red, pad; } palette[256];
+
+static int currcon = 0;
+
+/* --------------------------------------------------------------------- */
+
+ /*
+ * Open/Release the frame buffer device
+ */
+
+static int vga16fb_open(struct fb_info *info, int user)
+{
+ /*
+ * Nothing, only a usage count for the moment
+ */
+ MOD_INC_USE_COUNT;
+ return(0);
+}
+
+static int vga16fb_release(struct fb_info *info, int user)
+{
+ MOD_DEC_USE_COUNT;
+ return(0);
+}
+
+static void vga16fb_pan_var(struct fb_info *info, struct fb_var_screeninfo *var)
+{
+ u32 pos = (var->xres_virtual * var->yoffset + var->xoffset) >> 3;
+ outb(CRTC_START_HI, CRT_IC);
+ outb(pos >> 8, CRT_DC);
+ outb(CRTC_START_LO, CRT_IC);
+ outb(pos & 0xFF, CRT_DC);
+#if 0
+ /* if someone supports xoffset in bit resolution */
+ inb(IS1_RC); /* reset flip-flop */
+ outb(ATC_PEL, ATT_IW);
+ outb(xoffset & 7, ATT_IW);
+ inb(IS1_RC);
+ outb(0x20, ATT_IW);
+#endif
+}
+
+static int vga16fb_update_var(int con, struct fb_info *info)
+{
+ vga16fb_pan_var(info, &fb_display[con].var);
+ return 0;
+}
+
+static int vga16fb_get_fix(struct fb_fix_screeninfo *fix, int con,
+ struct fb_info *info)
+{
+ struct display *p;
+
+ if (con < 0)
+ p = &disp;
+ else
+ p = fb_display + con;
+
+ memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+ strcpy(fix->id,"VGA16 VGA");
+
+ fix->smem_start = (char *) 0xa0000;
+ fix->smem_len = 65536;
+ fix->type = FB_TYPE_VGA_PLANES;
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
+ fix->xpanstep = 8;
+ fix->ypanstep = 1;
+ fix->ywrapstep = 0;
+ fix->line_length = p->var.xres_virtual / 8;
+ return 0;
+}
+
+static int vga16fb_get_var(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info)
+{
+ if(con==-1)
+ memcpy(var, &vga16fb_defined, sizeof(struct fb_var_screeninfo));
+ else
+ *var=fb_display[con].var;
+ return 0;
+}
+
+static void vga16fb_set_disp(int con, struct vga16fb_info *info)
+{
+ struct fb_fix_screeninfo fix;
+ struct display *display;
+
+ if (con < 0)
+ display = &disp;
+ else
+ display = fb_display + con;
+
+
+ vga16fb_get_fix(&fix, con, &info->fb_info);
+
+ display->screen_base = info->video_vbase;
+ display->visual = fix.visual;
+ display->type = fix.type;
+ display->type_aux = fix.type_aux;
+ display->ypanstep = fix.ypanstep;
+ display->ywrapstep = fix.ywrapstep;
+ display->line_length = fix.line_length;
+ display->next_line = fix.line_length;
+ display->can_soft_blank = 1;
+ display->inverse = 0;
+
+ if (info->isVGA)
+ display->dispsw = &fbcon_vga_planes;
+ else
+ display->dispsw = &fbcon_ega_planes;
+ display->scrollmode = SCROLL_YREDRAW;
+}
+
+static void vga16fb_encode_var(struct fb_var_screeninfo *var,
+ const struct vga16fb_par *par,
+ const struct vga16fb_info *info)
+{
+ *var = par->var;
+}
+
+static void vga16fb_clock_chip(struct vga16fb_par *par,
+ unsigned int pixclock,
+ const struct vga16fb_info *info)
+{
+ static struct {
+ u32 pixclock;
+ u8 misc;
+ u8 seq_clock_mode;
+ } *ptr, *best, vgaclocks[] = {
+ { 79442 /* 12.587 */, 0x00, 0x08},
+ { 70616 /* 14.161 */, 0x04, 0x08},
+ { 39721 /* 25.175 */, 0x00, 0x00},
+ { 35308 /* 28.322 */, 0x04, 0x00},
+ { 0 /* bad */, 0x00, 0x00}};
+ int err;
+
+ best = vgaclocks;
+ err = pixclock - best->pixclock;
+ if (err < 0) err = -err;
+ for (ptr = vgaclocks + 1; ptr->pixclock; ptr++) {
+ int tmp;
+
+ tmp = pixclock - ptr->pixclock;
+ if (tmp < 0) tmp = -tmp;
+ if (tmp < err) {
+ err = tmp;
+ best = ptr;
+ }
+ }
+ par->misc |= best->misc;
+ par->seq[SEQ_CLOCK_MODE] |= best->seq_clock_mode;
+ par->var.pixclock = best->pixclock;
+}
+
+#define FAIL(X) return -EINVAL
+
+static int vga16fb_decode_var(const struct fb_var_screeninfo *var,
+ struct vga16fb_par *par,
+ const struct vga16fb_info *info)
+{
+ u32 xres, right, hslen, left, xtotal;
+ u32 yres, lower, vslen, upper, ytotal;
+ u32 vxres, xoffset, vyres, yoffset;
+ u32 pos;
+ u8 r7, rMode;
+ int i;
+
+ if (var->bits_per_pixel != 4)
+ return -EINVAL;
+ xres = (var->xres + 7) & ~7;
+ vxres = (var->xres_virtual + 0xF) & ~0xF;
+ xoffset = (var->xoffset + 7) & ~7;
+ left = (var->left_margin + 7) & ~7;
+ right = (var->right_margin + 7) & ~7;
+ hslen = (var->hsync_len + 7) & ~7;
+
+ if (vxres < xres)
+ vxres = xres;
+ if (xres + xoffset > vxres)
+ xoffset = vxres - xres;
+
+ par->var.xres = xres;
+ par->var.right_margin = right;
+ par->var.hsync_len = hslen;
+ par->var.left_margin = left;
+ par->var.xres_virtual = vxres;
+ par->var.xoffset = xoffset;
+
+ xres >>= 3;
+ right >>= 3;
+ hslen >>= 3;
+ left >>= 3;
+ vxres >>= 3;
+ xtotal = xres + right + hslen + left;
+ if (xtotal >= 256)
+ FAIL("xtotal too big");
+ if (hslen > 32)
+ FAIL("hslen too big");
+ if (right + hslen + left > 64)
+ FAIL("hblank too big");
+ par->crtc[CRTC_H_TOTAL] = xtotal - 5;
+ par->crtc[CRTC_H_BLANK_START] = xres - 1;
+ par->crtc[CRTC_H_DISP] = xres - 1;
+ pos = xres + right;
+ par->crtc[CRTC_H_SYNC_START] = pos;
+ pos += hslen;
+ par->crtc[CRTC_H_SYNC_END] = pos & 0x1F;
+ pos += left - 2; /* blank_end + 2 <= total + 5 */
+ par->crtc[CRTC_H_BLANK_END] = (pos & 0x1F) | 0x80;
+ if (pos & 0x20)
+ par->crtc[CRTC_H_SYNC_END] |= 0x80;
+
+ yres = var->yres;
+ lower = var->lower_margin;
+ vslen = var->vsync_len;
+ upper = var->upper_margin;
+ vyres = var->yres_virtual;
+ yoffset = var->yoffset;
+
+ if (yres > vyres)
+ vyres = yres;
+ if (vxres * vyres > 65536) {
+ vyres = 65536 / vxres;
+ if (vyres < yres)
+ return -ENOMEM;
+ }
+ if (yoffset + yres > vyres)
+ yoffset = vyres - yres;
+ par->var.yres = yres;
+ par->var.lower_margin = lower;
+ par->var.vsync_len = vslen;
+ par->var.upper_margin = upper;
+ par->var.yres_virtual = vyres;
+ par->var.yoffset = yoffset;
+
+ if (var->vmode & FB_VMODE_DOUBLE) {
+ yres <<= 1;
+ lower <<= 1;
+ vslen <<= 1;
+ upper <<= 1;
+ }
+ ytotal = yres + lower + vslen + upper;
+ if (ytotal > 1024) {
+ ytotal >>= 1;
+ yres >>= 1;
+ lower >>= 1;
+ vslen >>= 1;
+ upper >>= 1;
+ rMode = 0x04;
+ } else
+ rMode = 0x00;
+ if (ytotal > 1024)
+ FAIL("ytotal too big");
+ if (vslen > 16)
+ FAIL("vslen too big");
+ par->crtc[CRTC_V_TOTAL] = ytotal - 2;
+ r7 = 0x10; /* disable linecompare */
+ if (ytotal & 0x100) r7 |= 0x01;
+ if (ytotal & 0x200) r7 |= 0x20;
+ par->crtc[CRTC_PRESET_ROW] = 0;
+ par->crtc[CRTC_MAX_SCAN] = 0x40; /* 1 scanline, no linecmp */
+ par->var.vmode = var->vmode;
+ if (var->vmode & FB_VMODE_DOUBLE)
+ par->crtc[CRTC_MAX_SCAN] |= 0x80;
+ par->crtc[CRTC_CURSOR_START] = 0x20;
+ par->crtc[CRTC_CURSOR_END] = 0x00;
+ pos = yoffset * vxres + (xoffset >> 3);
+ par->crtc[CRTC_START_HI] = pos >> 8;
+ par->crtc[CRTC_START_LO] = pos & 0xFF;
+ par->crtc[CRTC_CURSOR_HI] = 0x00;
+ par->crtc[CRTC_CURSOR_LO] = 0x00;
+ pos = yres - 1;
+ par->crtc[CRTC_V_DISP_END] = pos & 0xFF;
+ par->crtc[CRTC_V_BLANK_START] = pos & 0xFF;
+ if (pos & 0x100)
+ r7 |= 0x0A; /* 0x02 -> DISP_END, 0x08 -> BLANK_START */
+ if (pos & 0x200) {
+ r7 |= 0x40; /* 0x40 -> DISP_END */
+ par->crtc[CRTC_MAX_SCAN] |= 0x20; /* BLANK_START */
+ }
+ pos += lower;
+ par->crtc[CRTC_V_SYNC_START] = pos & 0xFF;
+ if (pos & 0x100)
+ r7 |= 0x04;
+ if (pos & 0x200)
+ r7 |= 0x80;
+ pos += vslen;
+ par->crtc[CRTC_V_SYNC_END] = (pos & 0x0F) | 0x10; /* disabled IRQ */
+ pos += upper - 1; /* blank_end + 1 <= ytotal + 2 */
+ par->crtc[CRTC_V_BLANK_END] = pos & 0xFF; /* 0x7F for original VGA,
+ but some SVGA chips requires all 8 bits to set */
+ if (vxres >= 512)
+ FAIL("vxres too long");
+ par->crtc[CRTC_OFFSET] = vxres >> 1;
+ par->crtc[CRTC_UNDERLINE] = 0x1F;
+ par->crtc[CRTC_MODE] = rMode | 0xE3;
+ par->crtc[CRTC_LINE_COMPARE] = 0xFF;
+ par->crtc[CRTC_OVERFLOW] = r7;
+
+ par->vss = 0x00; /* 3DA */
+
+ for (i = 0x00; i < 0x10; i++)
+ par->atc[i] = i;
+ par->atc[ATC_MODE] = 0x81;
+ par->atc[ATC_OVERSCAN] = 0x00; /* 0 for EGA, 0xFF for VGA */
+ par->atc[ATC_PLANE_ENABLE] = 0x0F;
+ par->atc[ATC_PEL] = xoffset & 7;
+ par->atc[ATC_COLOR_PAGE] = 0x00;
+
+ par->misc = 0xC3; /* enable CPU, ports 0x3Dx, positive sync */
+ par->var.sync = var->sync;
+ if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+ par->misc &= ~0x40;
+ if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+ par->misc &= ~0x80;
+
+ par->seq[SEQ_CLOCK_MODE] = 0x01;
+ par->seq[SEQ_PLANE_WRITE] = 0x0F;
+ par->seq[SEQ_CHARACTER_MAP] = 0x00;
+ par->seq[SEQ_MEMORY_MODE] = 0x06;
+
+ par->gdc[GDC_SR_VALUE] = 0x00;
+ par->gdc[GDC_SR_ENABLE] = 0x0F;
+ par->gdc[GDC_COMPARE_VALUE] = 0x00;
+ par->gdc[GDC_DATA_ROTATE] = 0x20;
+ par->gdc[GDC_PLANE_READ] = 0;
+ par->gdc[GDC_MODE] = 0x00;
+ par->gdc[GDC_MISC] = 0x05;
+ par->gdc[GDC_COMPARE_MASK] = 0x0F;
+ par->gdc[GDC_BIT_MASK] = 0xFF;
+
+ vga16fb_clock_chip(par, var->pixclock, info);
+
+ par->var.bits_per_pixel = 4;
+ par->var.grayscale = var->grayscale;
+ par->var.red.offset = par->var.green.offset = par->var.blue.offset =
+ par->var.transp.offset = 0;
+ par->var.red.length = par->var.green.length = par->var.blue.length =
+ (info->isVGA) ? 6 : 2;
+ par->var.transp.length = 0;
+ par->var.nonstd = 0;
+ par->var.activate = FB_ACTIVATE_NOW;
+ par->var.height = -1;
+ par->var.width = -1;
+ par->var.accel_flags = 0;
+
+ return 0;
+}
+#undef FAIL
+
+static int vga16fb_set_par(const struct vga16fb_par *par,
+ struct vga16fb_info *info)
+{
+ int i;
+
+ outb(inb(MIS_R) | 0x01, MIS_W);
+
+ /* Enable graphics register modification */
+ if (!info->isVGA) {
+ outb(0x00, GRA_E0);
+ outb(0x01, GRA_E1);
+ }
+
+ /* update misc output register */
+ outb(par->misc, MIS_W);
+
+ /* synchronous reset on */
+ outb(0x00, SEQ_I);
+ outb(0x01, SEQ_D);
+
+ /* write sequencer registers */
+ outb(1, SEQ_I);
+ outb(par->seq[1] | 0x20, SEQ_D);
+ for (i = 2; i < SEQ_C; i++) {
+ outb(i, SEQ_I);
+ outb(par->seq[i], SEQ_D);
+ }
+
+ /* synchronous reset off */
+ outb(0x00, SEQ_I);
+ outb(0x03, SEQ_D);
+
+ /* deprotect CRT registers 0-7 */
+ outb(0x11, CRT_IC);
+ outb(par->crtc[0x11], CRT_DC);
+
+ /* write CRT registers */
+ for (i = 0; i < CRTC_REGS; i++) {
+ outb(i, CRT_IC);
+ outb(par->crtc[i], CRT_DC);
+ }
+
+ /* write graphics controller registers */
+ for (i = 0; i < GRA_C; i++) {
+ outb(i, GRA_I);
+ outb(par->gdc[i], GRA_D);
+ }
+
+ /* write attribute controller registers */
+ for (i = 0; i < ATT_C; i++) {
+ inb_p(IS1_RC); /* reset flip-flop */
+ outb_p(i, ATT_IW);
+ outb_p(par->atc[i], ATT_IW);
+ }
+
+ /* Wait for screen to stabilize. */
+ mdelay(50);
+
+ outb(0x01, SEQ_I);
+ outb(par->seq[1], SEQ_D);
+
+ inb(IS1_RC);
+ outb(0x20, ATT_IW);
+
+ return 0;
+}
+
+static int vga16fb_set_var(struct fb_var_screeninfo *var, int con,
+ struct fb_info *fb)
+{
+ struct vga16fb_info *info = (struct vga16fb_info*)fb;
+ struct vga16fb_par par;
+ struct display *display;
+ int err;
+
+ if (con < 0)
+ display = fb->disp;
+ else
+ display = fb_display + con;
+ if ((err = vga16fb_decode_var(var, &par, info)) != 0)
+ return err;
+ vga16fb_encode_var(var, &par, info);
+
+ if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_TEST)
+ return 0;
+
+ if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+ u32 oldxres, oldyres, oldvxres, oldvyres, oldbpp;
+
+ oldxres = display->var.xres;
+ oldyres = display->var.yres;
+ oldvxres = display->var.xres_virtual;
+ oldvyres = display->var.yres_virtual;
+ oldbpp = display->var.bits_per_pixel;
+
+ display->var = *var;
+
+ if (oldxres != var->xres || oldyres != var->yres ||
+ oldvxres != var->xres_virtual || oldvyres != var->yres_virtual ||
+ oldbpp != var->bits_per_pixel) {
+ vga16fb_set_disp(con, info);
+ if (info->fb_info.changevar)
+ info->fb_info.changevar(con);
+ }
+ if (con == currcon)
+ vga16fb_set_par(&par, info);
+ }
+
+ return 0;
+}
+
+static void ega16_setpalette(int regno, unsigned red, unsigned green, unsigned blue)
+{
+ static unsigned char map[] = { 000, 001, 010, 011 };
+ int val;
+
+ val = map[red>>14] | ((map[green>>14]) << 1) | ((map[blue>>14]) << 2);
+ inb_p(0x3DA); /* ! 0x3BA */
+ outb_p(regno, 0x3C0);
+ outb_p(val, 0x3C0);
+ inb_p(0x3DA); /* some clones need it */
+ outb_p(0x20, 0x3C0); /* unblank screen */
+}
+
+static int vga16_getcolreg(unsigned regno, unsigned *red, unsigned *green,
+ unsigned *blue, unsigned *transp,
+ struct fb_info *fb_info)
+{
+ /*
+ * Read a single color register and split it into colors/transparent.
+ * Return != 0 for invalid regno.
+ */
+
+ if (regno >= 16)
+ return 1;
+
+ *red = palette[regno].red;
+ *green = palette[regno].green;
+ *blue = palette[regno].blue;
+ *transp = 0;
+ return 0;
+}
+
+static void vga16_setpalette(int regno, unsigned red, unsigned green, unsigned blue)
+{
+ outb(regno, dac_reg);
+ outb(red >> 10, dac_val);
+ outb(green >> 10, dac_val);
+ outb(blue >> 10, dac_val);
+}
+
+static int vga16_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *fb_info)
+{
+ int gray;
+
+ /*
+ * Set a single color register. The values supplied are
+ * already rounded down to the hardware's capabilities
+ * (according to the entries in the `var' structure). Return
+ * != 0 for invalid regno.
+ */
+
+ if (regno >= 16)
+ return 1;
+
+ palette[regno].red = red;
+ palette[regno].green = green;
+ palette[regno].blue = blue;
+
+ if (currcon < 0)
+ gray = disp.var.grayscale;
+ else
+ gray = fb_display[currcon].var.grayscale;
+ if (gray) {
+ /* gray = 0.30*R + 0.59*G + 0.11*B */
+ red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
+ }
+ if (((struct vga16fb_info *) fb_info)->isVGA)
+ vga16_setpalette(regno,red,green,blue);
+ else
+ ega16_setpalette(regno,red,green,blue);
+
+ return 0;
+}
+
+static void do_install_cmap(int con, struct fb_info *info)
+{
+ if (con != currcon)
+ return;
+ if (fb_display[con].cmap.len)
+ fb_set_cmap(&fb_display[con].cmap, 1, vga16_setcolreg, info);
+ else
+ fb_set_cmap(fb_default_cmap(16), 1, vga16_setcolreg,
+ info);
+}
+
+static int vga16fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info)
+{
+ if (con == currcon) /* current console? */
+ return fb_get_cmap(cmap, kspc, vga16_getcolreg, info);
+ else if (fb_display[con].cmap.len) /* non default colormap? */
+ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+ else
+ fb_copy_cmap(fb_default_cmap(16),
+ cmap, kspc ? 0 : 2);
+ return 0;
+}
+
+static int vga16fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info)
+{
+ int err;
+
+ if (!fb_display[con].cmap.len) { /* no colormap allocated? */
+ err = fb_alloc_cmap(&fb_display[con].cmap,16,0);
+ if (err)
+ return err;
+ }
+ if (con == currcon) /* current console? */
+ return fb_set_cmap(cmap, kspc, vga16_setcolreg, info);
+ else
+ fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+ return 0;
+}
+
+static int vga16fb_pan_display(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info)
+{
+ if (var->xoffset + fb_display[con].var.xres > fb_display[con].var.xres_virtual ||
+ var->yoffset + fb_display[con].var.yres > fb_display[con].var.yres_virtual)
+ return -EINVAL;
+ if (con == currcon)
+ vga16fb_pan_var(info, var);
+ fb_display[con].var.xoffset = var->xoffset;
+ fb_display[con].var.yoffset = var->yoffset;
+ fb_display[con].var.vmode &= ~FB_VMODE_YWRAP;
+ return 0;
+}
+
+static int vga16fb_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg, int con,
+ struct fb_info *info)
+{
+ return -EINVAL;
+}
+
+static struct fb_ops vga16fb_ops = {
+ vga16fb_open,
+ vga16fb_release,
+ vga16fb_get_fix,
+ vga16fb_get_var,
+ vga16fb_set_var,
+ vga16fb_get_cmap,
+ vga16fb_set_cmap,
+ vga16fb_pan_display,
+ vga16fb_ioctl
+};
+
+void vga16fb_setup(char *options, int *ints)
+{
+ char *this_opt;
+
+ vga16fb.fb_info.fontname[0] = '\0';
+
+ if (!options || !*options)
+ return;
+
+ for(this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,",")) {
+ if (!*this_opt) continue;
+
+ if (!strncmp(this_opt, "font:", 5))
+ strcpy(vga16fb.fb_info.fontname, this_opt+5);
+ }
+}
+
+static int vga16fb_switch(int con, struct fb_info *fb)
+{
+ struct vga16fb_par par;
+ struct vga16fb_info *info = (struct vga16fb_info*)fb;
+
+ /* Do we have to save the colormap? */
+ if (fb_display[currcon].cmap.len)
+ fb_get_cmap(&fb_display[currcon].cmap, 1, vga16_getcolreg,
+ fb);
+
+ currcon = con;
+ vga16fb_decode_var(&fb_display[con].var, &par, info);
+ vga16fb_set_par(&par, info);
+ vga16fb_set_disp(con, info);
+
+ /* Install new colormap */
+ do_install_cmap(con, fb);
+/* vga16fb_update_var(con, fb); */
+ return 1;
+}
+
+/* The following VESA blanking code is taken from vgacon.c. The VGA
+ blanking code was originally by Huang shi chao, and modified by
+ Christoph Rimek (chrimek@toppoint.de) and todd j. derr
+ (tjd@barefoot.org) for Linux. */
+#define attrib_port 0x3c0
+#define seq_port_reg 0x3c4
+#define seq_port_val 0x3c5
+#define gr_port_reg 0x3ce
+#define gr_port_val 0x3cf
+#define video_misc_rd 0x3cc
+#define video_misc_wr 0x3c2
+#define vga_video_port_reg 0x3d4
+#define vga_video_port_val 0x3d5
+
+static void vga_vesa_blank(struct vga16fb_info *info, int mode)
+{
+ unsigned char SeqCtrlIndex;
+ unsigned char CrtCtrlIndex;
+
+ cli();
+ SeqCtrlIndex = inb_p(seq_port_reg);
+ CrtCtrlIndex = inb_p(vga_video_port_reg);
+
+ /* save original values of VGA controller registers */
+ if(!info->vesa_blanked) {
+ info->vga_state.CrtMiscIO = inb_p(video_misc_rd);
+ sti();
+
+ outb_p(0x00,vga_video_port_reg); /* HorizontalTotal */
+ info->vga_state.HorizontalTotal = inb_p(vga_video_port_val);
+ outb_p(0x01,vga_video_port_reg); /* HorizDisplayEnd */
+ info->vga_state.HorizDisplayEnd = inb_p(vga_video_port_val);
+ outb_p(0x04,vga_video_port_reg); /* StartHorizRetrace */
+ info->vga_state.StartHorizRetrace = inb_p(vga_video_port_val);
+ outb_p(0x05,vga_video_port_reg); /* EndHorizRetrace */
+ info->vga_state.EndHorizRetrace = inb_p(vga_video_port_val);
+ outb_p(0x07,vga_video_port_reg); /* Overflow */
+ info->vga_state.Overflow = inb_p(vga_video_port_val);
+ outb_p(0x10,vga_video_port_reg); /* StartVertRetrace */
+ info->vga_state.StartVertRetrace = inb_p(vga_video_port_val);
+ outb_p(0x11,vga_video_port_reg); /* EndVertRetrace */
+ info->vga_state.EndVertRetrace = inb_p(vga_video_port_val);
+ outb_p(0x17,vga_video_port_reg); /* ModeControl */
+ info->vga_state.ModeControl = inb_p(vga_video_port_val);
+ outb_p(0x01,seq_port_reg); /* ClockingMode */
+ info->vga_state.ClockingMode = inb_p(seq_port_val);
+ }
+
+ /* assure that video is enabled */
+ /* "0x20" is VIDEO_ENABLE_bit in register 01 of sequencer */
+ cli();
+ outb_p(0x01,seq_port_reg);
+ outb_p(info->vga_state.ClockingMode | 0x20,seq_port_val);
+
+ /* test for vertical retrace in process.... */
+ if ((info->vga_state.CrtMiscIO & 0x80) == 0x80)
+ outb_p(info->vga_state.CrtMiscIO & 0xef,video_misc_wr);
+
+ /*
+ * Set <End of vertical retrace> to minimum (0) and
+ * <Start of vertical Retrace> to maximum (incl. overflow)
+ * Result: turn off vertical sync (VSync) pulse.
+ */
+ if (mode & VESA_VSYNC_SUSPEND) {
+ outb_p(0x10,vga_video_port_reg); /* StartVertRetrace */
+ outb_p(0xff,vga_video_port_val); /* maximum value */
+ outb_p(0x11,vga_video_port_reg); /* EndVertRetrace */
+ outb_p(0x40,vga_video_port_val); /* minimum (bits 0..3) */
+ outb_p(0x07,vga_video_port_reg); /* Overflow */
+ outb_p(info->vga_state.Overflow | 0x84,vga_video_port_val); /* bits 9,10 of vert. retrace */
+ }
+
+ if (mode & VESA_HSYNC_SUSPEND) {
+ /*
+ * Set <End of horizontal retrace> to minimum (0) and
+ * <Start of horizontal Retrace> to maximum
+ * Result: turn off horizontal sync (HSync) pulse.
+ */
+ outb_p(0x04,vga_video_port_reg); /* StartHorizRetrace */
+ outb_p(0xff,vga_video_port_val); /* maximum */
+ outb_p(0x05,vga_video_port_reg); /* EndHorizRetrace */
+ outb_p(0x00,vga_video_port_val); /* minimum (0) */
+ }
+
+ /* restore both index registers */
+ outb_p(SeqCtrlIndex,seq_port_reg);
+ outb_p(CrtCtrlIndex,vga_video_port_reg);
+ sti();
+}
+
+static void vga_vesa_unblank(struct vga16fb_info *info)
+{
+ unsigned char SeqCtrlIndex;
+ unsigned char CrtCtrlIndex;
+
+ cli();
+ SeqCtrlIndex = inb_p(seq_port_reg);
+ CrtCtrlIndex = inb_p(vga_video_port_reg);
+
+ /* restore original values of VGA controller registers */
+ outb_p(info->vga_state.CrtMiscIO,video_misc_wr);
+
+ outb_p(0x00,vga_video_port_reg); /* HorizontalTotal */
+ outb_p(info->vga_state.HorizontalTotal,vga_video_port_val);
+ outb_p(0x01,vga_video_port_reg); /* HorizDisplayEnd */
+ outb_p(info->vga_state.HorizDisplayEnd,vga_video_port_val);
+ outb_p(0x04,vga_video_port_reg); /* StartHorizRetrace */
+ outb_p(info->vga_state.StartHorizRetrace,vga_video_port_val);
+ outb_p(0x05,vga_video_port_reg); /* EndHorizRetrace */
+ outb_p(info->vga_state.EndHorizRetrace,vga_video_port_val);
+ outb_p(0x07,vga_video_port_reg); /* Overflow */
+ outb_p(info->vga_state.Overflow,vga_video_port_val);
+ outb_p(0x10,vga_video_port_reg); /* StartVertRetrace */
+ outb_p(info->vga_state.StartVertRetrace,vga_video_port_val);
+ outb_p(0x11,vga_video_port_reg); /* EndVertRetrace */
+ outb_p(info->vga_state.EndVertRetrace,vga_video_port_val);
+ outb_p(0x17,vga_video_port_reg); /* ModeControl */
+ outb_p(info->vga_state.ModeControl,vga_video_port_val);
+ outb_p(0x01,seq_port_reg); /* ClockingMode */
+ outb_p(info->vga_state.ClockingMode,seq_port_val);
+
+ /* restore index/control registers */
+ outb_p(SeqCtrlIndex,seq_port_reg);
+ outb_p(CrtCtrlIndex,vga_video_port_reg);
+ sti();
+}
+
+static void vga_pal_blank(void)
+{
+ int i;
+
+ for (i=0; i<16; i++) {
+ outb_p (i, dac_reg) ;
+ outb_p (0, dac_val) ;
+ outb_p (0, dac_val) ;
+ outb_p (0, dac_val) ;
+ }
+}
+
+/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
+static void vga16fb_blank(int blank, struct fb_info *fb_info)
+{
+ struct vga16fb_info *info = (struct vga16fb_info*)fb_info;
+
+ switch (blank) {
+ case 0: /* Unblank */
+ if (info->vesa_blanked) {
+ vga_vesa_unblank(info);
+ info->vesa_blanked = 0;
+ }
+ if (info->palette_blanked) {
+ do_install_cmap(currcon, fb_info);
+ info->palette_blanked = 0;
+ }
+ break;
+ case 1: /* blank */
+ vga_pal_blank();
+ info->palette_blanked = 1;
+ break;
+ default: /* VESA blanking */
+ vga_vesa_blank(info, blank-1);
+ info->vesa_blanked = 1;
+ break;
+ }
+}
+
+__initfunc(void vga16fb_init(void))
+{
+ int i,j;
+
+ printk("vga16fb: initializing\n");
+
+ vga16fb.video_vbase = ioremap((unsigned long)0xa0000, 65536);
+ printk("vga16fb: mapped to 0x%p\n", vga16fb.video_vbase);
+
+ vga16fb.isVGA = ORIG_VIDEO_ISVGA;
+ vga16fb.palette_blanked = 0;
+ vga16fb.vesa_blanked = 0;
+
+ i = vga16fb.isVGA? 6 : 2;
+
+ vga16fb_defined.red.length = i;
+ vga16fb_defined.green.length = i;
+ vga16fb_defined.blue.length = i;
+ for(i = 0; i < 16; i++) {
+ j = color_table[i];
+ palette[i].red = default_red[j];
+ palette[i].green = default_grn[j];
+ palette[i].blue = default_blu[j];
+ }
+ if (vga16fb.isVGA)
+ request_region(0x3c0, 32, "vga+");
+ else
+ request_region(0x3C0, 32, "ega");
+
+ disp.var = vga16fb_defined;
+
+ /* name should not depend on EGA/VGA */
+ strcpy(vga16fb.fb_info.modename, "VGA16 VGA");
+ vga16fb.fb_info.changevar = NULL;
+ vga16fb.fb_info.node = -1;
+ vga16fb.fb_info.fbops = &vga16fb_ops;
+ vga16fb.fb_info.disp=&disp;
+ vga16fb.fb_info.switch_con=&vga16fb_switch;
+ vga16fb.fb_info.updatevar=&vga16fb_update_var;
+ vga16fb.fb_info.blank=&vga16fb_blank;
+ vga16fb.fb_info.flags=FBINFO_FLAG_DEFAULT;
+ vga16fb_set_disp(-1, &vga16fb);
+
+ if (register_framebuffer(&vga16fb.fb_info)<0)
+ return;
+
+ printk("fb%d: %s frame buffer device\n",
+ GET_FB_IDX(vga16fb.fb_info.node), vga16fb.fb_info.modename);
+}
+
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
+