summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--fs/hpfs/alloc.c434
-rw-r--r--fs/hpfs/anode.c482
-rw-r--r--fs/hpfs/buffer.c265
-rw-r--r--fs/hpfs/dentry.c62
-rw-r--r--fs/hpfs/dir.c253
-rw-r--r--fs/hpfs/dnode.c1070
-rw-r--r--fs/hpfs/ea.c305
-rw-r--r--fs/hpfs/file.c195
-rw-r--r--fs/hpfs/hpfs_fn.h318
-rw-r--r--fs/hpfs/inode.c381
-rw-r--r--fs/hpfs/map.c268
-rw-r--r--fs/hpfs/mmap.c128
-rw-r--r--fs/hpfs/name.c144
-rw-r--r--fs/hpfs/namei.c549
-rw-r--r--fs/hpfs/super.c598
-rw-r--r--fs/proc/sysvipc.c138
-rw-r--r--include/asm-alpha/hdreg.h12
-rw-r--r--include/asm-arm/hdreg.h13
-rw-r--r--include/asm-i386/hdreg.h12
-rw-r--r--include/asm-i386/page_offset.h8
-rw-r--r--include/asm-m68k/hdreg.h13
-rw-r--r--include/asm-mips/hdreg.h18
-rw-r--r--include/asm-ppc/hdreg.h17
-rw-r--r--include/asm-sparc64/hdreg.h13
-rw-r--r--include/linux/blkpg.h64
-rw-r--r--include/linux/cyclomx.h91
-rw-r--r--include/linux/cycx_cfm.h81
-rw-r--r--include/linux/cycx_drv.h66
-rw-r--r--include/linux/cycx_x25.h103
-rw-r--r--include/linux/dn.h161
-rw-r--r--include/linux/hdsmart.h61
-rw-r--r--include/linux/i2o.h588
-rw-r--r--include/linux/ide.h813
-rw-r--r--include/linux/isdn_budget.h62
-rw-r--r--include/linux/isdn_lzscomp.h26
-rw-r--r--include/linux/isdn_timru.h119
-rw-r--r--include/net/decnet_call.h2
-rw-r--r--include/net/dn.h206
-rw-r--r--include/net/dn_dev.h194
-rw-r--r--include/net/dn_fib.h83
-rw-r--r--include/net/dn_neigh.h29
-rw-r--r--include/net/dn_nsp.h194
-rw-r--r--include/net/dn_raw.h17
-rw-r--r--include/net/dn_route.h74
-rw-r--r--include/net/irda/smc-ircc.h160
-rw-r--r--include/net/irda/toshoboe.h165
-rw-r--r--include/video/fbcon-vga-planes.h37
-rw-r--r--net/decnet/Config.in13
-rw-r--r--net/decnet/Makefile30
-rw-r--r--net/decnet/TODO59
-rw-r--r--net/decnet/af_decnet.c2192
-rw-r--r--net/decnet/dn_dev.c1386
-rw-r--r--net/decnet/dn_fib.c805
-rw-r--r--net/decnet/dn_neigh.c633
-rw-r--r--net/decnet/dn_nsp_in.c703
-rw-r--r--net/decnet/dn_nsp_out.c640
-rw-r--r--net/decnet/dn_raw.c383
-rw-r--r--net/decnet/dn_route.c1028
-rw-r--r--net/decnet/dn_timer.c164
-rw-r--r--net/decnet/sysctl_net_decnet.c473
140 files changed, 59115 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:
+ */
+
diff --git a/fs/hpfs/alloc.c b/fs/hpfs/alloc.c
new file mode 100644
index 000000000..bca0497ae
--- /dev/null
+++ b/fs/hpfs/alloc.c
@@ -0,0 +1,434 @@
+/*
+ * linux/fs/hpfs/alloc.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * HPFS bitmap operations
+ */
+
+#include "hpfs_fn.h"
+
+/*
+ * Check if a sector is allocated in bitmap
+ * This is really slow. Turned on only if chk==2
+ */
+
+static int chk_if_allocated(struct super_block *s, secno sec, char *msg)
+{
+ struct quad_buffer_head qbh;
+ unsigned *bmp;
+ if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "chk"))) goto fail;
+ if ((bmp[(sec & 0x3fff) >> 5] >> (sec & 0x1f)) & 1) {
+ hpfs_error(s, "sector '%s' - %08x not allocated in bitmap", msg, sec);
+ goto fail1;
+ }
+ hpfs_brelse4(&qbh);
+ if (sec >= s->s_hpfs_dirband_start && sec < s->s_hpfs_dirband_start + s->s_hpfs_dirband_size) {
+ unsigned ssec = (sec - s->s_hpfs_dirband_start) / 4;
+ if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) goto fail;
+ if ((bmp[ssec >> 5] >> (ssec & 0x1f)) & 1) {
+ hpfs_error(s, "sector '%s' - %08x not allocated in directory bitmap", msg, sec);
+ goto fail1;
+ }
+ hpfs_brelse4(&qbh);
+ }
+ return 0;
+ fail1:
+ hpfs_brelse4(&qbh);
+ fail:
+ return 1;
+}
+
+/*
+ * Check if sector(s) have proper number and additionally check if they're
+ * allocated in bitmap.
+ */
+
+int hpfs_chk_sectors(struct super_block *s, secno start, int len, char *msg)
+{
+ if (start + len < start || start < 0x12 ||
+ start + len > s->s_hpfs_fs_size) {
+ hpfs_error(s, "sector(s) '%s' badly placed at %08x", msg, start);
+ return 1;
+ }
+ if (s->s_hpfs_chk>=2) {
+ int i;
+ for (i = 0; i < len; i++)
+ if (chk_if_allocated(s, start + i, msg)) return 1;
+ }
+ return 0;
+}
+
+static secno alloc_in_bmp(struct super_block *s, secno near, unsigned n, unsigned forward)
+{
+ struct quad_buffer_head qbh;
+ unsigned *bmp;
+ unsigned bs = near & ~0x3fff;
+ unsigned nr = (near & 0x3fff) & ~(n - 1);
+ /*unsigned mnr;*/
+ unsigned i, q;
+ int a, b;
+ secno ret = 0;
+ if (n != 1 && n != 4) {
+ hpfs_error(s, "Bad allocation size: %d", n);
+ return 0;
+ }
+ lock_super(s);
+ if (bs != ~0x3fff) {
+ if (!(bmp = hpfs_map_bitmap(s, near >> 14, &qbh, "aib"))) goto uls;
+ } else {
+ if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) goto uls;
+ }
+ /*if (!tstbits(bmp, nr + n, n + forward)) {
+ ret = bs + nr;
+ goto rt;
+ }
+ if (!tstbits(bmp, nr + 2*n, n + forward)) {
+ ret = bs + nr + n;
+ goto rt;
+ }*/
+ q = nr + n; b = 0;
+ while ((a = tstbits(bmp, q, n + forward))) {
+ q += a;
+ if (n != 1) q = ((q-1)&~(n-1))+n;
+ if (!b) {
+ if (q>>5 != nr>>5) {
+ b = 1;
+ q = nr & 0x1f;
+ }
+ } else if (q > nr) break;
+ }
+ if (!a) {
+ ret = bs + q;
+ goto rt;
+ }
+ nr >>= 5;
+ for (i = nr + 1; i != nr; i++, i &= 0x1ff) {
+ if (!bmp[i]) continue;
+ if (n + forward >= 0x3f && bmp[i] != -1) continue;
+ q = i<<5;
+ if (i > 0) {
+ unsigned k = bmp[i-1];
+ while (k & 0x80000000) {
+ q--; k <<= 1;
+ }
+ }
+ if (n != 1) q = ((q-1)&~(n-1))+n;
+ while ((a = tstbits(bmp, q, n + forward))) {
+ q += a;
+ if (n != 1) q = ((q-1)&~(n-1))+n;
+ if (q>>5 > i) break;
+ }
+ if (!a) {
+ ret = bs + q;
+ goto rt;
+ }
+ }
+ rt:
+ if (ret) {
+ if (s->s_hpfs_chk && ((ret >> 14) != (bs >> 14) || (bmp[(ret & 0x3fff) >> 5] | ~(((1 << n) - 1) << (ret & 0x1f))) != 0xffffffff)) {
+ hpfs_error(s, "Allocation doesn't work! Wanted %d, allocated at %08x", n, ret);
+ ret = 0;
+ goto b;
+ }
+ bmp[(ret & 0x3fff) >> 5] &= ~(((1 << n) - 1) << (ret & 0x1f));
+ hpfs_mark_4buffers_dirty(&qbh);
+ }
+ b:
+ hpfs_brelse4(&qbh);
+ uls:
+ unlock_super(s);
+ return ret;
+}
+
+/*
+ * Allocation strategy: 1) search place near the sector specified
+ * 2) search bitmap where free sectors last found
+ * 3) search all bitmaps
+ * 4) search all bitmaps ignoring number of pre-allocated
+ * sectors
+ */
+
+secno hpfs_alloc_sector(struct super_block *s, secno near, unsigned n, int forward, int lock)
+{
+ secno sec;
+ unsigned i;
+ unsigned n_bmps;
+ int b = s->s_hpfs_c_bitmap;
+ int f_p = 0;
+ if (forward < 0) {
+ forward = -forward;
+ f_p = 1;
+ }
+ if (lock) hpfs_lock_creation(s);
+ if (near && near < s->s_hpfs_fs_size)
+ if ((sec = alloc_in_bmp(s, near, n, f_p ? forward : forward/4))) goto ret;
+ if (b != -1) {
+ if ((sec = alloc_in_bmp(s, b<<14, n, f_p ? forward : forward/2))) {
+ b &= 0x0fffffff;
+ goto ret;
+ }
+ if (b > 0x10000000) if ((sec = alloc_in_bmp(s, (b&0xfffffff)<<14, n, f_p ? forward : 0))) goto ret;
+ }
+ n_bmps = (s->s_hpfs_fs_size + 0x4000 - 1) >> 14;
+ for (i = 0; i < n_bmps / 2; i++) {
+ if ((sec = alloc_in_bmp(s, (n_bmps/2+i) << 14, n, forward))) {
+ s->s_hpfs_c_bitmap = n_bmps/2+i;
+ goto ret;
+ }
+ if ((sec = alloc_in_bmp(s, (n_bmps/2-i-1) << 14, n, forward))) {
+ s->s_hpfs_c_bitmap = n_bmps/2-i-1;
+ goto ret;
+ }
+ }
+ if ((sec = alloc_in_bmp(s, (n_bmps-1) << 14, n, forward))) {
+ s->s_hpfs_c_bitmap = n_bmps-1;
+ goto ret;
+ }
+ if (!f_p) {
+ for (i = 0; i < n_bmps; i++)
+ if ((sec = alloc_in_bmp(s, i << 14, n, 0))) {
+ s->s_hpfs_c_bitmap = 0x10000000 + i;
+ goto ret;
+ }
+ }
+ sec = 0;
+ ret:
+ if (sec && f_p) {
+ for (i = 0; i < forward; i++) {
+ if (!hpfs_alloc_if_possible_nolock(s, sec + i + 1)) {
+ hpfs_error(s, "Prealloc doesn't work! Wanted %d, allocated at %08x, can't allocate %d", forward, sec, i);
+ sec = 0;
+ break;
+ }
+ }
+ }
+ if (lock) hpfs_unlock_creation(s);
+ return sec;
+}
+
+static secno alloc_in_dirband(struct super_block *s, secno near, int lock)
+{
+ unsigned nr = near;
+ secno sec;
+ if (nr < s->s_hpfs_dirband_start)
+ nr = s->s_hpfs_dirband_start;
+ if (nr >= s->s_hpfs_dirband_start + s->s_hpfs_dirband_size)
+ nr = s->s_hpfs_dirband_start + s->s_hpfs_dirband_size - 4;
+ nr -= s->s_hpfs_dirband_start;
+ nr >>= 2;
+ if (lock) hpfs_lock_creation(s);
+ sec = alloc_in_bmp(s, (~0x3fff) | nr, 1, 0);
+ if (lock) hpfs_unlock_creation(s);
+ if (!sec) return 0;
+ return ((sec & 0x3fff) << 2) + s->s_hpfs_dirband_start;
+}
+
+/* Alloc sector if it's free */
+
+int hpfs_alloc_if_possible_nolock(struct super_block *s, secno sec)
+{
+ struct quad_buffer_head qbh;
+ unsigned *bmp;
+ lock_super(s);
+ if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "aip"))) goto end;
+ if (bmp[(sec & 0x3fff) >> 5] & (1 << (sec & 0x1f))) {
+ bmp[(sec & 0x3fff) >> 5] &= ~(1 << (sec & 0x1f));
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ unlock_super(s);
+ return 1;
+ }
+ hpfs_brelse4(&qbh);
+ end:
+ unlock_super(s);
+ return 0;
+}
+
+int hpfs_alloc_if_possible(struct super_block *s, secno sec)
+{
+ int r;
+ hpfs_lock_creation(s);
+ r = hpfs_alloc_if_possible_nolock(s, sec);
+ hpfs_unlock_creation(s);
+ return r;
+}
+
+/* Free sectors in bitmaps */
+
+void hpfs_free_sectors(struct super_block *s, secno sec, unsigned n)
+{
+ struct quad_buffer_head qbh;
+ unsigned *bmp;
+ /*printk("2 - ");*/
+ if (!n) return;
+ if (sec < 0x12) {
+ hpfs_error(s, "Trying to free reserved sector %08x", sec);
+ return;
+ }
+ lock_super(s);
+ new_map:
+ if (!(bmp = hpfs_map_bitmap(s, sec >> 14, &qbh, "free"))) {
+ unlock_super(s);
+ return;
+ }
+ new_tst:
+ if ((bmp[(sec & 0x3fff) >> 5] >> (sec & 0x1f) & 1)) {
+ hpfs_error(s, "sector %08x not allocated", sec);
+ hpfs_brelse4(&qbh);
+ unlock_super(s);
+ return;
+ }
+ bmp[(sec & 0x3fff) >> 5] |= 1 << (sec & 0x1f);
+ if (!--n) {
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ unlock_super(s);
+ return;
+ }
+ if (!(++sec & 0x3fff)) {
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ goto new_map;
+ }
+ goto new_tst;
+}
+
+/*
+ * Check if there are at least n free dnodes on the filesystem.
+ * Called before adding to dnode. If we run out of space while
+ * splitting dnodes, it would corrupt dnode tree.
+ */
+
+int hpfs_check_free_dnodes(struct super_block *s, int n)
+{
+ int n_bmps = (s->s_hpfs_fs_size + 0x4000 - 1) >> 14;
+ int b = s->s_hpfs_c_bitmap & 0x0fffffff;
+ int i, j;
+ unsigned *bmp;
+ struct quad_buffer_head qbh;
+ if ((bmp = hpfs_map_dnode_bitmap(s, &qbh))) {
+ for (j = 0; j < 512; j++) {
+ unsigned k;
+ if (!bmp[j]) continue;
+ for (k = bmp[j]; k; k >>= 1) if (k & 1) if (!--n) {
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ }
+ }
+ hpfs_brelse4(&qbh);
+ i = 0;
+ if (s->s_hpfs_c_bitmap != -1 ) {
+ bmp = hpfs_map_bitmap(s, b, &qbh, "chkdn1");
+ goto chk_bmp;
+ }
+ chk_next:
+ if (i == b) i++;
+ if (i >= n_bmps) return 1;
+ bmp = hpfs_map_bitmap(s, i, &qbh, "chkdn2");
+ chk_bmp:
+ if (bmp) {
+ for (j = 0; j < 512; j++) {
+ unsigned k;
+ if (!bmp[j]) continue;
+ for (k = 0xf; k; k <<= 4)
+ if ((bmp[j] & k) == k) {
+ if (!--n) {
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ }
+ }
+ hpfs_brelse4(&qbh);
+ }
+ i++;
+ goto chk_next;
+}
+
+void hpfs_free_dnode(struct super_block *s, dnode_secno dno)
+{
+ if (s->s_hpfs_chk) if (dno & 3) {
+ hpfs_error(s, "hpfs_free_dnode: dnode %08x not aligned", dno);
+ return;
+ }
+ if (dno < s->s_hpfs_dirband_start ||
+ dno >= s->s_hpfs_dirband_start + s->s_hpfs_dirband_size) {
+ hpfs_free_sectors(s, dno, 4);
+ } else {
+ struct quad_buffer_head qbh;
+ unsigned *bmp;
+ unsigned ssec = (dno - s->s_hpfs_dirband_start) / 4;
+ lock_super(s);
+ if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) {
+ unlock_super(s);
+ return;
+ }
+ bmp[ssec >> 5] |= 1 << (ssec & 0x1f);
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ unlock_super(s);
+ }
+}
+
+struct dnode *hpfs_alloc_dnode(struct super_block *s, secno near,
+ dnode_secno *dno, struct quad_buffer_head *qbh,
+ int lock)
+{
+ struct dnode *d;
+ if (hpfs_count_one_bitmap(s, s->s_hpfs_dmap) > FREE_DNODES_ADD) {
+ if (!(*dno = alloc_in_dirband(s, near, lock)))
+ if (!(*dno = hpfs_alloc_sector(s, near, 4, 0, lock))) return NULL;
+ } else {
+ if (!(*dno = hpfs_alloc_sector(s, near, 4, 0, lock)))
+ if (!(*dno = alloc_in_dirband(s, near, lock))) return NULL;
+ }
+ if (!(d = hpfs_get_4sectors(s, *dno, qbh))) {
+ hpfs_free_dnode(s, *dno);
+ return NULL;
+ }
+ memset(d, 0, 2048);
+ d->magic = DNODE_MAGIC;
+ d->first_free = 52;
+ d->dirent[0] = 32;
+ d->dirent[2] = 8;
+ d->dirent[30] = 1;
+ d->dirent[31] = 255;
+ d->self = *dno;
+ return d;
+}
+
+struct fnode *hpfs_alloc_fnode(struct super_block *s, secno near, fnode_secno *fno,
+ struct buffer_head **bh)
+{
+ struct fnode *f;
+ if (!(*fno = hpfs_alloc_sector(s, near, 1, FNODE_ALLOC_FWD, 1))) return NULL;
+ if (!(f = hpfs_get_sector(s, *fno, bh))) {
+ hpfs_free_sectors(s, *fno, 1);
+ return NULL;
+ }
+ memset(f, 0, 512);
+ f->magic = FNODE_MAGIC;
+ f->ea_offs = 0xc4;
+ f->btree.n_free_nodes = 8;
+ f->btree.first_free = 8;
+ return f;
+}
+
+struct anode *hpfs_alloc_anode(struct super_block *s, secno near, anode_secno *ano,
+ struct buffer_head **bh)
+{
+ struct anode *a;
+ if (!(*ano = hpfs_alloc_sector(s, near, 1, ANODE_ALLOC_FWD, 1))) return NULL;
+ if (!(a = hpfs_get_sector(s, *ano, bh))) {
+ hpfs_free_sectors(s, *ano, 1);
+ return NULL;
+ }
+ memset(a, 0, 512);
+ a->magic = ANODE_MAGIC;
+ a->self = *ano;
+ a->btree.n_free_nodes = 40;
+ a->btree.n_used_nodes = 0;
+ a->btree.first_free = 8;
+ return a;
+}
diff --git a/fs/hpfs/anode.c b/fs/hpfs/anode.c
new file mode 100644
index 000000000..f2c4a3532
--- /dev/null
+++ b/fs/hpfs/anode.c
@@ -0,0 +1,482 @@
+/*
+ * linux/fs/hpfs/anode.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * handling HPFS anode tree that contains file allocation info
+ */
+
+#include "hpfs_fn.h"
+
+/* Find a sector in allocation tree */
+
+secno hpfs_bplus_lookup(struct super_block *s, struct inode *inode,
+ struct bplus_header *btree, unsigned sec,
+ struct buffer_head *bh)
+{
+ anode_secno a = -1;
+ struct anode *anode;
+ int i;
+ int c1, c2 = 0;
+ go_down:
+ if (s->s_hpfs_chk) if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_bplus_lookup")) return -1;
+ if (btree->internal) {
+ for (i = 0; i < btree->n_used_nodes; i++)
+ if (btree->u.internal[i].file_secno > sec) {
+ a = btree->u.internal[i].down;
+ brelse(bh);
+ if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
+ btree = &anode->btree;
+ goto go_down;
+ }
+ hpfs_error(s, "sector %08x not found in internal anode %08x", sec, a);
+ brelse(bh);
+ return -1;
+ }
+ for (i = 0; i < btree->n_used_nodes; i++)
+ if (btree->u.external[i].file_secno <= sec &&
+ btree->u.external[i].file_secno + btree->u.external[i].length > sec) {
+ a = btree->u.external[i].disk_secno + sec - btree->u.external[i].file_secno;
+ if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, a, 1, "data")) {
+ brelse(bh);
+ return -1;
+ }
+ if (inode) {
+ inode->i_hpfs_file_sec = btree->u.external[i].file_secno;
+ inode->i_hpfs_disk_sec = btree->u.external[i].disk_secno;
+ inode->i_hpfs_n_secs = btree->u.external[i].length;
+ }
+ brelse(bh);
+ return a;
+ }
+ hpfs_error(s, "sector %08x not found in external anode %08x", sec, a);
+ brelse(bh);
+ return -1;
+}
+
+/* Add a sector to tree */
+
+secno hpfs_add_sector_to_btree(struct super_block *s, secno node, int fnod, unsigned fsecno)
+{
+ struct bplus_header *btree;
+ struct anode *anode = NULL, *ranode = NULL;
+ struct fnode *fnode;
+ anode_secno a, na = -1, ra, up = -1;
+ secno se;
+ struct buffer_head *bh, *bh1, *bh2;
+ int n;
+ unsigned fs;
+ int c1, c2 = 0;
+ if (fnod) {
+ if (!(fnode = hpfs_map_fnode(s, node, &bh))) return -1;
+ btree = &fnode->btree;
+ } else {
+ if (!(anode = hpfs_map_anode(s, node, &bh))) return -1;
+ btree = &anode->btree;
+ }
+ a = node;
+ go_down:
+ if ((n = btree->n_used_nodes - 1) < -!!fnod) {
+ hpfs_error(s, "anode %08x has no entries", a);
+ brelse(bh);
+ return -1;
+ }
+ if (btree->internal) {
+ a = btree->u.internal[n].down;
+ btree->u.internal[n].file_secno = -1;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_add_sector_to_btree #1")) return -1;
+ if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
+ btree = &anode->btree;
+ goto go_down;
+ }
+ if (n >= 0) {
+ if (btree->u.external[n].file_secno + btree->u.external[n].length != fsecno) {
+ hpfs_error(s, "allocated size %08x, trying to add sector %08x, %cnode %08x",
+ btree->u.external[n].file_secno + btree->u.external[n].length, fsecno,
+ fnod?'f':'a', node);
+ brelse(bh);
+ return -1;
+ }
+ if (hpfs_alloc_if_possible(s, se = btree->u.external[n].disk_secno + btree->u.external[n].length)) {
+ btree->u.external[n].length++;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ return se;
+ }
+ } else {
+ if (fsecno) {
+ hpfs_error(s, "empty file %08x, trying to add sector %08x", node, fsecno);
+ brelse(bh);
+ return -1;
+ }
+ se = node;
+ }
+ if (!(se = hpfs_alloc_sector(s, se, 1, fsecno*ALLOC_M>ALLOC_FWD_MAX ? ALLOC_FWD_MAX : fsecno*ALLOC_M<ALLOC_FWD_MIN ? ALLOC_FWD_MIN : fsecno*ALLOC_M, 1))) {
+ brelse(bh);
+ return -1;
+ }
+ fs = n < 0 ? 0 : btree->u.external[n].file_secno + btree->u.external[n].length;
+ if (!btree->n_free_nodes) {
+ up = a != node ? anode->up : -1;
+ if (!(anode = hpfs_alloc_anode(s, a, &na, &bh1))) {
+ brelse(bh);
+ hpfs_free_sectors(s, se, 1);
+ return -1;
+ }
+ if (a == node && fnod) {
+ anode->up = node;
+ anode->btree.fnode_parent = 1;
+ anode->btree.n_used_nodes = btree->n_used_nodes;
+ anode->btree.first_free = btree->first_free;
+ anode->btree.n_free_nodes = 40 - anode->btree.n_used_nodes;
+ memcpy(&anode->u, &btree->u, btree->n_used_nodes * 12);
+ btree->internal = 1;
+ btree->n_free_nodes = 11;
+ btree->n_used_nodes = 1;
+ btree->first_free = (char *)&(btree->u.internal[1]) - (char *)btree;
+ btree->u.internal[0].file_secno = -1;
+ btree->u.internal[0].down = na;
+ mark_buffer_dirty(bh, 1);
+ } else if (!(ranode = hpfs_alloc_anode(s, /*a*/0, &ra, &bh2))) {
+ brelse(bh);
+ brelse(bh1);
+ hpfs_free_sectors(s, se, 1);
+ hpfs_free_sectors(s, na, 1);
+ return -1;
+ }
+ brelse(bh);
+ bh = bh1;
+ btree = &anode->btree;
+ }
+ btree->n_free_nodes--; n = btree->n_used_nodes++;
+ btree->first_free += 12;
+ btree->u.external[n].disk_secno = se;
+ btree->u.external[n].file_secno = fs;
+ btree->u.external[n].length = 1;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ if ((a == node && fnod) || na == -1) return se;
+ c2 = 0;
+ while (up != -1) {
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, up, &c1, &c2, "hpfs_add_sector_to_btree #2")) return -1;
+ if (up != node || !fnod) {
+ if (!(anode = hpfs_map_anode(s, up, &bh))) return -1;
+ btree = &anode->btree;
+ } else {
+ if (!(fnode = hpfs_map_fnode(s, up, &bh))) return -1;
+ btree = &fnode->btree;
+ }
+ if (btree->n_free_nodes) {
+ btree->n_free_nodes--; n = btree->n_used_nodes++;
+ btree->first_free += 8;
+ btree->u.internal[n].file_secno = -1;
+ btree->u.internal[n].down = na;
+ btree->u.internal[n-1].file_secno = fs;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ brelse(bh2);
+ hpfs_free_sectors(s, ra, 1);
+ if ((anode = hpfs_map_anode(s, na, &bh))) {
+ anode->up = up;
+ anode->btree.fnode_parent = up == node && fnod;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ }
+ return se;
+ }
+ up = up != node ? anode->up : -1;
+ btree->u.internal[btree->n_used_nodes - 1].file_secno = /*fs*/-1;
+ if (up == -1) anode->up = ra;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ a = na;
+ if ((anode = hpfs_alloc_anode(s, a, &na, &bh))) {
+ /*anode->up = up != -1 ? up : ra;*/
+ anode->btree.internal = 1;
+ anode->btree.n_used_nodes = 1;
+ anode->btree.n_free_nodes = 59;
+ anode->btree.first_free = 16;
+ anode->btree.u.internal[0].down = a;
+ anode->btree.u.internal[0].file_secno = -1;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ if ((anode = hpfs_map_anode(s, a, &bh))) {
+ anode->up = na;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ }
+ } else na = a;
+ }
+ if ((anode = hpfs_map_anode(s, na, &bh))) {
+ anode->up = node;
+ if (fnod) anode->btree.fnode_parent = 1;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ }
+ if (!fnod) {
+ if (!(anode = hpfs_map_anode(s, node, &bh))) {
+ brelse(bh2);
+ return -1;
+ }
+ btree = &anode->btree;
+ } else {
+ if (!(fnode = hpfs_map_fnode(s, node, &bh))) {
+ brelse(bh2);
+ return -1;
+ }
+ btree = &fnode->btree;
+ }
+ ranode->up = node;
+ memcpy(&ranode->btree, btree, btree->first_free);
+ if (fnod) ranode->btree.fnode_parent = 1;
+ ranode->btree.n_free_nodes = (ranode->btree.internal ? 60 : 40) - ranode->btree.n_used_nodes;
+ if (ranode->btree.internal) for (n = 0; n < ranode->btree.n_used_nodes; n++) {
+ struct anode *unode;
+ if ((unode = hpfs_map_anode(s, ranode->u.internal[n].down, &bh1))) {
+ unode->up = ra;
+ unode->btree.fnode_parent = 0;
+ mark_buffer_dirty(bh1, 1);
+ brelse(bh1);
+ }
+ }
+ btree->internal = 1;
+ btree->n_free_nodes = fnod ? 10 : 58;
+ btree->n_used_nodes = 2;
+ btree->first_free = (char *)&btree->u.internal[2] - (char *)btree;
+ btree->u.internal[0].file_secno = fs;
+ btree->u.internal[0].down = ra;
+ btree->u.internal[1].file_secno = -1;
+ btree->u.internal[1].down = na;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ mark_buffer_dirty(bh2, 1);
+ brelse(bh2);
+ return se;
+}
+
+/*
+ * Remove allocation tree. Recursion would look much nicer but
+ * I want to avoid it because it can cause stack overflow.
+ */
+
+void hpfs_remove_btree(struct super_block *s, struct bplus_header *btree)
+{
+ struct bplus_header *btree1 = btree;
+ struct anode *anode = NULL;
+ anode_secno ano = 0, oano;
+ struct buffer_head *bh;
+ int level = 0;
+ int pos = 0;
+ int i;
+ int c1, c2 = 0;
+ int d1, d2;
+ go_down:
+ d2 = 0;
+ while (btree1->internal) {
+ ano = btree1->u.internal[pos].down;
+ if (level) brelse(bh);
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, ano, &d1, &d2, "hpfs_remove_btree #1"))
+ return;
+ anode = hpfs_map_anode(s, ano, &bh);
+ btree1 = &anode->btree;
+ level++;
+ pos = 0;
+ }
+ for (i = 0; i < btree1->n_used_nodes; i++)
+ hpfs_free_sectors(s, btree1->u.external[i].disk_secno, btree1->u.external[i].length);
+ go_up:
+ if (!level) return;
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, ano, &c1, &c2, "hpfs_remove_btree #2")) return;
+ hpfs_free_sectors(s, ano, 1);
+ oano = ano;
+ ano = anode->up;
+ brelse(bh);
+ if (--level) {
+ anode = hpfs_map_anode(s, ano, &bh);
+ btree1 = &anode->btree;
+ } else btree1 = btree;
+ for (i = 0; i < btree1->n_used_nodes; i++)
+ if (btree1->u.internal[i].down == oano)
+ if ((pos = i + 1) < btree1->n_used_nodes) goto go_down;
+ else goto go_up;
+ hpfs_error(s, "reference to anode %08x not found in anode %08x (probably bad up pointer)",
+ oano, level ? ano : -1);
+ if (level) brelse(bh);
+}
+
+/* Just a wrapper around hpfs_bplus_lookup .. used for reading eas */
+
+static secno anode_lookup(struct super_block *s, anode_secno a, unsigned sec)
+{
+ struct anode *anode;
+ struct buffer_head *bh;
+ if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
+ return hpfs_bplus_lookup(s, NULL, &anode->btree, sec, bh);
+}
+
+int hpfs_ea_read(struct super_block *s, secno a, int ano, unsigned pos,
+ unsigned len, char *buf)
+{
+ struct buffer_head *bh;
+ char *data;
+ secno sec;
+ unsigned l;
+ while (len) {
+ if (ano) {
+ if ((sec = anode_lookup(s, a, pos >> 9)) == -1)
+ return -1;
+ } else sec = a + (pos >> 9);
+ if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #1")) return -1;
+ if (!(data = hpfs_map_sector(s, sec, &bh, (len - 1) >> 9)))
+ return -1;
+ l = 0x200 - (pos & 0x1ff); if (l > len) l = len;
+ memcpy(buf, data + (pos & 0x1ff), l);
+ brelse(bh);
+ buf += l; pos += l; len -= l;
+ }
+ return 0;
+}
+
+int hpfs_ea_write(struct super_block *s, secno a, int ano, unsigned pos,
+ unsigned len, char *buf)
+{
+ struct buffer_head *bh;
+ char *data;
+ secno sec;
+ unsigned l;
+ while (len) {
+ if (ano) {
+ if ((sec = anode_lookup(s, a, pos >> 9)) == -1)
+ return -1;
+ } else sec = a + (pos >> 9);
+ if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #2")) return -1;
+ if (!(data = hpfs_map_sector(s, sec, &bh, (len - 1) >> 9)))
+ return -1;
+ l = 0x200 - (pos & 0x1ff); if (l > len) l = len;
+ memcpy(data + (pos & 0x1ff), buf, l);
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ buf += l; pos += l; len -= l;
+ }
+ return 0;
+}
+
+void hpfs_ea_remove(struct super_block *s, secno a, int ano, unsigned len)
+{
+ struct anode *anode;
+ struct buffer_head *bh;
+ if (ano) {
+ if (!(anode = hpfs_map_anode(s, a, &bh))) return;
+ hpfs_remove_btree(s, &anode->btree);
+ brelse(bh);
+ hpfs_free_sectors(s, a, 1);
+ } else hpfs_free_sectors(s, a, (len + 511) >> 9);
+}
+
+/* Truncate allocation tree. Doesn't join anodes - I hope it doesn't matter */
+
+void hpfs_truncate_btree(struct super_block *s, secno f, int fno, unsigned secs)
+{
+ struct fnode *fnode;
+ struct anode *anode;
+ struct buffer_head *bh;
+ struct bplus_header *btree;
+ anode_secno node = f;
+ int i, j, nodes;
+ int c1, c2 = 0;
+ if (fno) {
+ if (!(fnode = hpfs_map_fnode(s, f, &bh))) return;
+ btree = &fnode->btree;
+ } else {
+ if (!(anode = hpfs_map_anode(s, f, &bh))) return;
+ btree = &anode->btree;
+ }
+ if (!secs) {
+ hpfs_remove_btree(s, btree);
+ if (fno) {
+ btree->n_free_nodes = 8;
+ btree->n_used_nodes = 0;
+ btree->first_free = 8;
+ btree->internal = 0;
+ mark_buffer_dirty(bh, 1);
+ } else hpfs_free_sectors(s, f, 1);
+ brelse(bh);
+ return;
+ }
+ while (btree->internal) {
+ nodes = btree->n_used_nodes + btree->n_free_nodes;
+ for (i = 0; i < btree->n_used_nodes; i++)
+ if (btree->u.internal[i].file_secno >= secs) goto f;
+ brelse(bh);
+ hpfs_error(s, "internal btree %08x doesn't end with -1", node);
+ return;
+ f:
+ for (j = i + 1; j < btree->n_used_nodes; j++)
+ hpfs_ea_remove(s, btree->u.internal[j].down, 1, 0);
+ btree->n_used_nodes = i + 1;
+ btree->n_free_nodes = nodes - btree->n_used_nodes;
+ btree->first_free = 8 + 8 * btree->n_used_nodes;
+ mark_buffer_dirty(bh, 1);
+ if (btree->u.internal[i].file_secno == secs) {
+ brelse(bh);
+ return;
+ }
+ node = btree->u.internal[i].down;
+ brelse(bh);
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, node, &c1, &c2, "hpfs_truncate_btree"))
+ return;
+ if (!(anode = hpfs_map_anode(s, node, &bh))) return;
+ btree = &anode->btree;
+ }
+ nodes = btree->n_used_nodes + btree->n_free_nodes;
+ for (i = 0; i < btree->n_used_nodes; i++)
+ if (btree->u.external[i].file_secno + btree->u.external[i].length >= secs) goto ff;
+ brelse(bh);
+ return;
+ ff:
+ if (secs <= btree->u.external[i].file_secno) {
+ hpfs_error(s, "there is an allocation error in file %08x, sector %08x", f, secs);
+ if (i) i--;
+ }
+ else if (btree->u.external[i].file_secno + btree->u.external[i].length > secs) {
+ hpfs_free_sectors(s, btree->u.external[i].disk_secno + secs -
+ btree->u.external[i].file_secno, btree->u.external[i].length
+ - secs + btree->u.external[i].file_secno); /* I hope gcc optimizes this :-) */
+ btree->u.external[i].length = secs - btree->u.external[i].file_secno;
+ }
+ for (j = i + 1; j < btree->n_used_nodes; j++)
+ hpfs_free_sectors(s, btree->u.external[j].disk_secno, btree->u.external[j].length);
+ btree->n_used_nodes = i + 1;
+ btree->n_free_nodes = nodes - btree->n_used_nodes;
+ btree->first_free = 8 + 12 * btree->n_used_nodes;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+}
+
+/* Remove file or directory and it's eas - note that directory must
+ be empty when this is called. */
+
+void hpfs_remove_fnode(struct super_block *s, fnode_secno fno)
+{
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ struct extended_attribute *ea;
+ struct extended_attribute *ea_end;
+ if (!(fnode = hpfs_map_fnode(s, fno, &bh))) return;
+ if (!fnode->dirflag) hpfs_remove_btree(s, &fnode->btree);
+ else hpfs_remove_dtree(s, fnode->u.external[0].disk_secno);
+ ea_end = fnode_end_ea(fnode);
+ for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
+ if (ea->indirect)
+ hpfs_ea_remove(s, ea_sec(ea), ea->anode, ea_len(ea));
+ hpfs_ea_ext_remove(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l);
+ brelse(bh);
+ hpfs_free_sectors(s, fno, 1);
+}
diff --git a/fs/hpfs/buffer.c b/fs/hpfs/buffer.c
new file mode 100644
index 000000000..8bcbf28fc
--- /dev/null
+++ b/fs/hpfs/buffer.c
@@ -0,0 +1,265 @@
+/*
+ * linux/fs/hpfs/buffer.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * general buffer i/o
+ */
+
+#include <linux/string.h>
+#include "hpfs_fn.h"
+
+void hpfs_lock_creation(struct super_block *s)
+{
+#ifdef DEBUG_LOCKS
+ printk("lock creation\n");
+#endif
+ while (s->s_hpfs_creation_de_lock) sleep_on(&s->s_hpfs_creation_de);
+ s->s_hpfs_creation_de_lock = 1;
+}
+
+void hpfs_unlock_creation(struct super_block *s)
+{
+#ifdef DEBUG_LOCKS
+ printk("unlock creation\n");
+#endif
+ s->s_hpfs_creation_de_lock = 0;
+ wake_up(&s->s_hpfs_creation_de);
+}
+
+void hpfs_lock_iget(struct super_block *s, int mode)
+{
+#ifdef DEBUG_LOCKS
+ printk("lock iget\n");
+#endif
+ while (s->s_hpfs_rd_inode) sleep_on(&s->s_hpfs_iget_q);
+ s->s_hpfs_rd_inode = mode;
+}
+
+void hpfs_unlock_iget(struct super_block *s)
+{
+#ifdef DEBUG_LOCKS
+ printk("unlock iget\n");
+#endif
+ s->s_hpfs_rd_inode = 0;
+ wake_up(&s->s_hpfs_iget_q);
+}
+
+void hpfs_lock_inode(struct inode *i)
+{
+ if (i) down(&i->i_hpfs_sem);
+}
+
+void hpfs_unlock_inode(struct inode *i)
+{
+ if (i) up(&i->i_hpfs_sem);
+}
+
+void hpfs_lock_2inodes(struct inode *i1, struct inode *i2)
+{
+ if (!i1) { if (i2) down(&i2->i_hpfs_sem); return; }
+ if (!i2) { if (i1) down(&i1->i_hpfs_sem); return; }
+ if (i1->i_ino < i2->i_ino) {
+ down(&i1->i_hpfs_sem);
+ down(&i2->i_hpfs_sem);
+ } else if (i1->i_ino > i2->i_ino) {
+ down(&i2->i_hpfs_sem);
+ down(&i1->i_hpfs_sem);
+ } else down(&i1->i_hpfs_sem);
+}
+
+void hpfs_unlock_2inodes(struct inode *i1, struct inode *i2)
+{
+ if (!i1) { if (i2) up(&i2->i_hpfs_sem); return; }
+ if (!i2) { if (i1) up(&i1->i_hpfs_sem); return; }
+ if (i1->i_ino < i2->i_ino) {
+ up(&i2->i_hpfs_sem);
+ up(&i1->i_hpfs_sem);
+ } else if (i1->i_ino > i2->i_ino) {
+ up(&i1->i_hpfs_sem);
+ up(&i2->i_hpfs_sem);
+ } else up(&i1->i_hpfs_sem);
+}
+
+void hpfs_lock_3inodes(struct inode *i1, struct inode *i2, struct inode *i3)
+{
+ if (!i1) { hpfs_lock_2inodes(i2, i3); return; }
+ if (!i2) { hpfs_lock_2inodes(i1, i3); return; }
+ if (!i3) { hpfs_lock_2inodes(i1, i2); return; }
+ if (i1->i_ino < i2->i_ino && i1->i_ino < i3->i_ino) {
+ down(&i1->i_hpfs_sem);
+ hpfs_lock_2inodes(i2, i3);
+ } else if (i2->i_ino < i1->i_ino && i2->i_ino < i3->i_ino) {
+ down(&i2->i_hpfs_sem);
+ hpfs_lock_2inodes(i1, i3);
+ } else if (i3->i_ino < i1->i_ino && i3->i_ino < i2->i_ino) {
+ down(&i3->i_hpfs_sem);
+ hpfs_lock_2inodes(i1, i2);
+ } else if (i1->i_ino != i2->i_ino) hpfs_lock_2inodes(i1, i2);
+ else hpfs_lock_2inodes(i1, i3);
+}
+
+void hpfs_unlock_3inodes(struct inode *i1, struct inode *i2, struct inode *i3)
+{
+ if (!i1) { hpfs_unlock_2inodes(i2, i3); return; }
+ if (!i2) { hpfs_unlock_2inodes(i1, i3); return; }
+ if (!i3) { hpfs_unlock_2inodes(i1, i2); return; }
+ if (i1->i_ino < i2->i_ino && i1->i_ino < i3->i_ino) {
+ hpfs_unlock_2inodes(i2, i3);
+ up(&i1->i_hpfs_sem);
+ } else if (i2->i_ino < i1->i_ino && i2->i_ino < i3->i_ino) {
+ hpfs_unlock_2inodes(i1, i3);
+ up(&i2->i_hpfs_sem);
+ } else if (i3->i_ino < i1->i_ino && i3->i_ino < i2->i_ino) {
+ hpfs_unlock_2inodes(i1, i2);
+ up(&i3->i_hpfs_sem);
+ } else if (i1->i_ino != i2->i_ino) hpfs_unlock_2inodes(i1, i2);
+ else hpfs_unlock_2inodes(i1, i3);
+}
+
+/* Map a sector into a buffer and return pointers to it and to the buffer. */
+
+void *hpfs_map_sector(struct super_block *s, unsigned secno, struct buffer_head **bhp,
+ int ahead)
+{
+ kdev_t dev = s->s_dev;
+ struct buffer_head *bh;
+
+ if (!ahead || secno + ahead >= s->s_hpfs_fs_size)
+ *bhp = bh = bread(dev, secno, 512);
+ else *bhp = bh = breada(dev, secno, 512, 0, (ahead + 1) << 9);
+ if (bh != NULL)
+ return bh->b_data;
+ else {
+ printk("HPFS: hpfs_map_sector: read error\n");
+ return NULL;
+ }
+}
+
+/* Like hpfs_map_sector but don't read anything */
+
+void *hpfs_get_sector(struct super_block *s, unsigned secno, struct buffer_head **bhp)
+{
+ struct buffer_head *bh;
+ /*return hpfs_map_sector(s, secno, bhp, 0);*/
+
+ if ((*bhp = bh = getblk(s->s_dev, secno, 512)) != NULL) {
+ mark_buffer_uptodate(bh, 1);
+ return bh->b_data;
+ } else {
+ printk("HPFS: hpfs_get_sector: getblk failed\n");
+ return NULL;
+ }
+}
+
+/* Map 4 sectors into a 4buffer and return pointers to it and to the buffer. */
+
+void *hpfs_map_4sectors(struct super_block *s, unsigned secno, struct quad_buffer_head *qbh,
+ int ahead)
+{
+ kdev_t dev = s->s_dev;
+ struct buffer_head *bh;
+ char *data;
+
+ if (secno & 3) {
+ printk("HPFS: hpfs_map_4sectors: unaligned read\n");
+ return 0;
+ }
+
+ qbh->data = data = (char *)kmalloc(2048, GFP_KERNEL);
+ if (!data) {
+ printk("HPFS: hpfs_map_4sectors: out of memory\n");
+ goto bail;
+ }
+
+ if (!ahead || secno + 4 + ahead > s->s_hpfs_fs_size)
+ qbh->bh[0] = bh = breada(dev, secno, 512, 0, 2048);
+ else qbh->bh[0] = bh = breada(dev, secno, 512, 0, (ahead + 4) << 9);
+ if (!bh)
+ goto bail0;
+ memcpy(data, bh->b_data, 512);
+
+ qbh->bh[1] = bh = bread(dev, secno + 1, 512);
+ if (!bh)
+ goto bail1;
+ memcpy(data + 512, bh->b_data, 512);
+
+ qbh->bh[2] = bh = bread(dev, secno + 2, 512);
+ if (!bh)
+ goto bail2;
+ memcpy(data + 2 * 512, bh->b_data, 512);
+
+ qbh->bh[3] = bh = bread(dev, secno + 3, 512);
+ if (!bh)
+ goto bail3;
+ memcpy(data + 3 * 512, bh->b_data, 512);
+
+ return data;
+
+ bail3:
+ brelse(qbh->bh[2]);
+ bail2:
+ brelse(qbh->bh[1]);
+ bail1:
+ brelse(qbh->bh[0]);
+ bail0:
+ kfree_s(data, 2048);
+ printk("HPFS: hpfs_map_4sectors: read error\n");
+ bail:
+ return NULL;
+}
+
+/* Don't read sectors */
+
+void *hpfs_get_4sectors(struct super_block *s, unsigned secno,
+ struct quad_buffer_head *qbh)
+{
+ if (secno & 3) {
+ printk("HPFS: hpfs_get_4sectors: unaligned read\n");
+ return 0;
+ }
+
+ /*return hpfs_map_4sectors(s, secno, qbh, 0);*/
+ if (!(qbh->data = kmalloc(2048, GFP_KERNEL))) {
+ printk("HPFS: hpfs_get_4sectors: out of memory\n");
+ return NULL;
+ }
+ if (!(hpfs_get_sector(s, secno, &qbh->bh[0]))) goto bail0;
+ if (!(hpfs_get_sector(s, secno + 1, &qbh->bh[1]))) goto bail1;
+ if (!(hpfs_get_sector(s, secno + 2, &qbh->bh[2]))) goto bail2;
+ if (!(hpfs_get_sector(s, secno + 3, &qbh->bh[3]))) goto bail3;
+ memcpy(qbh->data, qbh->bh[0]->b_data, 512);
+ memcpy(qbh->data + 512, qbh->bh[1]->b_data, 512);
+ memcpy(qbh->data + 2*512, qbh->bh[2]->b_data, 512);
+ memcpy(qbh->data + 3*512, qbh->bh[3]->b_data, 512);
+ return qbh->data;
+
+ bail3: brelse(qbh->bh[2]);
+ bail2: brelse(qbh->bh[1]);
+ bail1: brelse(qbh->bh[0]);
+ bail0:
+ return NULL;
+}
+
+
+void hpfs_brelse4(struct quad_buffer_head *qbh)
+{
+ brelse(qbh->bh[3]);
+ brelse(qbh->bh[2]);
+ brelse(qbh->bh[1]);
+ brelse(qbh->bh[0]);
+ kfree_s(qbh->data, 2048);
+}
+
+void hpfs_mark_4buffers_dirty(struct quad_buffer_head *qbh)
+{
+ PRINTK(("hpfs_mark_4buffers_dirty\n"));
+ memcpy(qbh->bh[0]->b_data, qbh->data, 512);
+ memcpy(qbh->bh[1]->b_data, qbh->data + 512, 512);
+ memcpy(qbh->bh[2]->b_data, qbh->data + 2 * 512, 512);
+ memcpy(qbh->bh[3]->b_data, qbh->data + 3 * 512, 512);
+ mark_buffer_dirty(qbh->bh[0],1);
+ mark_buffer_dirty(qbh->bh[1],1);
+ mark_buffer_dirty(qbh->bh[2],1);
+ mark_buffer_dirty(qbh->bh[3],1);
+}
diff --git a/fs/hpfs/dentry.c b/fs/hpfs/dentry.c
new file mode 100644
index 000000000..b5c3cf2e8
--- /dev/null
+++ b/fs/hpfs/dentry.c
@@ -0,0 +1,62 @@
+/*
+ * linux/fs/hpfs/dentry.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * dcache operations
+ */
+
+#include "hpfs_fn.h"
+
+/*
+ * Note: the dentry argument is the parent dentry.
+ */
+
+int hpfs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
+{
+ unsigned long hash;
+ int i;
+ unsigned l = qstr->len;
+
+ if (l == 1) if (qstr->name[0]=='.') goto x;
+ if (l == 2) if (qstr->name[0]=='.' || qstr->name[1]=='.') goto x;
+ hpfs_adjust_length((char *)qstr->name, &l);
+ /*if (hpfs_chk_name((char *)qstr->name,&l))*/
+ /*return -ENAMETOOLONG;*/
+ /*return -ENOENT;*/
+ x:
+
+ hash = init_name_hash();
+ for (i = 0; i < l; i++)
+ hash = partial_name_hash(hpfs_upcase(dentry->d_sb->s_hpfs_cp_table,qstr->name[i]), hash);
+ qstr->hash = end_name_hash(hash);
+
+ return 0;
+}
+
+int hpfs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
+{
+ unsigned al=a->len;
+ unsigned bl=b->len;
+ hpfs_adjust_length((char *)a->name, &al);
+ /*hpfs_adjust_length((char *)b->name, &bl);*/
+ /* 'a' is the qstr of an already existing dentry, so the name
+ * must be valid. 'b' must be validated first.
+ */
+
+ if (hpfs_chk_name((char *)b->name, &bl)) return 1;
+ if (hpfs_compare_names(dentry->d_sb, (char *)a->name, al, (char *)b->name, bl, 0)) return 1;
+ return 0;
+}
+
+struct dentry_operations hpfs_dentry_operations = {
+ NULL, /* d_validate */
+ hpfs_hash_dentry, /* d_hash */
+ hpfs_compare_dentry, /* d_compare */
+ NULL /* d_delete */
+};
+
+void hpfs_set_dentry_operations(struct dentry *dentry)
+{
+ dentry->d_op = &hpfs_dentry_operations;
+}
diff --git a/fs/hpfs/dir.c b/fs/hpfs/dir.c
new file mode 100644
index 000000000..8af35847d
--- /dev/null
+++ b/fs/hpfs/dir.c
@@ -0,0 +1,253 @@
+/*
+ * linux/fs/hpfs/dir.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * directory VFS functions
+ */
+
+#include "hpfs_fn.h"
+
+int hpfs_dir_read(struct file *filp, char *name, size_t len, loff_t *loff)
+{
+ return -EISDIR;
+}
+
+int hpfs_dir_release(struct inode *inode, struct file *filp)
+{
+ hpfs_del_pos(inode, &filp->f_pos);
+ /*hpfs_write_if_changed(inode);*/
+ return 0;
+}
+
+int hpfs_readdir(struct file *filp, void * dirent, filldir_t filldir)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de;
+ int lc;
+ long old_pos;
+ char *tempname;
+ int c1, c2 = 0;
+
+ if (inode->i_sb->s_hpfs_chk) {
+ if (hpfs_chk_sectors(inode->i_sb, inode->i_ino, 1, "dir_fnode"))
+ return -EFSERROR;
+ if (hpfs_chk_sectors(inode->i_sb, inode->i_hpfs_dno, 4, "dir_dnode"))
+ return -EFSERROR;
+ }
+ if (inode->i_sb->s_hpfs_chk >= 2) {
+ struct buffer_head *bh;
+ struct fnode *fno;
+ int e = 0;
+ if (!(fno = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh)))
+ return -EIOERROR;
+ if (!fno->dirflag) {
+ e = 1;
+ hpfs_error(inode->i_sb, "not a directory, fnode %08x",inode->i_ino);
+ }
+ if (inode->i_hpfs_dno != fno->u.external[0].disk_secno) {
+ e = 1;
+ hpfs_error(inode->i_sb, "corrupted inode: i_hpfs_dno == %08x, fnode -> dnode == %08x", inode->i_hpfs_dno, fno->u.external[0].disk_secno);
+ }
+ brelse(bh);
+ if (e) return -EFSERROR;
+ }
+ lc = inode->i_sb->s_hpfs_lowercase;
+ if (filp->f_pos == -2) { /* diff -r requires this (note, that diff -r */
+ filp->f_pos = -3; /* also fails on msdos filesystem in 2.0) */
+ return 0;
+ }
+ if (filp->f_pos == -3) return -ENOENT;
+
+ hpfs_lock_inode(inode);
+
+ while (1) {
+ again:
+ /* This won't work when cycle is longer than number of dirents
+ accepted by filldir, but what can I do?
+ maybe killall -9 ls helps */
+ if (inode->i_sb->s_hpfs_chk)
+ if (hpfs_stop_cycles(inode->i_sb, filp->f_pos, &c1, &c2, "hpfs_readdir")) {
+ hpfs_unlock_inode(inode);
+ return -EFSERROR;
+ }
+ if (filp->f_pos == -2) {
+ hpfs_unlock_inode(inode);
+ return 0;
+ }
+ if (filp->f_pos == 3 || filp->f_pos == 4 || filp->f_pos == 5) {
+ printk("HPFS: warning: pos==%d\n",(int)filp->f_pos);
+ hpfs_unlock_inode(inode);
+ return 0;
+ }
+ if (filp->f_pos == 0) {
+ if (filldir(dirent, ".", 1, filp->f_pos, inode->i_ino) < 0) {
+ hpfs_unlock_inode(inode);
+ return 0;
+ }
+ filp->f_pos = -1;
+ }
+ if (filp->f_pos == -1) {
+ if (filldir(dirent, "..", 2, filp->f_pos, inode->i_hpfs_parent_dir) < 0) {
+ hpfs_unlock_inode(inode);
+ return 0;
+ }
+ filp->f_pos = 1;
+ }
+ if (filp->f_pos == 1) {
+ filp->f_pos = ((loff_t) hpfs_de_as_down_as_possible(inode->i_sb, inode->i_hpfs_dno) << 4) + 1;
+ hpfs_add_pos(inode, &filp->f_pos);
+ filp->f_version = inode->i_version;
+ }
+ /*if (filp->f_version != inode->i_version) {
+ hpfs_unlock_inode(inode);
+ return -ENOENT;
+ }*/
+ old_pos = filp->f_pos;
+ if (!(de = map_pos_dirent(inode, &filp->f_pos, &qbh))) {
+ hpfs_unlock_inode(inode);
+ return -EIOERROR;
+ }
+ if (de->first || de->last) {
+ if (inode->i_sb->s_hpfs_chk) {
+ if (de->first && !de->last && (de->namelen != 2 || de ->name[0] != 1 || de->name[1] != 1)) hpfs_error(inode->i_sb, "hpfs_readdir: bad ^A^A entry; pos = %08x", old_pos);
+ if (de->last && (de->namelen != 1 || de ->name[0] != 255)) hpfs_error(inode->i_sb, "hpfs_readdir: bad \\377 entry; pos = %08x", old_pos);
+ }
+ hpfs_brelse4(&qbh);
+ goto again;
+ }
+ tempname = hpfs_translate_name(inode->i_sb, de->name, de->namelen, lc, de->not_8x3);
+ if (filldir(dirent, tempname, de->namelen, old_pos, de->fnode) < 0) {
+ filp->f_pos = old_pos;
+ if (tempname != (char *)de->name) kfree(tempname);
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_inode(inode);
+ return 0;
+ }
+ if (tempname != (char *)de->name) kfree(tempname);
+ hpfs_brelse4(&qbh);
+ }
+}
+
+/*
+ * lookup. Search the specified directory for the specified name, set
+ * *result to the corresponding inode.
+ *
+ * lookup uses the inode number to tell read_inode whether it is reading
+ * the inode of a directory or a file -- file ino's are odd, directory
+ * ino's are even. read_inode avoids i/o for file inodes; everything
+ * needed is up here in the directory. (And file fnodes are out in
+ * the boondocks.)
+ *
+ * - M.P.: this is over, sometimes we've got to read file's fnode for eas
+ * inode numbers are just fnode sector numbers; iget lock is used
+ * to tell read_inode to read fnode or not.
+ */
+
+struct dentry *hpfs_lookup(struct inode *dir, struct dentry *dentry)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de;
+ ino_t ino;
+ int err;
+ struct inode *result = NULL;
+
+ if ((err = hpfs_chk_name((char *)name, &len))) {
+ if (err == -ENAMETOOLONG) return ERR_PTR(-ENAMETOOLONG);
+ goto end_add;
+ }
+
+ hpfs_lock_inode(dir);
+ /*
+ * '.' and '..' will never be passed here.
+ */
+
+ de = map_dirent(dir, dir->i_hpfs_dno, (char *) name, len, NULL, &qbh);
+
+ /*
+ * This is not really a bailout, just means file not found.
+ */
+
+ if (!de) goto end;
+
+ /*
+ * Get inode number, what we're after.
+ */
+
+ ino = de->fnode;
+
+ /*
+ * Go find or make an inode.
+ */
+
+ hpfs_lock_iget(dir->i_sb, de->directory || (de->ea_size && dir->i_sb->s_hpfs_eas) ? 1 : 2);
+ if (!(result = iget(dir->i_sb, ino))) {
+ hpfs_unlock_iget(dir->i_sb);
+ hpfs_error(result->i_sb, "hpfs_lookup: can't get inode");
+ goto bail1;
+ }
+ if (!de->directory) result->i_hpfs_parent_dir = dir->i_ino;
+ hpfs_unlock_iget(dir->i_sb);
+
+ hpfs_decide_conv(result, (char *)name, len);
+
+ if (de->has_acl || de->has_xtd_perm) if (!(dir->i_sb->s_flags & MS_RDONLY)) {
+ hpfs_error(result->i_sb, "ACLs or XPERM found. This is probably HPFS386. This driver doesn't support it now. Send me some info on these structures");
+ goto bail1;
+ }
+
+ /*
+ * Fill in the info from the directory if this is a newly created
+ * inode.
+ */
+
+ if (!result->i_ctime) {
+ if (!(result->i_ctime = local_to_gmt(dir->i_sb, de->creation_date)))
+ result->i_ctime = 1;
+ result->i_mtime = local_to_gmt(dir->i_sb, de->write_date);
+ result->i_atime = local_to_gmt(dir->i_sb, de->read_date);
+ result->i_hpfs_ea_size = de->ea_size;
+ if (!result->i_hpfs_ea_mode && de->read_only)
+ result->i_mode &= ~0222;
+ if (!de->directory) {
+ if (result->i_size == -1) {
+ result->i_size = de->file_size;
+ /*
+ * i_blocks should count the fnode and any anodes.
+ * We count 1 for the fnode and don't bother about
+ * anodes -- the disk heads are on the directory band
+ * and we want them to stay there.
+ */
+ result->i_blocks = 1 + ((result->i_size + 511) >> 9);
+ }
+ }
+ }
+
+ hpfs_brelse4(&qbh);
+
+ /*
+ * Made it.
+ */
+
+ end:
+ hpfs_unlock_inode(dir);
+ end_add:
+ hpfs_set_dentry_operations(dentry);
+ d_add(dentry, result);
+ return NULL;
+
+ /*
+ * Didn't.
+ */
+ bail1:
+
+ hpfs_brelse4(&qbh);
+
+ /*bail:*/
+
+ hpfs_unlock_inode(dir);
+ return ERR_PTR(-ENOENT);
+}
diff --git a/fs/hpfs/dnode.c b/fs/hpfs/dnode.c
new file mode 100644
index 000000000..e4b4bbc91
--- /dev/null
+++ b/fs/hpfs/dnode.c
@@ -0,0 +1,1070 @@
+/*
+ * linux/fs/hpfs/dnode.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * handling directory dnode tree - adding, deleteing & searching for dirents
+ */
+
+#include "hpfs_fn.h"
+
+static loff_t get_pos(struct dnode *d, struct hpfs_dirent *fde)
+{
+ struct hpfs_dirent *de;
+ struct hpfs_dirent *de_end = dnode_end_de(d);
+ int i = 1;
+ for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+ if (de == fde) return ((loff_t) d->self << 4) | (loff_t)i;
+ i++;
+ }
+ printk("HPFS: get_pos: not_found\n");
+ return ((loff_t)d->self << 4) | (loff_t)1;
+}
+
+void hpfs_add_pos(struct inode *inode, loff_t *pos)
+{
+ int i = 0;
+ loff_t **ppos;
+ if (inode->i_hpfs_rddir_off)
+ for (; inode->i_hpfs_rddir_off[i]; i++)
+ if (inode->i_hpfs_rddir_off[i] == pos) return;
+ if (!(i&0x0f)) {
+ if (!(ppos = kmalloc((i+0x11) * sizeof(loff_t*), GFP_KERNEL))) {
+ printk("HPFS: out of memory for position list\n");
+ return;
+ }
+ if (inode->i_hpfs_rddir_off) {
+ memcpy(ppos, inode->i_hpfs_rddir_off, i * sizeof(loff_t));
+ kfree(inode->i_hpfs_rddir_off);
+ }
+ inode->i_hpfs_rddir_off = ppos;
+ }
+ inode->i_hpfs_rddir_off[i] = pos;
+ inode->i_hpfs_rddir_off[i + 1] = NULL;
+}
+
+void hpfs_del_pos(struct inode *inode, loff_t *pos)
+{
+ loff_t **i, **j;
+ if (!inode->i_hpfs_rddir_off) goto not_f;
+ for (i = inode->i_hpfs_rddir_off; *i; i++) if (*i == pos) goto fnd;
+ goto not_f;
+ fnd:
+ for (j = i + 1; *j; j++) ;
+ *i = *(j - 1);
+ *(j - 1) = NULL;
+ if (j - 1 == inode->i_hpfs_rddir_off) {
+ kfree(inode->i_hpfs_rddir_off);
+ inode->i_hpfs_rddir_off = NULL;
+ }
+ return;
+ not_f:
+ /*printk("HPFS: warning: position pointer %p->%08x not found\n", pos, (int)*pos);*/
+ return;
+}
+
+static void for_all_poss(struct inode *inode, void (*f)(loff_t *, loff_t, loff_t),
+ loff_t p1, loff_t p2)
+{
+ loff_t **i;
+ if (!inode->i_hpfs_rddir_off) return;
+ for (i = inode->i_hpfs_rddir_off; *i; i++) (*f)(*i, p1, p2);
+ return;
+}
+
+void hpfs_pos_subst(loff_t *p, loff_t f, loff_t t)
+{
+ if (*p == f) *p = t;
+}
+
+/*void hpfs_hpfs_pos_substd(loff_t *p, loff_t f, loff_t t)
+{
+ if ((*p & ~0x3f) == (f & ~0x3f)) *p = (t & ~0x3f) | (*p & 0x3f);
+}*/
+
+void hpfs_pos_ins(loff_t *p, loff_t d, loff_t c)
+{
+ if ((*p & ~0x3f) == (d & ~0x3f) && (*p & 0x3f) >= (d & 0x3f)) {
+ int n = (*p & 0x3f) + c;
+ if (n > 0x3f) printk("HPFS: hpfs_pos_ins: %08x + %d\n", (int)*p, (int)c >> 8);
+ else *p = (*p & ~0x3f) | n;
+ }
+}
+
+void hpfs_pos_del(loff_t *p, loff_t d, loff_t c)
+{
+ if ((*p & ~0x3f) == (d & ~0x3f) && (*p & 0x3f) >= (d & 0x3f)) {
+ int n = (*p & 0x3f) - c;
+ if (n < 1) printk("HPFS: hpfs_pos_ins: %08x - %d\n", (int)*p, (int)c >> 8);
+ else *p = (*p & ~0x3f) | n;
+ }
+}
+
+static struct hpfs_dirent *dnode_pre_last_de(struct dnode *d)
+{
+ struct hpfs_dirent *de, *de_end, *dee = NULL, *deee = NULL;
+ de_end = dnode_end_de(d);
+ for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+ deee = dee; dee = de;
+ }
+ return deee;
+}
+
+static struct hpfs_dirent *dnode_last_de(struct dnode *d)
+{
+ struct hpfs_dirent *de, *de_end, *dee = NULL;
+ de_end = dnode_end_de(d);
+ for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+ dee = de;
+ }
+ return dee;
+}
+
+static void set_last_pointer(struct super_block *s, struct dnode *d, dnode_secno ptr)
+{
+ struct hpfs_dirent *de;
+ if (!(de = dnode_last_de(d))) {
+ hpfs_error(s, "set_last_pointer: empty dnode %08x", d->self);
+ return;
+ }
+ if (s->s_hpfs_chk) {
+ if (de->down) {
+ hpfs_error(s, "set_last_pointer: dnode %08x has already last pointer %08x",
+ d->self, de_down_pointer(de));
+ return;
+ }
+ if (de->length != 32) {
+ hpfs_error(s, "set_last_pointer: bad last dirent in dnode %08x", d->self);
+ return;
+ }
+ }
+ if (ptr) {
+ if ((d->first_free += 4) > 2048) {
+ hpfs_error(s,"set_last_pointer: too long dnode %08x", d->self);
+ d->first_free -= 4;
+ return;
+ }
+ de->length = 36;
+ de->down = 1;
+ *(dnode_secno *)((char *)de + 32) = ptr;
+ }
+}
+
+/* Add an entry to dnode and don't care if it grows over 2048 bytes */
+
+struct hpfs_dirent *hpfs_add_de(struct super_block *s, struct dnode *d, unsigned char *name,
+ unsigned namelen, secno down_ptr)
+{
+ struct hpfs_dirent *de;
+ struct hpfs_dirent *de_end = dnode_end_de(d);
+ unsigned d_size = de_size(namelen, down_ptr);
+ for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+ int c = hpfs_compare_names(s, name, namelen, de->name, de->namelen, de->last);
+ if (!c) {
+ hpfs_error(s, "name (%c,%d) already exists in dnode %08x", *name, namelen, d->self);
+ return NULL;
+ }
+ if (c < 0) break;
+ }
+ memmove((char *)de + d_size, de, (char *)de_end - (char *)de);
+ memset(de, 0, d_size);
+ if (down_ptr) {
+ *(int *)((char *)de + d_size - 4) = down_ptr;
+ de->down = 1;
+ }
+ de->length = d_size;
+ if (down_ptr) de->down = 1;
+ de->not_8x3 = hpfs_is_name_long(name, namelen);
+ de->namelen = namelen;
+ memcpy(de->name, name, namelen);
+ d->first_free += d_size;
+ return de;
+}
+
+/* Delete dirent and don't care about it's subtree */
+
+void hpfs_delete_de(struct super_block *s, struct dnode *d, struct hpfs_dirent *de)
+{
+ if (de->last) {
+ hpfs_error(s, "attempt to delete last dirent in dnode %08x", d->self);
+ return;
+ }
+ d->first_free -= de->length;
+ memmove(de, de_next_de(de), d->first_free + (char *)d - (char *)de);
+}
+
+static void fix_up_ptrs(struct super_block *s, struct dnode *d)
+{
+ struct hpfs_dirent *de;
+ struct hpfs_dirent *de_end = dnode_end_de(d);
+ dnode_secno dno = d->self;
+ for (de = dnode_first_de(d); de < de_end; de = de_next_de(de))
+ if (de->down) {
+ struct quad_buffer_head qbh;
+ struct dnode *dd;
+ if ((dd = hpfs_map_dnode(s, de_down_pointer(de), &qbh))) {
+ if (dd->up != dno || dd->root_dnode) {
+ dd->up = dno;
+ dd->root_dnode = 0;
+ hpfs_mark_4buffers_dirty(&qbh);
+ }
+ hpfs_brelse4(&qbh);
+ }
+ }
+}
+
+/* Add an entry to dnode and do dnode splitting if required */
+
+int hpfs_add_to_dnode(struct inode *i, dnode_secno dno, unsigned char *name, unsigned namelen,
+ struct hpfs_dirent *new_de, dnode_secno down_ptr)
+{
+ struct quad_buffer_head qbh, qbh1, qbh2;
+ struct dnode *d, *ad, *rd, *nd = NULL;
+ dnode_secno adno, rdno;
+ struct hpfs_dirent *de;
+ struct hpfs_dirent nde;
+ char *nname;
+ int h;
+ int pos;
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ int c1, c2 = 0;
+ if (!(nname = kmalloc(256, GFP_KERNEL))) {
+ printk("HPFS: out of memory, can't add to dnode\n");
+ return 1;
+ }
+ go_up:
+ if (namelen >= 256) {
+ hpfs_error(i->i_sb, "hpfs_add_to_dnode: namelen == %d", namelen);
+ if (nd) kfree(nd);
+ kfree(nname);
+ return 1;
+ }
+ if (!(d = hpfs_map_dnode(i->i_sb, dno, &qbh))) {
+ if (nd) kfree(nd);
+ kfree(nname);
+ return 1;
+ }
+ go_up_a:
+ if (i->i_sb->s_hpfs_chk)
+ if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "hpfs_add_to_dnode")) {
+ hpfs_brelse4(&qbh);
+ if (nd) kfree(nd);
+ kfree(nname);
+ return 1;
+ }
+ if (d->first_free + de_size(namelen, down_ptr) <= 2048) {
+ loff_t t;
+ copy_de(de=hpfs_add_de(i->i_sb, d, name, namelen, down_ptr), new_de);
+ t = get_pos(d, de);
+ for_all_poss(i, hpfs_pos_ins, t, 1);
+ for_all_poss(i, hpfs_pos_subst, 4, t);
+ for_all_poss(i, hpfs_pos_subst, 5, t + 1);
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ if (nd) kfree(nd);
+ kfree(nname);
+ return 0;
+ }
+ if (!nd) if (!(nd = kmalloc(0x924, GFP_KERNEL))) {
+ /* 0x924 is a max size of dnode after adding a dirent with
+ max name length. We alloc this only once. There must
+ not be any error while splitting dnodes, otherwise the
+ whole directory, not only file we're adding, would
+ be lost. */
+ printk("HPFS: out of memory for dnode splitting\n");
+ hpfs_brelse4(&qbh);
+ kfree(nname);
+ return 1;
+ }
+ memcpy(nd, d, d->first_free);
+ copy_de(de = hpfs_add_de(i->i_sb, nd, name, namelen, down_ptr), new_de);
+ for_all_poss(i, hpfs_pos_ins, get_pos(nd, de), 1);
+ h = ((char *)dnode_last_de(nd) - (char *)nd) / 2 + 10;
+ if (!(ad = hpfs_alloc_dnode(i->i_sb, d->up, &adno, &qbh1, 0))) {
+ hpfs_error(i->i_sb, "unable to alloc dnode - dnode tree will be corrupted");
+ hpfs_brelse4(&qbh);
+ kfree(nd);
+ kfree(nname);
+ return 1;
+ }
+ i->i_size += 2048;
+ i->i_blocks += 4;
+ pos = 1;
+ for (de = dnode_first_de(nd); (char *)de_next_de(de) - (char *)nd < h; de = de_next_de(de)) {
+ copy_de(hpfs_add_de(i->i_sb, ad, de->name, de->namelen, de->down ? de_down_pointer(de) : 0), de);
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | pos, ((loff_t)adno << 4) | pos);
+ pos++;
+ }
+ copy_de(new_de = &nde, de);
+ memcpy(name = nname, de->name, namelen = de->namelen);
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | pos, 4);
+ down_ptr = adno;
+ set_last_pointer(i->i_sb, ad, de->down ? de_down_pointer(de) : 0);
+ de = de_next_de(de);
+ memmove((char *)nd + 20, de, nd->first_free + (char *)nd - (char *)de);
+ nd->first_free -= (char *)de - (char *)nd - 20;
+ memcpy(d, nd, nd->first_free);
+ for_all_poss(i, hpfs_pos_del, (loff_t)dno << 4, pos);
+ fix_up_ptrs(i->i_sb, ad);
+ if (!d->root_dnode) {
+ dno = ad->up = d->up;
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ goto go_up;
+ }
+ if (!(rd = hpfs_alloc_dnode(i->i_sb, d->up, &rdno, &qbh2, 0))) {
+ hpfs_error(i->i_sb, "unable to alloc dnode - dnode tree will be corrupted");
+ hpfs_brelse4(&qbh);
+ hpfs_brelse4(&qbh1);
+ kfree(nd);
+ kfree(nname);
+ return 1;
+ }
+ i->i_size += 2048;
+ i->i_blocks += 4;
+ rd->root_dnode = 1;
+ rd->up = d->up;
+ if (!(fnode = hpfs_map_fnode(i->i_sb, d->up, &bh))) {
+ hpfs_free_dnode(i->i_sb, rdno);
+ hpfs_brelse4(&qbh);
+ hpfs_brelse4(&qbh1);
+ hpfs_brelse4(&qbh2);
+ kfree(nd);
+ kfree(nname);
+ return 1;
+ }
+ fnode->u.external[0].disk_secno = rdno;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ d->up = ad->up = i->i_hpfs_dno = rdno;
+ d->root_dnode = ad->root_dnode = 0;
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ qbh = qbh2;
+ set_last_pointer(i->i_sb, rd, dno);
+ dno = rdno;
+ d = rd;
+ goto go_up_a;
+}
+
+/*
+ * Add an entry to directory btree.
+ * I hate such crazy directory structure.
+ * It's easy to read but terrible to write.
+ * I wrote this directory code 4 times.
+ * I hope, now it's finally bug-free.
+ */
+
+int hpfs_add_dirent(struct inode *i, unsigned char *name, unsigned namelen,
+ struct hpfs_dirent *new_de, int cdepth)
+{
+ struct dnode *d;
+ struct hpfs_dirent *de, *de_end;
+ struct quad_buffer_head qbh;
+ dnode_secno dno;
+ int c;
+ int c1, c2 = 0;
+ dno = i->i_hpfs_dno;
+ down:
+ if (i->i_sb->s_hpfs_chk)
+ if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "hpfs_add_dirent")) return 1;
+ if (!(d = hpfs_map_dnode(i->i_sb, dno, &qbh))) return 1;
+ de_end = dnode_end_de(d);
+ for (de = dnode_first_de(d); de < de_end; de = de_next_de(de)) {
+ if (!(c = hpfs_compare_names(i->i_sb, name, namelen, de->name, de->namelen, de->last))) {
+ hpfs_brelse4(&qbh);
+ return -1;
+ }
+ if (c < 0) {
+ if (de->down) {
+ dno = de_down_pointer(de);
+ hpfs_brelse4(&qbh);
+ goto down;
+ }
+ break;
+ }
+ }
+ hpfs_brelse4(&qbh);
+ if (!cdepth) hpfs_lock_creation(i->i_sb);
+ if (hpfs_check_free_dnodes(i->i_sb, FREE_DNODES_ADD)) {
+ c = 1;
+ goto ret;
+ }
+ i->i_version = ++event;
+ c = hpfs_add_to_dnode(i, dno, name, namelen, new_de, 0);
+ ret:
+ if (!cdepth) hpfs_unlock_creation(i->i_sb);
+ return c;
+}
+
+/*
+ * Find dirent with higher name in 'from' subtree and move it to 'to' dnode.
+ * Return the dnode we moved from (to be checked later if it's empty)
+ */
+
+static secno move_to_top(struct inode *i, dnode_secno from, dnode_secno to)
+{
+ dnode_secno dno, ddno;
+ dnode_secno chk_up = to;
+ struct dnode *dnode;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de, *nde;
+ int a;
+ loff_t t;
+ int c1, c2 = 0;
+ dno = from;
+ while (1) {
+ if (i->i_sb->s_hpfs_chk)
+ if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "move_to_top"))
+ return 0;
+ if (!(dnode = hpfs_map_dnode(i->i_sb, dno, &qbh))) return 0;
+ if (i->i_sb->s_hpfs_chk) {
+ if (dnode->up != chk_up) {
+ hpfs_error(i->i_sb, "move_to_top: up pointer from %08x should be %08x, is %08x",
+ dno, chk_up, dnode->up);
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ chk_up = dno;
+ }
+ if (!(de = dnode_last_de(dnode))) {
+ hpfs_error(i->i_sb, "move_to_top: dnode %08x has no last de", dno);
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ if (!de->down) break;
+ dno = de_down_pointer(de);
+ hpfs_brelse4(&qbh);
+ }
+ while (!(de = dnode_pre_last_de(dnode))) {
+ dnode_secno up = dnode->up;
+ hpfs_brelse4(&qbh);
+ hpfs_free_dnode(i->i_sb, dno);
+ i->i_size -= 2048;
+ i->i_blocks -= 4;
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, 5);
+ if (up == to) return to;
+ if (!(dnode = hpfs_map_dnode(i->i_sb, up, &qbh))) return 0;
+ if (dnode->root_dnode) {
+ hpfs_error(i->i_sb, "move_to_top: got to root_dnode while moving from %08x to %08x", from, to);
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ de = dnode_last_de(dnode);
+ if (!de || !de->down) {
+ hpfs_error(i->i_sb, "move_to_top: dnode %08x doesn't point down to %08x", up, dno);
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ dnode->first_free -= 4;
+ de->length -= 4;
+ de->down = 0;
+ hpfs_mark_4buffers_dirty(&qbh);
+ dno = up;
+ }
+ t = get_pos(dnode, de);
+ for_all_poss(i, hpfs_pos_subst, t, 4);
+ for_all_poss(i, hpfs_pos_subst, t + 1, 5);
+ if (!(nde = kmalloc(de->length, GFP_KERNEL))) {
+ hpfs_error(i->i_sb, "out of memory for dirent - directory will be corrupted");
+ hpfs_brelse4(&qbh);
+ return 0;
+ }
+ memcpy(nde, de, de->length);
+ ddno = de->down ? de_down_pointer(de) : 0;
+ hpfs_delete_de(i->i_sb, dnode, de);
+ set_last_pointer(i->i_sb, dnode, ddno);
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ a = hpfs_add_to_dnode(i, to, nde->name, nde->namelen, nde, from);
+ kfree(nde);
+ if (a) return 0;
+ return dno;
+}
+
+/*
+ * Check if a dnode is empty and delete it from the tree
+ * (chkdsk doesn't like empty dnodes)
+ */
+
+static void delete_empty_dnode(struct inode *i, dnode_secno dno)
+{
+ struct quad_buffer_head qbh;
+ struct dnode *dnode;
+ dnode_secno down, up, ndown;
+ int p;
+ struct hpfs_dirent *de;
+ int c1, c2 = 0;
+ try_it_again:
+ if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "delete_empty_dnode")) return;
+ if (!(dnode = hpfs_map_dnode(i->i_sb, dno, &qbh))) return;
+ if (dnode->first_free > 56) goto end;
+ if (dnode->first_free == 52 || dnode->first_free == 56) {
+ struct hpfs_dirent *de_end;
+ int root = dnode->root_dnode;
+ up = dnode->up;
+ de = dnode_first_de(dnode);
+ down = de->down ? de_down_pointer(de) : 0;
+ if (i->i_sb->s_hpfs_chk) if (root && !down) {
+ hpfs_error(i->i_sb, "delete_empty_dnode: root dnode %08x is empty", dno);
+ goto end;
+ }
+ hpfs_brelse4(&qbh);
+ hpfs_free_dnode(i->i_sb, dno);
+ i->i_size -= 2048;
+ i->i_blocks -= 4;
+ if (root) {
+ struct fnode *fnode;
+ struct buffer_head *bh;
+ struct dnode *d1;
+ struct quad_buffer_head qbh1;
+ if (i->i_sb->s_hpfs_chk) if (up != i->i_ino) {
+ hpfs_error(i->i_sb, "bad pointer to fnode, dnode %08x, pointing to %08x, should be %08x", dno, up, i->i_ino);
+ return;
+ }
+ if ((d1 = hpfs_map_dnode(i->i_sb, down, &qbh1))) {
+ d1->up = up;
+ d1->root_dnode = 1;
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ }
+ if ((fnode = hpfs_map_fnode(i->i_sb, up, &bh))) {
+ fnode->u.external[0].disk_secno = down;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ }
+ i->i_hpfs_dno = down;
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, (loff_t) -2);
+ return;
+ }
+ if (!(dnode = hpfs_map_dnode(i->i_sb, up, &qbh))) return;
+ p = 1;
+ de_end = dnode_end_de(dnode);
+ for (de = dnode_first_de(dnode); de < de_end; de = de_next_de(de), p++)
+ if (de->down) if (de_down_pointer(de) == dno) goto fnd;
+ hpfs_error(i->i_sb, "delete_empty_dnode: pointer to dnode %08x not found in dnode %08x", dno, up);
+ goto end;
+ fnd:
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)dno << 4) | 1, ((loff_t)up << 4) | p);
+ if (!down) {
+ de->down = 0;
+ de->length -= 4;
+ dnode->first_free -= 4;
+ memmove(de_next_de(de), (char *)de_next_de(de) + 4,
+ (char *)dnode + dnode->first_free - (char *)de_next_de(de));
+ } else {
+ struct dnode *d1;
+ struct quad_buffer_head qbh1;
+ *(dnode_secno *) ((void *) de + de->length - 4) = down;
+ if ((d1 = hpfs_map_dnode(i->i_sb, down, &qbh1))) {
+ d1->up = up;
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ }
+ }
+ } else {
+ hpfs_error(i->i_sb, "delete_empty_dnode: dnode %08x, first_free == %03x", dno, dnode->first_free);
+ goto end;
+ }
+
+ if (!de->last) {
+ struct hpfs_dirent *de_next = de_next_de(de);
+ struct hpfs_dirent *de_cp;
+ struct dnode *d1;
+ struct quad_buffer_head qbh1;
+ if (!de_next->down) goto endm;
+ ndown = de_down_pointer(de_next);
+ if (!(de_cp = kmalloc(de->length, GFP_KERNEL))) {
+ printk("HPFS: out of memory for dtree balancing\n");
+ goto endm;
+ }
+ memcpy(de_cp, de, de->length);
+ hpfs_delete_de(i->i_sb, dnode, de);
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)up << 4) | p, 4);
+ for_all_poss(i, hpfs_pos_del, ((loff_t)up << 4) | p, 1);
+ if (de_cp->down) if ((d1 = hpfs_map_dnode(i->i_sb, de_down_pointer(de_cp), &qbh1))) {
+ d1->up = ndown;
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ }
+ hpfs_add_to_dnode(i, ndown, de_cp->name, de_cp->namelen, de_cp, de_cp->down ? de_down_pointer(de_cp) : 0);
+ /*printk("UP-TO-DNODE: %08x (ndown = %08x, down = %08x, dno = %08x)\n", up, ndown, down, dno);*/
+ dno = up;
+ kfree(de_cp);
+ goto try_it_again;
+ } else {
+ struct hpfs_dirent *de_prev = dnode_pre_last_de(dnode);
+ struct hpfs_dirent *de_cp;
+ struct dnode *d1;
+ struct quad_buffer_head qbh1;
+ dnode_secno dlp;
+ if (!de_prev) {
+ hpfs_error(i->i_sb, "delete_empty_dnode: empty dnode %08x", up);
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ dno = up;
+ goto try_it_again;
+ }
+ if (!de_prev->down) goto endm;
+ ndown = de_down_pointer(de_prev);
+ if ((d1 = hpfs_map_dnode(i->i_sb, ndown, &qbh1))) {
+ struct hpfs_dirent *del = dnode_last_de(d1);
+ dlp = del->down ? de_down_pointer(del) : 0;
+ if (!dlp && down) {
+ if (d1->first_free > 2044) {
+ if (i->i_sb->s_hpfs_chk >= 2) {
+ printk("HPFS: warning: unbalanced dnode tree, see hpfs.txt 4 more info\n");
+ printk("HPFS: warning: terminating balancing operation\n");
+ }
+ hpfs_brelse4(&qbh1);
+ goto endm;
+ }
+ if (i->i_sb->s_hpfs_chk >= 2) {
+ printk("HPFS: warning: unbalanced dnode tree, see hpfs.txt 4 more info\n");
+ printk("HPFS: warning: goin'on\n");
+ }
+ del->length += 4;
+ del->down = 1;
+ d1->first_free += 4;
+ }
+ if (dlp && !down) {
+ del->length -= 4;
+ del->down = 0;
+ d1->first_free -= 4;
+ } else if (down)
+ *(dnode_secno *) ((void *) del + del->length - 4) = down;
+ } else goto endm;
+ if (!(de_cp = kmalloc(de_prev->length, GFP_KERNEL))) {
+ printk("HPFS: out of memory for dtree balancing\n");
+ hpfs_brelse4(&qbh1);
+ goto endm;
+ }
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ memcpy(de_cp, de_prev, de_prev->length);
+ hpfs_delete_de(i->i_sb, dnode, de_prev);
+ if (!de_prev->down) {
+ de_prev->length += 4;
+ de_prev->down = 1;
+ dnode->first_free += 4;
+ }
+ *(dnode_secno *) ((void *) de_prev + de_prev->length - 4) = ndown;
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)up << 4) | (p - 1), 4);
+ for_all_poss(i, hpfs_pos_subst, ((loff_t)up << 4) | p, ((loff_t)up << 4) | (p - 1));
+ if (down) if ((d1 = hpfs_map_dnode(i->i_sb, de_down_pointer(de), &qbh1))) {
+ d1->up = ndown;
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ }
+ hpfs_add_to_dnode(i, ndown, de_cp->name, de_cp->namelen, de_cp, dlp);
+ dno = up;
+ kfree(de_cp);
+ goto try_it_again;
+ }
+ endm:
+ hpfs_mark_4buffers_dirty(&qbh);
+ end:
+ hpfs_brelse4(&qbh);
+}
+
+
+/* Delete dirent from directory */
+
+int hpfs_remove_dirent(struct inode *i, dnode_secno dno, struct hpfs_dirent *de,
+ struct quad_buffer_head *qbh, int depth)
+{
+ struct dnode *dnode = qbh->data;
+ dnode_secno down = 0;
+ int lock = 0;
+ loff_t t;
+ if (de->first || de->last) {
+ hpfs_error(i->i_sb, "hpfs_remove_dirent: attempt to delete first or last dirent in dnode %08x", dno);
+ hpfs_brelse4(qbh);
+ return 1;
+ }
+ if (de->down) down = de_down_pointer(de);
+ if (depth && (de->down || (de == dnode_first_de(dnode) && de_next_de(de)->last))) {
+ lock = 1;
+ hpfs_lock_creation(i->i_sb);
+ if (hpfs_check_free_dnodes(i->i_sb, FREE_DNODES_DEL)) {
+ hpfs_brelse4(qbh);
+ hpfs_unlock_creation(i->i_sb);
+ return 2;
+ }
+ }
+ i->i_version = ++event;
+ for_all_poss(i, hpfs_pos_del, (t = get_pos(dnode, de)) + 1, 1);
+ hpfs_delete_de(i->i_sb, dnode, de);
+ hpfs_mark_4buffers_dirty(qbh);
+ hpfs_brelse4(qbh);
+ if (down) {
+ dnode_secno a = move_to_top(i, down, dno);
+ for_all_poss(i, hpfs_pos_subst, 5, t);
+ if (a) delete_empty_dnode(i, a);
+ if (lock) hpfs_unlock_creation(i->i_sb);
+ return !a;
+ }
+ delete_empty_dnode(i, dno);
+ if (lock) hpfs_unlock_creation(i->i_sb);
+ return 0;
+}
+
+void hpfs_count_dnodes(struct super_block *s, dnode_secno dno, int *n_dnodes,
+ int *n_subdirs, int *n_items)
+{
+ struct dnode *dnode;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de;
+ dnode_secno ptr, odno = 0;
+ int c1, c2 = 0;
+ int d1, d2 = 0;
+ go_down:
+ if (n_dnodes) (*n_dnodes)++;
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, dno, &c1, &c2, "hpfs_count_dnodes #1")) return;
+ ptr = 0;
+ go_up:
+ if (!(dnode = hpfs_map_dnode(s, dno, &qbh))) return;
+ if (s->s_hpfs_chk) if (odno && odno != -1 && dnode->up != odno)
+ hpfs_error(s, "hpfs_count_dnodes: bad up pointer; dnode %08x, down %08x points to %08x", odno, dno, dnode->up);
+ de = dnode_first_de(dnode);
+ if (ptr) while(1) {
+ if (de->down) if (de_down_pointer(de) == ptr) goto process_de;
+ if (de->last) {
+ hpfs_brelse4(&qbh);
+ hpfs_error(s, "hpfs_count_dnodes: pointer to dnode %08x not found in dnode %08x, got here from %08x",
+ ptr, dno, odno);
+ return;
+ }
+ de = de_next_de(de);
+ }
+ next_de:
+ if (de->down) {
+ odno = dno;
+ dno = de_down_pointer(de);
+ hpfs_brelse4(&qbh);
+ goto go_down;
+ }
+ process_de:
+ if (!de->first && !de->last && de->directory && n_subdirs) (*n_subdirs)++;
+ if (!de->first && !de->last && n_items) (*n_items)++;
+ if ((de = de_next_de(de)) < dnode_end_de(dnode)) goto next_de;
+ ptr = dno;
+ dno = dnode->up;
+ if (dnode->root_dnode) {
+ hpfs_brelse4(&qbh);
+ return;
+ }
+ hpfs_brelse4(&qbh);
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, ptr, &d1, &d2, "hpfs_count_dnodes #2")) return;
+ odno = -1;
+ goto go_up;
+}
+
+static struct hpfs_dirent *map_nth_dirent(struct super_block *s, dnode_secno dno, int n,
+ struct quad_buffer_head *qbh, struct dnode **dn)
+{
+ int i;
+ struct hpfs_dirent *de, *de_end;
+ struct dnode *dnode;
+ dnode = hpfs_map_dnode(s, dno, qbh);
+ if (!dnode) return NULL;
+ if (dn) *dn=dnode;
+ de = dnode_first_de(dnode);
+ de_end = dnode_end_de(dnode);
+ for (i = 1; de < de_end; i++, de = de_next_de(de)) {
+ if (i == n) {
+ return de;
+ }
+ if (de->last) break;
+ }
+ hpfs_brelse4(qbh);
+ hpfs_error(s, "map_nth_dirent: n too high; dnode = %08x, requested %08x", dno, n);
+ return NULL;
+}
+
+dnode_secno hpfs_de_as_down_as_possible(struct super_block *s, dnode_secno dno)
+{
+ struct quad_buffer_head qbh;
+ dnode_secno d = dno;
+ dnode_secno up = 0;
+ struct hpfs_dirent *de;
+ int c1, c2 = 0;
+
+ again:
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, d, &c1, &c2, "hpfs_de_as_down_as_possible"))
+ return d;
+ if (!(de = map_nth_dirent(s, d, 1, &qbh, NULL))) return dno;
+ if (s->s_hpfs_chk)
+ if (up && ((struct dnode *)qbh.data)->up != up)
+ hpfs_error(s, "hpfs_de_as_down_as_possible: bad up pointer; dnode %08x, down %08x points to %08x", up, d, ((struct dnode *)qbh.data)->up);
+ if (!de->down) {
+ hpfs_brelse4(&qbh);
+ return d;
+ }
+ up = d;
+ d = de_down_pointer(de);
+ hpfs_brelse4(&qbh);
+ goto again;
+}
+
+struct hpfs_dirent *map_pos_dirent(struct inode *inode, loff_t *posp,
+ struct quad_buffer_head *qbh)
+{
+ loff_t pos;
+ unsigned c;
+ dnode_secno dno;
+ struct hpfs_dirent *de, *d;
+ struct hpfs_dirent *up_de;
+ struct hpfs_dirent *end_up_de;
+ struct dnode *dnode;
+ struct dnode *up_dnode;
+ struct quad_buffer_head qbh0;
+
+ pos = *posp;
+ dno = pos >> 6 << 2;
+ pos &= 077;
+ if (!(de = map_nth_dirent(inode->i_sb, dno, pos, qbh, &dnode)))
+ goto bail;
+
+ /* Going to the next dirent */
+ if ((d = de_next_de(de)) < dnode_end_de(dnode)) {
+ if (!(++*posp & 077)) {
+ hpfs_error(inode->i_sb, "map_pos_dirent: pos crossed dnode boundary; pos = %08x", *posp);
+ goto bail;
+ }
+ /* We're going down the tree */
+ if (d->down) {
+ *posp = ((loff_t) hpfs_de_as_down_as_possible(inode->i_sb, de_down_pointer(d)) << 4) + 1;
+ }
+
+ return de;
+ }
+
+ /* Going up */
+ if (dnode->root_dnode) goto bail;
+
+ if (!(up_dnode = hpfs_map_dnode(inode->i_sb, dnode->up, &qbh0)))
+ goto bail;
+
+ end_up_de = dnode_end_de(up_dnode);
+ c = 0;
+ for (up_de = dnode_first_de(up_dnode); up_de < end_up_de;
+ up_de = de_next_de(up_de)) {
+ if (!(++c & 077)) hpfs_error(inode->i_sb,
+ "map_pos_dirent: pos crossed dnode boundary; dnode = %08x", dnode->up);
+ if (up_de->down && de_down_pointer(up_de) == dno) {
+ *posp = ((loff_t) dnode->up << 4) + c;
+ hpfs_brelse4(&qbh0);
+ return de;
+ }
+ }
+
+ hpfs_error(inode->i_sb, "map_pos_dirent: pointer to dnode %08x not found in parent dnode %08x",
+ dno, dnode->up);
+ hpfs_brelse4(&qbh0);
+
+ bail:
+ *posp = -2;
+ return de;
+}
+
+/* Find a dirent in tree */
+
+struct hpfs_dirent *map_dirent(struct inode *inode, dnode_secno dno, char *name, unsigned len,
+ dnode_secno *dd, struct quad_buffer_head *qbh)
+{
+ struct dnode *dnode;
+ struct hpfs_dirent *de;
+ struct hpfs_dirent *de_end;
+ int c1, c2 = 0;
+
+ if (!S_ISDIR(inode->i_mode)) hpfs_error(inode->i_sb, "map_dirent: not a directory\n");
+ again:
+ if (inode->i_sb->s_hpfs_chk)
+ if (hpfs_stop_cycles(inode->i_sb, dno, &c1, &c2, "map_dirent")) return NULL;
+ if (!(dnode = hpfs_map_dnode(inode->i_sb, dno, qbh))) return NULL;
+
+ de_end = dnode_end_de(dnode);
+ for (de = dnode_first_de(dnode); de < de_end; de = de_next_de(de)) {
+ int t = hpfs_compare_names(inode->i_sb, name, len, de->name, de->namelen, de->last);
+ if (!t) {
+ if (dd) *dd = dno;
+ return de;
+ }
+ if (t < 0) {
+ if (de->down) {
+ dno = de_down_pointer(de);
+ hpfs_brelse4(qbh);
+ goto again;
+ }
+ break;
+ }
+ }
+ hpfs_brelse4(qbh);
+ return NULL;
+}
+
+/*
+ * Remove empty directory. In normal cases it is only one dnode with two
+ * entries, but we must handle also such obscure cases when it's a tree
+ * of empty dnodes.
+ */
+
+void hpfs_remove_dtree(struct super_block *s, dnode_secno dno)
+{
+ struct quad_buffer_head qbh;
+ struct dnode *dnode;
+ struct hpfs_dirent *de;
+ dnode_secno d1, d2, rdno = dno;
+ while (1) {
+ if (!(dnode = hpfs_map_dnode(s, dno, &qbh))) return;
+ de = dnode_first_de(dnode);
+ if (de->last) {
+ if (de->down) d1 = de_down_pointer(de);
+ else goto error;
+ hpfs_brelse4(&qbh);
+ hpfs_free_dnode(s, dno);
+ dno = d1;
+ } else break;
+ }
+ if (!de->first) goto error;
+ d1 = de->down ? de_down_pointer(de) : 0;
+ de = de_next_de(de);
+ if (!de->last) goto error;
+ d2 = de->down ? de_down_pointer(de) : 0;
+ hpfs_brelse4(&qbh);
+ hpfs_free_dnode(s, dno);
+ do {
+ while (d1) {
+ if (!(dnode = hpfs_map_dnode(s, dno = d1, &qbh))) return;
+ de = dnode_first_de(dnode);
+ if (!de->last) goto error;
+ d1 = de->down ? de_down_pointer(de) : 0;
+ hpfs_brelse4(&qbh);
+ hpfs_free_dnode(s, dno);
+ }
+ d1 = d2;
+ d2 = 0;
+ } while (d1);
+ return;
+ error:
+ hpfs_brelse4(&qbh);
+ hpfs_free_dnode(s, dno);
+ hpfs_error(s, "directory %08x is corrupted or not empty", rdno);
+}
+
+/*
+ * Find dirent for specified fnode. Use truncated 15-char name in fnode as
+ * a help for searching.
+ */
+
+struct hpfs_dirent *map_fnode_dirent(struct super_block *s, fnode_secno fno,
+ struct fnode *f, struct quad_buffer_head *qbh)
+{
+ char *name1;
+ char *name2;
+ int name1len, name2len;
+ struct dnode *d;
+ dnode_secno dno, downd;
+ struct fnode *upf;
+ struct buffer_head *bh;
+ struct hpfs_dirent *de, *de_end;
+ int c;
+ int c1, c2 = 0;
+ int d1, d2 = 0;
+ name1 = f->name;
+ if (!(name2 = kmalloc(256, GFP_KERNEL))) {
+ printk("HPFS: out of memory, can't map dirent\n");
+ return NULL;
+ }
+ if (f->len <= 15)
+ memcpy(name2, name1, name1len = name2len = f->len);
+ else {
+ memcpy(name2, name1, 15);
+ memset(name2 + 15, 0xff, 256 - 15);
+ /*name2[15] = 0xff;*/
+ name1len = 15; name2len = 256;
+ }
+ if (!(upf = hpfs_map_fnode(s, f->up, &bh))) {
+ kfree(name2);
+ return NULL;
+ }
+ if (!upf->dirflag) {
+ brelse(bh);
+ hpfs_error(s, "fnode %08x has non-directory parent %08x", fno, f->up);
+ kfree(name2);
+ return NULL;
+ }
+ dno = upf->u.external[0].disk_secno;
+ brelse(bh);
+ go_down:
+ downd = 0;
+ go_up:
+ if (!(d = hpfs_map_dnode(s, dno, qbh))) {
+ kfree(name2);
+ return NULL;
+ }
+ de_end = dnode_end_de(d);
+ de = dnode_first_de(d);
+ if (downd) {
+ while (de < de_end) {
+ if (de->down) if (de_down_pointer(de) == downd) goto f;
+ de = de_next_de(de);
+ }
+ hpfs_error(s, "pointer to dnode %08x not found in dnode %08x", downd, dno);
+ hpfs_brelse4(qbh);
+ kfree(name2);
+ return NULL;
+ }
+ next_de:
+ if (de->fnode == fno) {
+ kfree(name2);
+ return de;
+ }
+ c = hpfs_compare_names(s, name1, name1len, de->name, de->namelen, de->last);
+ if (c < 0 && de->down) {
+ dno = de_down_pointer(de);
+ hpfs_brelse4(qbh);
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, dno, &c1, &c2, "map_fnode_dirent #1")) {
+ kfree(name2);
+ return NULL;
+ }
+ goto go_down;
+ }
+ f:
+ if (de->fnode == fno) {
+ kfree(name2);
+ return de;
+ }
+ c = hpfs_compare_names(s, name2, name2len, de->name, de->namelen, de->last);
+ if (c < 0 && !de->last) goto not_found;
+ if ((de = de_next_de(de)) < de_end) goto next_de;
+ if (d->root_dnode) goto not_found;
+ downd = dno;
+ dno = d->up;
+ hpfs_brelse4(qbh);
+ if (s->s_hpfs_chk)
+ if (hpfs_stop_cycles(s, downd, &d1, &d2, "map_fnode_dirent #2")) {
+ kfree(name2);
+ return NULL;
+ }
+ goto go_up;
+ not_found:
+ hpfs_brelse4(qbh);
+ hpfs_error(s, "dirent for fnode %08x not found", fno);
+ kfree(name2);
+ return NULL;
+}
diff --git a/fs/hpfs/ea.c b/fs/hpfs/ea.c
new file mode 100644
index 000000000..6dac91fd8
--- /dev/null
+++ b/fs/hpfs/ea.c
@@ -0,0 +1,305 @@
+/*
+ * linux/fs/hpfs/ea.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * handling extended attributes
+ */
+
+#include <linux/string.h>
+#include "hpfs_fn.h"
+
+/* Remove external extended attributes. ano specifies wheter a is a
+ direct sector where eas start or an anode */
+
+void hpfs_ea_ext_remove(struct super_block *s, secno a, int ano, unsigned len)
+{
+ unsigned pos = 0;
+ while (pos < len) {
+ char ex[4 + 255 + 1 + 8];
+ struct extended_attribute *ea = (struct extended_attribute *)ex;
+ if (pos + 4 > len) {
+ hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x",
+ ano ? "anode" : "sectors", a, len);
+ return;
+ }
+ if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return;
+ if (ea->indirect) {
+ if (ea->valuelen != 8) {
+ hpfs_error(s, "ea->indirect set while ea->valuelen!=8, %s %08x, pos %08x",
+ ano ? "anode" : "sectors", a, pos);
+ return;
+ }
+ if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 9, ex+4))
+ return;
+ hpfs_ea_remove(s, ea_sec(ea), ea->anode, ea_len(ea));
+ }
+ pos += ea->namelen + ea->valuelen + 5;
+ }
+ if (!ano) hpfs_free_sectors(s, a, (len+511) >> 9);
+ else {
+ struct buffer_head *bh;
+ struct anode *anode;
+ if ((anode = hpfs_map_anode(s, a, &bh))) {
+ hpfs_remove_btree(s, &anode->btree);
+ brelse(bh);
+ hpfs_free_sectors(s, a, 1);
+ }
+ }
+}
+
+static char *get_indirect_ea(struct super_block *s, int ano, secno a, int size)
+{
+ char *ret;
+ if (!(ret = kmalloc(size + 1, GFP_KERNEL))) {
+ printk("HPFS: out of memory for EA\n");
+ return NULL;
+ }
+ if (hpfs_ea_read(s, a, ano, 0, size, ret)) {
+ kfree(ret);
+ return NULL;
+ }
+ ret[size] = 0;
+ return ret;
+}
+
+static void set_indirect_ea(struct super_block *s, int ano, secno a, char *data,
+ int size)
+{
+ hpfs_ea_write(s, a, ano, 0, size, data);
+}
+
+/* Read an extended attribute named 'key' */
+
+char *hpfs_get_ea(struct super_block *s, struct fnode *fnode, char *key, int *size)
+{
+ char *ret;
+ unsigned pos;
+ int ano, len;
+ secno a;
+ struct extended_attribute *ea;
+ struct extended_attribute *ea_end = fnode_end_ea(fnode);
+ for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
+ if (!strcmp(ea->name, key)) {
+ if (ea->indirect)
+ return get_indirect_ea(s, ea->anode, ea_sec(ea), *size = ea_len(ea));
+ if (!(ret = kmalloc((*size = ea->valuelen) + 1, GFP_KERNEL))) {
+ printk("HPFS: out of memory for EA\n");
+ return NULL;
+ }
+ memcpy(ret, ea_data(ea), ea->valuelen);
+ ret[ea->valuelen] = 0;
+ return ret;
+ }
+ a = fnode->ea_secno;
+ len = fnode->ea_size_l;
+ ano = fnode->ea_anode;
+ pos = 0;
+ while (pos < len) {
+ char ex[4 + 255 + 1 + 8];
+ ea = (struct extended_attribute *)ex;
+ if (pos + 4 > len) {
+ hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x",
+ ano ? "anode" : "sectors", a, len);
+ return NULL;
+ }
+ if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return NULL;
+ if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea->indirect ? 8 : 0), ex + 4))
+ return NULL;
+ if (!strcmp(ea->name, key)) {
+ if (ea->indirect)
+ return get_indirect_ea(s, ea->anode, ea_sec(ea), *size = ea_len(ea));
+ if (!(ret = kmalloc((*size = ea->valuelen) + 1, GFP_KERNEL))) {
+ printk("HPFS: out of memory for EA\n");
+ return NULL;
+ }
+ if (hpfs_ea_read(s, a, ano, pos + 4 + ea->namelen + 1, ea->valuelen, ret)) {
+ kfree(ret);
+ return NULL;
+ }
+ ret[ea->valuelen] = 0;
+ return ret;
+ }
+ pos += ea->namelen + ea->valuelen + 5;
+ }
+ return NULL;
+}
+
+/*
+ * Update or create extended attribute 'key' with value 'data'. Note that
+ * when this ea exists, it MUST have the same size as size of data.
+ * This driver can't change sizes of eas ('cause I just don't need it).
+ */
+
+void hpfs_set_ea(struct inode *inode, struct fnode *fnode, char *key, char *data, int size)
+{
+ fnode_secno fno = inode->i_ino;
+ struct super_block *s = inode->i_sb;
+ unsigned pos;
+ int ano, len;
+ secno a;
+ unsigned char h[4];
+ struct extended_attribute *ea;
+ struct extended_attribute *ea_end = fnode_end_ea(fnode);
+ for (ea = fnode_ea(fnode); ea < ea_end; ea = next_ea(ea))
+ if (!strcmp(ea->name, key)) {
+ if (ea->indirect) {
+ if (ea_len(ea) == size)
+ set_indirect_ea(s, ea->anode, ea_sec(ea), data, size);
+ } else if (ea->valuelen == size) {
+ memcpy(ea_data(ea), data, size);
+ }
+ return;
+ }
+ a = fnode->ea_secno;
+ len = fnode->ea_size_l;
+ ano = fnode->ea_anode;
+ pos = 0;
+ while (pos < len) {
+ char ex[4 + 255 + 1 + 8];
+ ea = (struct extended_attribute *)ex;
+ if (pos + 4 > len) {
+ hpfs_error(s, "EAs don't end correctly, %s %08x, len %08x",
+ ano ? "anode" : "sectors", a, len);
+ return;
+ }
+ if (hpfs_ea_read(s, a, ano, pos, 4, ex)) return;
+ if (hpfs_ea_read(s, a, ano, pos + 4, ea->namelen + 1 + (ea->indirect ? 8 : 0), ex + 4))
+ return;
+ if (!strcmp(ea->name, key)) {
+ if (ea->indirect) {
+ if (ea_len(ea) == size)
+ set_indirect_ea(s, ea->anode, ea_sec(ea), data, size);
+ }
+ else {
+ if (ea->valuelen == size)
+ hpfs_ea_write(s, a, ano, pos + 4 + ea->namelen + 1, size, data);
+ }
+ return;
+ }
+ pos += ea->namelen + ea->valuelen + 5;
+ }
+ if (!fnode->ea_size_s) {
+ /*if (fnode->ea_size_s) {
+ hpfs_error(s, "fnode %08x: ea_size_s == %03x, ea_offs == 0",
+ inode->i_ino, fnode->ea_size_s);
+ return;
+ }*/
+ fnode->ea_offs = 0xc4;
+ }
+ if (fnode->ea_offs < 0xc4 || fnode->ea_offs + fnode->ea_size_s > 0x200) {
+ hpfs_error(s, "fnode %08x: ea_offs == %03x, ea_size_s == %03x",
+ inode->i_ino, fnode->ea_offs, fnode->ea_size_s);
+ return;
+ }
+ if ((fnode->ea_size_s || !fnode->ea_size_l) &&
+ fnode->ea_offs + fnode->ea_size_s + strlen(key) + size + 5 <= 0x200) {
+ /* I'm not sure ... maybe we overwrite ACL here. I have no info
+ on it right now :-( */
+ ea = fnode_end_ea(fnode);
+ *(char *)ea = 0;
+ ea->namelen = strlen(key);
+ ea->valuelen = size;
+ strcpy(ea->name, key);
+ memcpy(ea_data(ea), data, size);
+ fnode->ea_size_s += strlen(key) + size + 5;
+ goto ret;
+ }
+ /* Most the code here is 99.9993422% unused. I hope there are no bugs.
+ But what .. HPFS.IFS has also bugs in ea management. */
+ if (fnode->ea_size_s && !fnode->ea_size_l) {
+ secno n;
+ struct buffer_head *bh;
+ char *data;
+ if (!(n = hpfs_alloc_sector(s, fno, 1, 0, 1))) return;
+ if (!(data = hpfs_get_sector(s, n, &bh))) {
+ hpfs_free_sectors(s, n, 1);
+ return;
+ }
+ memcpy(data, fnode_ea(fnode), fnode->ea_size_s);
+ fnode->ea_size_l = fnode->ea_size_s;
+ fnode->ea_size_s = 0;
+ fnode->ea_secno = n;
+ fnode->ea_anode = 0;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ }
+ pos = fnode->ea_size_l + 5 + strlen(key) + size;
+ len = (fnode->ea_size_l + 511) >> 9;
+ if (pos >= 30000) goto bail;
+ while (((pos + 511) >> 9) > len) {
+ if (!len) {
+ if (!(fnode->ea_secno = hpfs_alloc_sector(s, fno, 1, 0, 1)))
+ goto bail;
+ fnode->ea_anode = 0;
+ len++;
+ }
+ else if (!fnode->ea_anode)
+ if (hpfs_alloc_if_possible(s, fnode->ea_secno + len)) len++;
+ else {
+ /* Aargh... don't know how to create ea anodes :-( */
+ /*struct buffer_head *bh;
+ struct anode *anode;
+ anode_secno a_s;
+ if (!(anode = hpfs_alloc_anode(s, fno, &a_s, &bh)))
+ goto bail;
+ anode->up = fno;
+ anode->btree.fnode_parent = 1;
+ anode->btree.n_free_nodes--;
+ anode->btree.n_used_nodes++;
+ anode->btree.first_free += 12;
+ anode->u.external[0].disk_secno = fnode->ea_secno;
+ anode->u.external[0].file_secno = 0;
+ anode->u.external[0].length = len;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ fnode->ea_anode = 1;
+ fnode->ea_secno = a_s;*/
+ secno new_sec;
+ int i;
+ if (!(new_sec = hpfs_alloc_sector(s, fno, 1, 1 - ((pos + 511) >> 9), 1)))
+ goto bail;
+ for (i = 0; i < len; i++) {
+ struct buffer_head *bh1, *bh2;
+ void *b1, *b2;
+ if (!(b1 = hpfs_map_sector(s, fnode->ea_secno + i, &bh1, len - i - 1))) {
+ hpfs_free_sectors(s, new_sec, (pos + 511) >> 9);
+ goto bail;
+ }
+ if (!(b2 = hpfs_get_sector(s, new_sec + i, &bh2))) {
+ brelse(bh1);
+ hpfs_free_sectors(s, new_sec, (pos + 511) >> 9);
+ goto bail;
+ }
+ memcpy(b2, b1, 512);
+ brelse(bh1);
+ mark_buffer_dirty(bh2, 1);
+ brelse(bh2);
+ }
+ hpfs_free_sectors(s, fnode->ea_secno, len);
+ fnode->ea_secno = new_sec;
+ len = (pos + 511) >> 9;
+ }
+ if (fnode->ea_anode)
+ if (hpfs_add_sector_to_btree(s, fnode->ea_secno, 0, len) != -1)
+ len++;
+ else goto bail;
+ }
+ h[0] = 0;
+ h[1] = strlen(key);
+ h[2] = size & 0xff;
+ h[3] = size >> 8;
+ if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l, 4, h)) goto bail;
+ if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l + 4, h[1] + 1, key)) goto bail;
+ if (hpfs_ea_write(s, fnode->ea_secno, fnode->ea_anode, fnode->ea_size_l + 5 + h[1], size, data)) goto bail;
+ fnode->ea_size_l = pos;
+ ret:
+ inode->i_hpfs_ea_size += 5 + strlen(key) + size;
+ return;
+ bail:
+ if (fnode->ea_secno)
+ if (fnode->ea_anode) hpfs_truncate_btree(s, fnode->ea_secno, 1, (fnode->ea_size_l + 511) >> 9);
+ else hpfs_free_sectors(s, fnode->ea_secno + ((fnode->ea_size_l + 511) >> 9), len - ((fnode->ea_size_l + 511) >> 9));
+ else fnode->ea_secno = fnode->ea_size_l = 0;
+}
+
diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c
new file mode 100644
index 000000000..7cea8dcdc
--- /dev/null
+++ b/fs/hpfs/file.c
@@ -0,0 +1,195 @@
+/*
+ * linux/fs/hpfs/file.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * file VFS functions
+ */
+
+#include <linux/string.h>
+#include "hpfs_fn.h"
+
+int hpfs_open(struct inode *i, struct file *f)
+{
+ hpfs_lock_inode(i);
+ hpfs_unlock_inode(i); /* make sure nobody is deleting the file */
+ if (!i->i_nlink) return -ENOENT;
+ return 0;
+}
+
+int hpfs_file_release(struct inode *inode, struct file *file)
+{
+ hpfs_write_if_changed(inode);
+ return 0;
+}
+
+int hpfs_file_fsync(struct file *file, struct dentry *dentry)
+{
+ /*return file_fsync(file, dentry);*/
+ return 0; /* Don't fsync :-) */
+}
+
+/*
+ * generic_file_read often calls bmap with non-existing sector,
+ * so we must ignore such errors.
+ */
+
+secno hpfs_bmap(struct inode *inode, unsigned file_secno)
+{
+ unsigned n, disk_secno;
+ struct fnode *fnode;
+ struct buffer_head *bh;
+ if (((inode->i_size + 511) >> 9) <= file_secno) return 0;
+ n = file_secno - inode->i_hpfs_file_sec;
+ if (n < inode->i_hpfs_n_secs) return inode->i_hpfs_disk_sec + n;
+ if (!(fnode = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh))) return 0;
+ disk_secno = hpfs_bplus_lookup(inode->i_sb, inode, &fnode->btree, file_secno, bh);
+ if (disk_secno == -1) return 0;
+ if (hpfs_chk_sectors(inode->i_sb, disk_secno, 1, "bmap")) return 0;
+ return disk_secno;
+}
+
+void hpfs_truncate(struct inode *i)
+{
+ if (IS_IMMUTABLE(i)) return /*-EPERM*/;
+ i->i_hpfs_n_secs = 0;
+ hpfs_truncate_btree(i->i_sb, i->i_ino, 1, ((i->i_size + 511) >> 9));
+ i->i_blocks = 1 + ((i->i_size + 511) >> 9);
+ hpfs_write_inode(i);
+}
+
+ssize_t hpfs_file_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+ int i,j;
+ int a = generic_file_read(filp, buf, count, ppos);
+ if (inode->i_hpfs_conv != CONV_TEXT || a < 0) {
+ return a;
+ }
+ for (i = 0, j = 0; i < a; i++) {
+ char c;
+ int error;
+ if ((error = get_user(c, buf + i))) return error;
+ if (c != '\r') {
+ if (i != j) put_user(c, buf + j);
+ j++;
+ }
+ }
+ return j;
+}
+
+ssize_t hpfs_file_write(struct file *filp, const char *buf, size_t count,
+ loff_t *ppos)
+{
+ struct inode *i = filp->f_dentry->d_inode;
+ int carry, error = 0;
+ const char *start = buf;
+ if (!i) return -EINVAL;
+ if (!S_ISREG(i->i_mode)) return -EINVAL;
+ if (IS_IMMUTABLE(i)) return -EPERM;
+ if (filp->f_flags & O_APPEND) *ppos = i->i_size;
+ if (count <= 0) return 0;
+ if ((unsigned)(*ppos+count) >= 0x80000000U || (unsigned)count >= 0x80000000U) return -EFBIG;
+ carry = 0;
+ while (count || carry) {
+ int ii, add = 0;
+ secno sec = 0; /* Go away, uninitialized variable warning */
+ int offset, size, written;
+ char ch;
+ struct buffer_head *bh;
+ char *data;
+ offset = *ppos & 0x1ff;
+ size = count > 0x200 - offset ? 0x200 - offset : count;
+ if ((*ppos >> 9) < ((i->i_size + 0x1ff) >> 9)) {
+ i->i_hpfs_n_secs = 0;
+ if (!(sec = hpfs_bmap(i, *ppos >> 9))) {
+ hpfs_error(i->i_sb, "bmap failed, file %08x, fsec %08x",
+ i->i_ino, *ppos >> 9);
+ error =- EFSERROR;
+ break;
+ }
+ } else for (ii = (i->i_size + 0x1ff) >> 9, add = 1; ii <= *ppos >> 9; ii++) {
+ if ((sec = hpfs_add_sector_to_btree(i->i_sb, i->i_ino, 1, ii)) == -1) {
+ hpfs_truncate(i);
+ return -ENOSPC;
+ }
+ if (*ppos != i->i_size)
+ if ((data = hpfs_get_sector(i->i_sb, sec, &bh))) {
+ memset(data, 0, 512);
+ mark_buffer_dirty(bh, 0);
+ brelse(bh);
+ }
+ i->i_size = 0x200 * ii + 1;
+ i->i_blocks++;
+ /*mark_inode_dirty(i);*/i->i_hpfs_dirty = 1;
+ if (i->i_sb->s_hpfs_chk >= 2) {
+ secno bsec;
+ bsec = hpfs_bmap(i, ii);
+ if (sec != bsec) {
+ hpfs_error(i->i_sb, "sec == %08x, bmap returns %08x", sec, bsec);
+ error = -EFSERROR;
+ break;
+ }
+ }
+ PRINTK(("file_write: added %08x\n", sec));
+ }
+ if (!sec || sec == 15) {
+ hpfs_error(i->i_sb, "bmap returned empty sector");
+ error = -EFSERROR;
+ break;
+ }
+ if (i->i_sb->s_hpfs_chk)
+ if (hpfs_chk_sectors(i->i_sb, sec, 1, "data")) {
+ error = -EFSERROR;
+ break;
+ }
+ if ((!offset && size == 0x200) || add)
+ data = hpfs_get_sector(i->i_sb, sec, &bh);
+ else data = hpfs_map_sector(i->i_sb, sec, &bh, 0);
+ if (!data) {
+ error = -EIO;
+ break;
+ }
+ if (i->i_hpfs_conv != CONV_TEXT) {
+ memcpy_fromfs(data + offset, buf, written = size);
+ buf += size;
+ } else {
+ int left;
+ char *to;
+ /* LF->CR/LF conversion, stolen from fat fs */
+ written = left = 0x200 - offset;
+ to = (char *) bh->b_data + (*ppos & 0x1ff);
+ if (carry) {
+ *to++ = '\n';
+ left--;
+ carry = 0;
+ }
+ for (size = 0; size < count && left; size++) {
+ if ((error = get_user(ch, buf++))) break;
+ if (ch == '\n') {
+ *to++ = '\r';
+ left--;
+ }
+ if (!left) carry = 1;
+ else {
+ *to++ = ch;
+ left--;
+ }
+ }
+ written -= left;
+ }
+ update_vm_cache(i, *ppos, bh->b_data + (*ppos & 0x1ff), written);
+ *ppos += written;
+ if (*ppos > i->i_size) {
+ i->i_size = *ppos;
+ /*mark_inode_dirty(i);*/i->i_hpfs_dirty = 1;
+ }
+ mark_buffer_dirty(bh, 0);
+ brelse(bh);
+ count -= size;
+ }
+ if (start == buf) return error;
+ i->i_mtime = CURRENT_TIME;
+ /*mark_inode_dirty(i);*/i->i_hpfs_dirty = 1;
+ return buf - start;
+}
diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h
new file mode 100644
index 000000000..cdb2c709e
--- /dev/null
+++ b/fs/hpfs/hpfs_fn.h
@@ -0,0 +1,318 @@
+/*
+ * linux/fs/hpfs/hpfs_fn.h
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * function headers
+ */
+
+//#define DBG
+//#define DEBUG_LOCKS
+
+#include <linux/fs.h>
+#include <linux/hpfs_fs.h>
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/locks.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <asm/bitops.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+
+#include <stdarg.h>
+
+#include "hpfs.h"
+
+#define memcpy_tofs memcpy
+#define memcpy_fromfs memcpy
+
+#define EIOERROR EIO
+#define EFSERROR EPERM
+#define EMEMERROR ENOMEM
+
+#define ANODE_ALLOC_FWD 512
+#define FNODE_ALLOC_FWD 0
+#define ALLOC_FWD_MIN 16
+#define ALLOC_FWD_MAX 128
+#define ALLOC_M 1
+#define FNODE_RD_AHEAD 16
+#define ANODE_RD_AHEAD 16
+#define DNODE_RD_AHEAD 4
+
+#define FREE_DNODES_ADD 58
+#define FREE_DNODES_DEL 29
+
+#define CHKCOND(x,y) if (!(x)) printk y
+
+#ifdef DBG
+#define PRINTK(x) printk x
+#else
+#undef PRINTK
+#define PRINTK(x)
+#endif
+
+typedef void nonconst; /* What this is for ? */
+
+/*
+ * local time (HPFS) to GMT (Unix)
+ */
+
+extern inline time_t local_to_gmt(struct super_block *s, time_t t)
+{
+ extern struct timezone sys_tz;
+ return t + sys_tz.tz_minuteswest * 60 - (sys_tz.tz_dsttime ? 3600 : 0) +s->s_hpfs_timeshift;
+}
+
+extern inline time_t gmt_to_local(struct super_block *s, time_t t)
+{
+ extern struct timezone sys_tz;
+ return t - sys_tz.tz_minuteswest * 60 + (sys_tz.tz_dsttime ? 3600 : 0) -s->s_hpfs_timeshift;
+}
+
+/*
+ * conv= options
+ */
+
+#define CONV_BINARY 0 /* no conversion */
+#define CONV_TEXT 1 /* crlf->newline */
+#define CONV_AUTO 2 /* decide based on file contents */
+
+/* Four 512-byte buffers and the 2k block obtained by concatenating them */
+
+struct quad_buffer_head {
+ struct buffer_head *bh[4];
+ void *data;
+};
+
+/* The b-tree down pointer from a dir entry */
+
+extern inline dnode_secno de_down_pointer (struct hpfs_dirent *de)
+{
+ CHKCOND(de->down,("HPFS: de_down_pointer: !de->down\n"));
+ return *(dnode_secno *) ((void *) de + de->length - 4);
+}
+
+/* The first dir entry in a dnode */
+
+extern inline struct hpfs_dirent *dnode_first_de (struct dnode *dnode)
+{
+ return (void *) dnode->dirent;
+}
+
+/* The end+1 of the dir entries */
+
+extern inline struct hpfs_dirent *dnode_end_de (struct dnode *dnode)
+{
+ CHKCOND(dnode->first_free>=0x14 && dnode->first_free<=0xa00,("HPFS: dnode_end_de: dnode->first_free = %d\n",(int)dnode->first_free));
+ return (void *) dnode + dnode->first_free;
+}
+
+/* The dir entry after dir entry de */
+
+extern inline struct hpfs_dirent *de_next_de (struct hpfs_dirent *de)
+{
+ CHKCOND(de->length>=0x20 && de->length<0x800,("HPFS: de_next_de: de->length = %d\n",(int)de->length));
+ return (void *) de + de->length;
+}
+
+extern inline struct extended_attribute *fnode_ea(struct fnode *fnode)
+{
+ return (struct extended_attribute *)((char *)fnode + fnode->ea_offs);
+}
+
+extern inline struct extended_attribute *fnode_end_ea(struct fnode *fnode)
+{
+ return (struct extended_attribute *)((char *)fnode + fnode->ea_offs + fnode->ea_size_s);
+}
+
+extern inline struct extended_attribute *next_ea(struct extended_attribute *ea)
+{
+ return (struct extended_attribute *)((char *)ea + 5 + ea->namelen + ea->valuelen);
+}
+
+extern inline secno ea_sec(struct extended_attribute *ea)
+{
+ return *(secno *)((char *)ea + 9 + ea->namelen);
+}
+
+extern inline secno ea_len(struct extended_attribute *ea)
+{
+ return *(secno *)((char *)ea + 5 + ea->namelen);
+}
+
+extern inline char *ea_data(struct extended_attribute *ea)
+{
+ return (char *)((char *)ea + 5 + ea->namelen);
+}
+
+extern inline unsigned de_size(int namelen, secno down_ptr)
+{
+ return ((0x1f + namelen + 3) & ~3) + (down_ptr ? 4 : 0);
+}
+
+extern inline void copy_de(struct hpfs_dirent *dst, struct hpfs_dirent *src)
+{
+ int a = dst->down;
+ int n = dst->not_8x3;
+ if (!dst || !src) return;
+ memcpy((char *)dst + 2, (char *)src + 2, 28);
+ dst->down = a;
+ dst->not_8x3 = n;
+}
+
+extern inline unsigned tstbits(unsigned *bmp, unsigned b, unsigned n)
+{
+ int i;
+ if ((b >= 0x4000) || (b + n - 1 >= 0x4000)) return n;
+ if (!((bmp[(b & 0x3fff) >> 5] >> (b & 0x1f)) & 1)) return 1;
+ for (i = 1; i < n; i++)
+ if (/*b+i < 0x4000 &&*/ !((bmp[((b+i) & 0x3fff) >> 5] >> ((b+i) & 0x1f)) & 1))
+ return i + 1;
+ return 0;
+}
+
+/* alloc.c */
+
+int hpfs_chk_sectors(struct super_block *, secno, int, char *);
+secno hpfs_alloc_sector(struct super_block *, secno, unsigned, int, int);
+int hpfs_alloc_if_possible_nolock(struct super_block *, secno);
+int hpfs_alloc_if_possible(struct super_block *, secno);
+void hpfs_free_sectors(struct super_block *, secno, unsigned);
+int hpfs_check_free_dnodes(struct super_block *, int);
+void hpfs_free_dnode(struct super_block *, secno);
+struct dnode *hpfs_alloc_dnode(struct super_block *, secno, dnode_secno *, struct quad_buffer_head *, int);
+struct fnode *hpfs_alloc_fnode(struct super_block *, secno, fnode_secno *, struct buffer_head **);
+struct anode *hpfs_alloc_anode(struct super_block *, secno, anode_secno *, struct buffer_head **);
+
+/* anode.c */
+
+secno hpfs_bplus_lookup(struct super_block *, struct inode *, struct bplus_header *, unsigned, struct buffer_head *);
+secno hpfs_add_sector_to_btree(struct super_block *, secno, int, unsigned);
+void hpfs_remove_btree(struct super_block *, struct bplus_header *);
+int hpfs_ea_read(struct super_block *, secno, int, unsigned, unsigned, char *);
+int hpfs_ea_write(struct super_block *, secno, int, unsigned, unsigned, char *);
+void hpfs_ea_remove(struct super_block *, secno, int, unsigned);
+void hpfs_truncate_btree(struct super_block *, secno, int, unsigned);
+void hpfs_remove_fnode(struct super_block *, fnode_secno fno);
+
+/* buffer.c */
+
+void hpfs_lock_creation(struct super_block *);
+void hpfs_unlock_creation(struct super_block *);
+void hpfs_lock_iget(struct super_block *, int);
+void hpfs_unlock_iget(struct super_block *);
+void hpfs_lock_inode(struct inode *);
+void hpfs_unlock_inode(struct inode *);
+void hpfs_lock_2inodes(struct inode *, struct inode *);
+void hpfs_unlock_2inodes(struct inode *, struct inode *);
+void hpfs_lock_3inodes(struct inode *, struct inode *, struct inode *);
+void hpfs_unlock_3inodes(struct inode *, struct inode *, struct inode *);
+void *hpfs_map_sector(struct super_block *, unsigned, struct buffer_head **, int);
+void *hpfs_get_sector(struct super_block *, unsigned, struct buffer_head **);
+void *hpfs_map_4sectors(struct super_block *, unsigned, struct quad_buffer_head *, int);
+void *hpfs_get_4sectors(struct super_block *, unsigned, struct quad_buffer_head *);
+void hpfs_brelse4(struct quad_buffer_head *);
+void hpfs_mark_4buffers_dirty(struct quad_buffer_head *);
+
+/* dentry.c */
+
+void hpfs_set_dentry_operations(struct dentry *);
+
+/* dir.c */
+
+int hpfs_dir_read(struct file *, char *, size_t, loff_t *);
+int hpfs_dir_release(struct inode *, struct file *);
+int hpfs_readdir(struct file *, void *, filldir_t);
+struct dentry *hpfs_lookup(struct inode *, struct dentry *);
+
+/* dnode.c */
+
+void hpfs_add_pos(struct inode *, loff_t *);
+void hpfs_del_pos(struct inode *, loff_t *);
+struct hpfs_dirent *hpfs_add_de(struct super_block *, struct dnode *, unsigned char *, unsigned, secno);
+void hpfs_delete_de(struct super_block *, struct dnode *, struct hpfs_dirent *);
+int hpfs_add_to_dnode(struct inode *, dnode_secno, unsigned char *, unsigned, struct hpfs_dirent *, dnode_secno);
+int hpfs_add_dirent(struct inode *, unsigned char *, unsigned, struct hpfs_dirent *, int);
+int hpfs_remove_dirent(struct inode *, dnode_secno, struct hpfs_dirent *, struct quad_buffer_head *, int);
+void hpfs_count_dnodes(struct super_block *, dnode_secno, int *, int *, int *);
+dnode_secno hpfs_de_as_down_as_possible(struct super_block *, dnode_secno dno);
+struct hpfs_dirent *map_pos_dirent(struct inode *, loff_t *, struct quad_buffer_head *);
+struct hpfs_dirent *map_dirent(struct inode *, dnode_secno, char *, unsigned, dnode_secno *, struct quad_buffer_head *);
+void hpfs_remove_dtree(struct super_block *, dnode_secno);
+struct hpfs_dirent *map_fnode_dirent(struct super_block *, fnode_secno, struct fnode *, struct quad_buffer_head *);
+
+/* ea.c */
+
+void hpfs_ea_ext_remove(struct super_block *, secno, int, unsigned);
+char *hpfs_get_ea(struct super_block *, struct fnode *, char *, int *);
+void hpfs_set_ea(struct inode *, struct fnode *, char *, char *, int);
+
+/* file.c */
+
+int hpfs_file_release(struct inode *, struct file *);
+int hpfs_open(struct inode *, struct file *);
+int hpfs_file_fsync(struct file *, struct dentry *);
+secno hpfs_bmap(struct inode *, unsigned);
+void hpfs_truncate(struct inode *);
+ssize_t hpfs_file_read(struct file *, char *, size_t, loff_t *);
+ssize_t hpfs_file_write(struct file *, const char *, size_t, loff_t *);
+
+/* inode.c */
+
+void hpfs_read_inode(struct inode *);
+void hpfs_write_inode_ea(struct inode *, struct fnode *);
+void hpfs_write_inode(struct inode *);
+void hpfs_write_inode_nolock(struct inode *);
+int hpfs_notify_change(struct dentry *, struct iattr *);
+void hpfs_write_if_changed(struct inode *);
+void hpfs_delete_inode(struct inode *);
+
+/* map.c */
+
+unsigned *hpfs_map_dnode_bitmap(struct super_block *, struct quad_buffer_head *);
+unsigned *hpfs_map_bitmap(struct super_block *, unsigned, struct quad_buffer_head *, char *);
+char *hpfs_load_code_page(struct super_block *, secno);
+secno *hpfs_load_bitmap_directory(struct super_block *, secno bmp);
+struct fnode *hpfs_map_fnode(struct super_block *s, ino_t, struct buffer_head **);
+struct anode *hpfs_map_anode(struct super_block *s, anode_secno, struct buffer_head **);
+struct dnode *hpfs_map_dnode(struct super_block *s, dnode_secno, struct quad_buffer_head *);
+dnode_secno hpfs_fnode_dno(struct super_block *s, ino_t ino);
+
+/* mmap.c */
+
+int hpfs_mmap(struct file *, struct vm_area_struct *);
+
+/* name.c */
+
+unsigned char hpfs_upcase(unsigned char *, unsigned char);
+int hpfs_chk_name(unsigned char *, unsigned *);
+char *hpfs_translate_name(struct super_block *, unsigned char *, unsigned, int, int);
+int hpfs_compare_names(struct super_block *, unsigned char *, unsigned, unsigned char *, unsigned, int);
+int hpfs_is_name_long(unsigned char *, unsigned);
+void hpfs_adjust_length(unsigned char *, unsigned *);
+void hpfs_decide_conv(struct inode *, unsigned char *, unsigned);
+
+/* namei.c */
+
+int hpfs_mkdir(struct inode *, struct dentry *, int);
+int hpfs_create(struct inode *, struct dentry *, int);
+int hpfs_mknod(struct inode *, struct dentry *, int, int);
+int hpfs_symlink(struct inode *, struct dentry *, const char *);
+int hpfs_unlink(struct inode *, struct dentry *);
+int hpfs_rmdir(struct inode *, struct dentry *);
+int hpfs_readlink(struct dentry *, char *, int);
+struct dentry *hpfs_follow_link(struct dentry *, struct dentry *, unsigned int);
+int hpfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
+
+/* super.c */
+
+void hpfs_error(struct super_block *, char *, ...);
+int hpfs_stop_cycles(struct super_block *, int, int *, int *, char *);
+int hpfs_remount_fs(struct super_block *, int *, char *);
+void hpfs_put_super(struct super_block *);
+unsigned hpfs_count_one_bitmap(struct super_block *, secno);
+int hpfs_statfs(struct super_block *, struct statfs *, int);
+struct super_block *hpfs_read_super(struct super_block *, void *, int);
diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c
new file mode 100644
index 000000000..99bfa1004
--- /dev/null
+++ b/fs/hpfs/inode.c
@@ -0,0 +1,381 @@
+/*
+ * linux/fs/hpfs/inode.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * inode VFS functions
+ */
+
+#include "hpfs_fn.h"
+
+static const struct file_operations hpfs_file_ops =
+{
+ NULL, /* lseek - default */
+ hpfs_file_read, /* read */
+ hpfs_file_write, /* write */
+ NULL, /* readdir - bad */
+ NULL, /* poll - default */
+ NULL, /* ioctl - default */
+ generic_file_mmap/*hpfs_mmap*/, /* mmap */
+ hpfs_open, /* open */
+ NULL, /* flush */
+ hpfs_file_release, /* release */
+ hpfs_file_fsync, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL, /* lock */
+};
+
+static const struct inode_operations hpfs_file_iops =
+{
+ (nonconst *) & hpfs_file_ops, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ generic_readpage, /* readpage */
+ NULL, /* writepage */
+ (int (*)(struct inode *, int))
+ &hpfs_bmap, /* bmap */
+ &hpfs_truncate, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL, /* revalidate */
+};
+
+static const struct file_operations hpfs_dir_ops =
+{
+ NULL, /* lseek - default */
+ hpfs_dir_read, /* read */
+ NULL, /* write - bad */
+ hpfs_readdir, /* readdir */
+ NULL, /* poll - default */
+ NULL, /* ioctl - default */
+ NULL, /* mmap */
+ hpfs_open, /* open */
+ NULL, /* flush */
+ hpfs_dir_release, /* no special release code */
+ hpfs_file_fsync, /* fsync */
+ NULL, /* fasync */
+ NULL, /* check_media_change */
+ NULL, /* revalidate */
+ NULL, /* lock */
+};
+
+static const struct inode_operations hpfs_dir_iops =
+{
+ (nonconst *) & hpfs_dir_ops, /* default directory file ops */
+ hpfs_create, /* create */
+ hpfs_lookup, /* lookup */
+ NULL, /* link */
+ hpfs_unlink, /* unlink */
+ hpfs_symlink, /* symlink */
+ hpfs_mkdir, /* mkdir */
+ hpfs_rmdir, /* rmdir */
+ hpfs_mknod, /* mknod */
+ hpfs_rename, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL, /* revalidate */
+};
+
+const struct inode_operations hpfs_symlink_iops =
+{
+ NULL, /* default file operations */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ hpfs_readlink, /* readlink */
+ hpfs_follow_link, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL, /* permission */
+ NULL, /* smap */
+ NULL, /* updatepage */
+ NULL, /* revalidate */
+};
+
+
+void hpfs_read_inode(struct inode *i)
+{
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ struct super_block *sb = i->i_sb;
+ unsigned char *ea;
+ int ea_size;
+ i->i_op = 0;
+ /*i->i_hpfs_sem = MUTEX;*/
+ init_MUTEX(&i->i_hpfs_sem);
+ i->i_uid = sb->s_hpfs_uid;
+ i->i_gid = sb->s_hpfs_gid;
+ i->i_mode = sb->s_hpfs_mode;
+ i->i_hpfs_conv = sb->s_hpfs_conv;
+ i->i_blksize = 512;
+ i->i_size = -1;
+ i->i_blocks = -1;
+
+ i->i_hpfs_dno = 0;
+ i->i_hpfs_n_secs = 0;
+ i->i_hpfs_file_sec = 0;
+ i->i_hpfs_disk_sec = 0;
+ i->i_hpfs_dpos = 0;
+ i->i_hpfs_dsubdno = 0;
+ i->i_hpfs_ea_mode = 0;
+ i->i_hpfs_ea_uid = 0;
+ i->i_hpfs_ea_gid = 0;
+ i->i_hpfs_ea_size = 0;
+ i->i_version = ++event;
+
+ i->i_hpfs_rddir_off = NULL;
+ i->i_hpfs_dirty = 0;
+
+ i->i_atime = 0;
+ i->i_mtime = 0;
+ i->i_ctime = 0;
+
+ if (!i->i_sb->s_hpfs_rd_inode)
+ hpfs_error(i->i_sb, "read_inode: s_hpfs_rd_inode == 0");
+ if (i->i_sb->s_hpfs_rd_inode == 2) {
+ i->i_mode |= S_IFREG;
+ i->i_mode &= ~0111;
+ i->i_op = (struct inode_operations *) &hpfs_file_iops;
+ i->i_nlink = 1;
+ return;
+ }
+ if (!(fnode = hpfs_map_fnode(sb, i->i_ino, &bh))) {
+ /*i->i_mode |= S_IFREG;
+ i->i_mode &= ~0111;
+ i->i_op = (struct inode_operations *) &hpfs_file_iops;
+ i->i_nlink = 0;*/
+ make_bad_inode(i);
+ return;
+ }
+ if (i->i_sb->s_hpfs_eas) {
+ if ((ea = hpfs_get_ea(i->i_sb, fnode, "UID", &ea_size))) {
+ if (ea_size == 2) {
+ i->i_uid = ea[0] + (ea[1] << 8);
+ i->i_hpfs_ea_uid = 1;
+ }
+ kfree(ea);
+ }
+ if ((ea = hpfs_get_ea(i->i_sb, fnode, "GID", &ea_size))) {
+ if (ea_size == 2) {
+ i->i_gid = ea[0] + (ea[1] << 8);
+ i->i_hpfs_ea_gid = 1;
+ }
+ kfree(ea);
+ }
+ if ((ea = hpfs_get_ea(i->i_sb, fnode, "SYMLINK", &ea_size))) {
+ kfree(ea);
+ i->i_mode = S_IFLNK | 0777;
+ i->i_op = (struct inode_operations *) &hpfs_symlink_iops;
+ i->i_nlink = 1;
+ i->i_size = ea_size;
+ i->i_blocks = 1;
+ brelse(bh);
+ return;
+ }
+ if ((ea = hpfs_get_ea(i->i_sb, fnode, "MODE", &ea_size))) {
+ int rdev = 0;
+ umode_t mode = sb->s_hpfs_mode;
+ if (ea_size == 2) {
+ mode = ea[0] + (ea[1] << 8);
+ i->i_hpfs_ea_mode = 1;
+ }
+ kfree(ea);
+ i->i_mode = mode;
+ if (S_ISBLK(mode) || S_ISCHR(mode)) {
+ if ((ea = hpfs_get_ea(i->i_sb, fnode, "DEV", &ea_size))) {
+ if (ea_size == 4)
+ rdev = ea[0] + (ea[1] << 8) + (ea[2] << 16) + (ea[3] << 24);
+ kfree(ea);
+ }
+ }
+ if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode)) {
+ brelse(bh);
+ i->i_nlink = 1;
+ i->i_size = 0;
+ i->i_blocks = 1;
+ init_special_inode(i, mode, rdev);
+ return;
+ }
+ }
+ }
+ if (fnode->dirflag) {
+ unsigned n_dnodes, n_subdirs;
+ i->i_mode |= S_IFDIR;
+ i->i_op = (struct inode_operations *) &hpfs_dir_iops;
+ i->i_hpfs_parent_dir = fnode->up;
+ i->i_hpfs_dno = fnode->u.external[0].disk_secno;
+ if (sb->s_hpfs_chk >= 2) {
+ struct buffer_head *bh0;
+ if (hpfs_map_fnode(sb, i->i_hpfs_parent_dir, &bh0)) brelse(bh0);
+ }
+ n_dnodes = 0; n_subdirs = 0;
+ hpfs_count_dnodes(i->i_sb, i->i_hpfs_dno, &n_dnodes, &n_subdirs, NULL);
+ i->i_blocks = 4 * n_dnodes;
+ i->i_size = 2048 * n_dnodes;
+ i->i_nlink = 2 + n_subdirs;
+ } else {
+ i->i_mode |= S_IFREG;
+ if (!i->i_hpfs_ea_mode) i->i_mode &= ~0111;
+ i->i_op = (struct inode_operations *) &hpfs_file_iops;
+ i->i_nlink = 1;
+ i->i_size = fnode->file_size;
+ i->i_blocks = ((i->i_size + 511) >> 9) + 1;
+ }
+ brelse(bh);
+}
+
+void hpfs_write_inode_ea(struct inode *i, struct fnode *fnode)
+{
+ if (fnode->acl_size_l || fnode->acl_size_s) {
+ /* Some unknown structures like ACL may be in fnode,
+ we'd better not overwrite them */
+ hpfs_error(i->i_sb, "fnode %08x has some unknown HPFS386 stuctures", i->i_ino);
+ } else if (i->i_sb->s_hpfs_eas >= 2) {
+ unsigned char ea[4];
+ if ((i->i_uid != i->i_sb->s_hpfs_uid) || i->i_hpfs_ea_uid) {
+ ea[0] = i->i_uid & 0xff;
+ ea[1] = i->i_uid >> 8;
+ hpfs_set_ea(i, fnode, "UID", ea, 2);
+ i->i_hpfs_ea_uid = 1;
+ }
+ if ((i->i_gid != i->i_sb->s_hpfs_gid) || i->i_hpfs_ea_gid) {
+ ea[0] = i->i_gid & 0xff;
+ ea[1] = i->i_gid >> 8;
+ hpfs_set_ea(i, fnode, "GID", ea, 2);
+ i->i_hpfs_ea_gid = 1;
+ }
+ if (!S_ISLNK(i->i_mode))
+ if ((i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0 : 0111))
+ | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))
+ && i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0222 : 0333))
+ | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))) || i->i_hpfs_ea_mode) {
+ ea[0] = i->i_mode & 0xff;
+ ea[1] = i->i_mode >> 8;
+ hpfs_set_ea(i, fnode, "MODE", ea, 2);
+ i->i_hpfs_ea_mode = 1;
+ }
+ if (S_ISBLK(i->i_mode) || S_ISCHR(i->i_mode)) {
+ int d = kdev_t_to_nr(i->i_rdev);
+ ea[0] = d & 0xff;
+ ea[1] = (d >> 8) & 0xff;
+ ea[2] = (d >> 16) & 0xff;
+ ea[3] = d >> 24;
+ hpfs_set_ea(i, fnode, "DEV", ea, 4);
+ }
+ }
+}
+
+void hpfs_write_inode(struct inode *i)
+{
+ struct inode *parent;
+ if (!i->i_nlink) return;
+ if (i->i_ino == i->i_sb->s_hpfs_root) return;
+ if (i->i_hpfs_rddir_off && !i->i_count) {
+ if (*i->i_hpfs_rddir_off) printk("HPFS: write_inode: some position still there\n");
+ kfree(i->i_hpfs_rddir_off);
+ i->i_hpfs_rddir_off = NULL;
+ }
+ i->i_hpfs_dirty = 0;
+ hpfs_lock_iget(i->i_sb, 1);
+ parent = iget(i->i_sb, i->i_hpfs_parent_dir);
+ hpfs_unlock_iget(i->i_sb);
+ hpfs_lock_inode(parent);
+ hpfs_write_inode_nolock(i);
+ hpfs_unlock_inode(parent);
+ iput(parent);
+}
+
+void hpfs_write_inode_nolock(struct inode *i)
+{
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de;
+ if (i->i_ino == i->i_sb->s_hpfs_root) return;
+ if (!(fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) return;
+ if (i->i_ino != i->i_sb->s_hpfs_root) {
+ if (!(de = map_fnode_dirent(i->i_sb, i->i_ino, fnode, &qbh))) {
+ brelse(bh);
+ return;
+ }
+ } else de = NULL;
+ if (S_ISREG(i->i_mode)) {
+ fnode->file_size = de->file_size = i->i_size;
+ } else if (S_ISDIR(i->i_mode)) {
+ fnode->file_size = de->file_size = 0;
+ }
+ hpfs_write_inode_ea(i, fnode);
+ if (de) {
+ de->write_date = gmt_to_local(i->i_sb, i->i_mtime);
+ de->read_date = gmt_to_local(i->i_sb, i->i_atime);
+ de->creation_date = gmt_to_local(i->i_sb, i->i_ctime);
+ de->read_only = !(i->i_mode & 0222);
+ de->ea_size = i->i_hpfs_ea_size;
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ }
+ if (S_ISDIR(i->i_mode)) {
+ if ((de = map_dirent(i, i->i_hpfs_dno, "\001\001", 2, NULL, &qbh))) {
+ de->write_date = gmt_to_local(i->i_sb, i->i_mtime);
+ de->read_date = gmt_to_local(i->i_sb, i->i_atime);
+ de->creation_date = gmt_to_local(i->i_sb, i->i_ctime);
+ de->read_only = !(i->i_mode & 0222);
+ de->ea_size = /*i->i_hpfs_ea_size*/0;
+ de->file_size = 0;
+ hpfs_mark_4buffers_dirty(&qbh);
+ hpfs_brelse4(&qbh);
+ } else hpfs_error(i->i_sb, "directory %08x doesn't have '.' entry", i->i_ino);
+ }
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+}
+
+int hpfs_notify_change(struct dentry *dentry, struct iattr *attr)
+{
+ struct inode *inode = dentry->d_inode;
+ int error;
+ if (inode->i_sb->s_hpfs_root == inode->i_ino) return -EINVAL;
+ if ((error = inode_change_ok(inode, attr))) return error;
+ inode_setattr(inode, attr);
+ hpfs_write_inode(inode);
+ return 0;
+}
+
+void hpfs_write_if_changed(struct inode *inode)
+{
+ if (inode->i_hpfs_dirty) {
+ hpfs_write_inode(inode);
+ }
+}
+
+void hpfs_delete_inode(struct inode *inode)
+{
+ hpfs_remove_fnode(inode->i_sb, inode->i_ino);
+}
diff --git a/fs/hpfs/map.c b/fs/hpfs/map.c
new file mode 100644
index 000000000..a0e0feada
--- /dev/null
+++ b/fs/hpfs/map.c
@@ -0,0 +1,268 @@
+/*
+ * linux/fs/hpfs/map.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * mapping structures to memory with some minimal checks
+ */
+
+#include "hpfs_fn.h"
+
+unsigned *hpfs_map_dnode_bitmap(struct super_block *s, struct quad_buffer_head *qbh)
+{
+ return hpfs_map_4sectors(s, s->s_hpfs_dmap, qbh, 0);
+}
+
+unsigned int *hpfs_map_bitmap(struct super_block *s, unsigned bmp_block,
+ struct quad_buffer_head *qbh, char *id)
+{
+ secno sec;
+ if (s->s_hpfs_chk) if (bmp_block * 16384 > s->s_hpfs_fs_size) {
+ hpfs_error(s, "hpfs_map_bitmap called with bad parameter: %08x at %s", bmp_block, id);
+ return NULL;
+ }
+ sec = s->s_hpfs_bmp_dir[bmp_block];
+ if (!sec || sec > s->s_hpfs_fs_size-4) {
+ hpfs_error(s, "invalid bitmap block pointer %08x -> %08x at %s", bmp_block, sec, id);
+ return NULL;
+ }
+ return hpfs_map_4sectors(s, sec, qbh, 4);
+}
+
+/*
+ * Load first code page into kernel memory, return pointer to 256-byte array,
+ * first 128 bytes are uppercasing table for chars 128-255, next 128 bytes are
+ * lowercasing table
+ */
+
+char *hpfs_load_code_page(struct super_block *s, secno cps)
+{
+ struct buffer_head *bh;
+ secno cpds;
+ unsigned cpi;
+ unsigned char *ptr;
+ unsigned char *cp_table;
+ int i;
+ struct code_page_data *cpd;
+ struct code_page_directory *cp = hpfs_map_sector(s, cps, &bh, 0);
+ if (!cp) return NULL;
+ if (cp->magic != CP_DIR_MAGIC) {
+ printk("HPFS: Code page directory magic doesn't match (magic = %08x)\n", cp->magic);
+ brelse(bh);
+ return NULL;
+ }
+ if (!cp->n_code_pages) {
+ printk("HPFS: n_code_pages == 0\n");
+ brelse(bh);
+ return NULL;
+ }
+ cpds = cp->array[0].code_page_data;
+ cpi = cp->array[0].index;
+ brelse(bh);
+
+ if (!(cpd = hpfs_map_sector(s, cpds, &bh, 0))) return NULL;
+ if ((unsigned)cpd->offs[cpi] > 0x178) {
+ printk("HPFS: Code page index out of sector\n");
+ brelse(bh);
+ return NULL;
+ }
+ ptr = (char *)cpd + cpd->offs[cpi] + 6;
+ if (!(cp_table = kmalloc(256, GFP_KERNEL))) {
+ printk("HPFS: out of memory for code page table\n");
+ brelse(bh);
+ return NULL;
+ }
+ memcpy(cp_table, ptr, 128);
+ brelse(bh);
+
+ /* Try to build lowercasing table from uppercasing one */
+
+ for (i=128; i<256; i++) cp_table[i]=i;
+ for (i=128; i<256; i++) if (cp_table[i-128]!=i && cp_table[i-128]>=128)
+ cp_table[cp_table[i-128]] = i;
+
+ return cp_table;
+}
+
+secno *hpfs_load_bitmap_directory(struct super_block *s, secno bmp)
+{
+ struct buffer_head *bh;
+ int n = (s->s_hpfs_fs_size + 0x200000 - 1) >> 21;
+ int i;
+ secno *b;
+ if (!(b = kmalloc(n * 512, GFP_KERNEL))) {
+ printk("HPFS: can't allocate memory for bitmap directory\n");
+ return NULL;
+ }
+ for (i=0;i<n;i++) {
+ secno *d = hpfs_map_sector(s, bmp+i, &bh, n - i - 1);
+ if (!d) {
+ kfree(b);
+ return NULL;
+ }
+ memcpy((char *)b + 512 * i, d, 512);
+ brelse(bh);
+ }
+ return b;
+}
+
+/*
+ * Load fnode to memory
+ */
+
+struct fnode *hpfs_map_fnode(struct super_block *s, ino_t ino, struct buffer_head **bhp)
+{
+ struct fnode *fnode;
+ if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, ino, 1, "fnode")) {
+ return NULL;
+ }
+ if ((fnode = hpfs_map_sector(s, ino, bhp, FNODE_RD_AHEAD))) {
+ if (s->s_hpfs_chk) {
+ struct extended_attribute *ea;
+ struct extended_attribute *ea_end;
+ if (fnode->magic != FNODE_MAGIC) {
+ hpfs_error(s, "bad magic on fnode %08x", ino);
+ goto bail;
+ }
+ if (!fnode->dirflag) {
+ if ((unsigned)fnode->btree.n_used_nodes + (unsigned)fnode->btree.n_free_nodes !=
+ (fnode->btree.internal ? 12 : 8)) {
+ hpfs_error(s, "bad number of nodes in fnode %08x", ino);
+ goto bail;
+ }
+ if (fnode->btree.first_free !=
+ 8 + fnode->btree.n_used_nodes * (fnode->btree.internal ? 8 : 12)) {
+ hpfs_error(s, "bad first_free pointer in fnode %08x", ino);
+ goto bail;
+ }
+ }
+ if (fnode->ea_size_s && ((signed int)fnode->ea_offs < 0xc4 ||
+ (signed int)fnode->ea_offs + fnode->ea_size_s > 0x200)) {
+ hpfs_error(s, "bad EA info in fnode %08x: ea_offs == %04x ea_size_s == %04x",
+ ino, fnode->ea_offs, fnode->ea_size_s);
+ goto bail;
+ }
+ ea = fnode_ea(fnode);
+ ea_end = fnode_end_ea(fnode);
+ while (ea != ea_end) {
+ if (ea > ea_end) {
+ hpfs_error(s, "bad EA in fnode %08x", ino);
+ goto bail;
+ }
+ ea = next_ea(ea);
+ }
+ }
+ }
+ return fnode;
+ bail:
+ brelse(*bhp);
+ return NULL;
+}
+
+struct anode *hpfs_map_anode(struct super_block *s, anode_secno ano, struct buffer_head **bhp)
+{
+ struct anode *anode;
+ if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, ano, 1, "anode")) return NULL;
+ if ((anode = hpfs_map_sector(s, ano, bhp, ANODE_RD_AHEAD)))
+ if (s->s_hpfs_chk) {
+ if (anode->magic != ANODE_MAGIC || anode->self != ano) {
+ hpfs_error(s, "bad magic on anode %08x", ano);
+ goto bail;
+ }
+ if ((unsigned)anode->btree.n_used_nodes + (unsigned)anode->btree.n_free_nodes !=
+ (anode->btree.internal ? 60 : 40)) {
+ hpfs_error(s, "bad number of nodes in anode %08x", ano);
+ goto bail;
+ }
+ if (anode->btree.first_free !=
+ 8 + anode->btree.n_used_nodes * (anode->btree.internal ? 8 : 12)) {
+ hpfs_error(s, "bad first_free pointer in anode %08x", ano);
+ goto bail;
+ }
+ }
+ return anode;
+ bail:
+ brelse(*bhp);
+ return NULL;
+}
+
+/*
+ * Load dnode to memory and do some checks
+ */
+
+struct dnode *hpfs_map_dnode(struct super_block *s, unsigned secno,
+ struct quad_buffer_head *qbh)
+{
+ struct dnode *dnode;
+ if (s->s_hpfs_chk) {
+ if (hpfs_chk_sectors(s, secno, 4, "dnode")) return NULL;
+ if (secno & 3) {
+ hpfs_error(s, "dnode %08x not byte-aligned", secno);
+ return NULL;
+ }
+ }
+ if ((dnode = hpfs_map_4sectors(s, secno, qbh, DNODE_RD_AHEAD)))
+ if (s->s_hpfs_chk) {
+ unsigned p, pp = 0;
+ unsigned char *d = (char *)dnode;
+ int b = 0;
+ if (dnode->magic != DNODE_MAGIC) {
+ hpfs_error(s, "bad magic on dnode %08x", secno);
+ goto bail;
+ }
+ if (dnode->self != secno)
+ hpfs_error(s, "bad self pointer on dnode %08x self = %08x", secno, dnode->self);
+ /* Check dirents - bad dirents would cause infinite
+ loops or shooting to memory */
+ if (dnode->first_free > 2048/* || dnode->first_free < 84*/) {
+ hpfs_error(s, "dnode %08x has first_free == %08x", secno, dnode->first_free);
+ goto bail;
+ }
+ for (p = 20; p < dnode->first_free; p += d[p] + (d[p+1] << 8)) {
+ struct hpfs_dirent *de = (struct hpfs_dirent *)((char *)dnode + p);
+ if (de->length > 292 || (de->length < 32) || (de->length & 3)) {
+ hpfs_error(s, "bad dirent size in dnode %08x, dirent %03x, last %03x", secno, p, pp);
+ goto bail;
+ }
+ if (((31 + de->namelen + de->down*4 + 3) & ~3) != de->length) {
+ hpfs_error(s, "namelen does not match dirent size in dnode %08x, dirent %03x, last %03x", secno, p, pp);
+ goto bail;
+ }
+ if (s->s_hpfs_chk >= 2) b |= 1 << de->down;
+ if (de->down) if (de_down_pointer(de) < 0x10) {
+ hpfs_error(s, "bad down pointer in dnode %08x, dirent %03x, last %03x", secno, p, pp);
+ goto bail;
+ }
+ pp = p;
+
+ }
+ if (p != dnode->first_free) {
+ hpfs_error(s, "size on last dirent does not match first_free; dnode %08x", secno);
+ goto bail;
+ }
+ if (d[pp + 30] != 1 || d[pp + 31] != 255) {
+ hpfs_error(s, "dnode %08x does not end with \\377 entry", secno);
+ goto bail;
+ }
+ if (b == 3) printk("HPFS: warning: unbalanced dnode tree, dnode %08x; see hpfs.txt 4 more info\n", secno);
+ }
+ return dnode;
+ bail:
+ hpfs_brelse4(qbh);
+ return NULL;
+}
+
+dnode_secno hpfs_fnode_dno(struct super_block *s, ino_t ino)
+{
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ dnode_secno dno;
+
+ fnode = hpfs_map_fnode(s, ino, &bh);
+ if (!fnode)
+ return 0;
+
+ dno = fnode->u.external[0].disk_secno;
+ brelse(bh);
+ return dno;
+}
diff --git a/fs/hpfs/mmap.c b/fs/hpfs/mmap.c
new file mode 100644
index 000000000..3fd544664
--- /dev/null
+++ b/fs/hpfs/mmap.c
@@ -0,0 +1,128 @@
+/*
+ * linux/fs/hpfs/mmap.c
+ *
+ * taken from fat filesystem
+ *
+ * Written by Jacques Gelinas (jacques@solucorp.qc.ca)
+ * Inspired by fs/nfs/mmap.c (Jon Tombs 15 Aug 1993)
+ *
+ * Modified for HPFS by Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz)
+ *
+ * mmap handling for hpfs filesystem
+ * (generic_file_mmap may be used only on filesystems that keep zeros
+ * in last file sector beyond end)
+ */
+
+/*
+ * generic_file_mmap doesn't erase the space beyond file end in last sector. :-(
+ * Hpfs doesn't keep zeros in last sector. This causes problems with kernel
+ * mkdep.c and probably other programs. Additionally this could be a security
+ * hole - some interesting data, like pieces of /etc/shadow could be found
+ * beyond file end.
+ *
+ * So, I can't use generic mmap. mmap from fat filesystem looks good, so I used
+ * it.
+ *
+ * BTW. fat uses generic mmap on normal disks. Doesn't it also have above bugs?
+ * I don't think Msdos erases space in last sector.
+ *
+ * If you fix generic_file_mmap, you can remove this file and use it.
+ */
+
+#include "hpfs_fn.h"
+
+/*
+ * Fill in the supplied page for mmap
+ */
+
+static unsigned long hpfs_file_mmap_nopage(
+ struct vm_area_struct * area,
+ unsigned long address,
+ int error_code)
+{
+ /*struct inode * inode = area->vm_inode;*/
+ struct inode * inode = area->vm_file->f_dentry->d_inode;
+ unsigned long page;
+ unsigned int clear;
+ loff_t pos;
+ long gap; /* distance from eof to pos */
+
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)
+ return page;
+ address &= PAGE_MASK;
+ pos = address - area->vm_start + area->vm_offset;
+
+ clear = 0;
+ gap = inode->i_size - pos;
+ if (gap <= 0){
+ /* mmaping beyond end of file */
+ clear = PAGE_SIZE;
+ }else{
+ int cur_read;
+ int need_read;
+ /*struct file *filp = area->vm_file;*/
+ struct file filp;
+ if (gap < PAGE_SIZE){
+ clear = PAGE_SIZE - gap;
+ }
+ filp.f_reada = 0;
+ filp.f_pos = pos;
+ filp.f_dentry=area->vm_file->f_dentry;
+ need_read = PAGE_SIZE - clear;
+ {
+ mm_segment_t cur_fs = get_fs();
+ set_fs (KERNEL_DS);
+ cur_read = generic_file_read (&filp,(char*)page
+ ,need_read,&pos);
+ set_fs (cur_fs);
+ }
+ if (cur_read != need_read){
+ hpfs_error(inode->i_sb, "Error while reading an mmap file %08x", inode->i_ino);
+ }
+ }
+ if (clear > 0){
+ memset ((char*)page+PAGE_SIZE-clear,0,clear);
+ }
+ return page;
+}
+
+struct vm_operations_struct hpfs_file_mmap = {
+ NULL, /* open */
+ NULL, /* close */
+ NULL, /* unmap */
+ NULL, /* protect */
+ NULL, /* sync */
+ NULL, /* advise */
+ hpfs_file_mmap_nopage, /* nopage */
+ NULL, /* wppage */
+ NULL, /* swapout */
+ NULL, /* swapin */
+};
+
+/*
+ * This is used for a general mmap of an msdos file
+ * Returns 0 if ok, or a negative error code if not.
+ */
+int hpfs_mmap(struct file * file, struct vm_area_struct * vma)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ /*printk("start mmap\n");*/
+ if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */
+ return -EINVAL;
+ if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
+ return -EINVAL;
+ if (!inode->i_sb || !S_ISREG(inode->i_mode))
+ return -EACCES;
+ /*if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ mark_inode_dirty(inode);
+ }*/
+
+ vma->vm_file = file;
+ /*inode->i_count++;*/
+ file->f_count++;
+ vma->vm_ops = &hpfs_file_mmap;
+ /*printk("end mmap\n");*/
+ return 0;
+}
diff --git a/fs/hpfs/name.c b/fs/hpfs/name.c
new file mode 100644
index 000000000..f9fa94c7e
--- /dev/null
+++ b/fs/hpfs/name.c
@@ -0,0 +1,144 @@
+/*
+ * linux/fs/hpfs/name.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * operations with filenames
+ */
+
+#include <linux/string.h>
+#include "hpfs_fn.h"
+
+char *text_postfix[]={
+".ASM", ".BAS", ".BAT", ".C", ".CC", ".CFG", ".CMD", ".CON", ".CPP", ".DEF",
+".DOC", ".DPR", ".ERX", ".H", ".HPP", ".HTM", ".HTML", ".JAVA", ".LOG", ".PAS",
+".RC", ".TEX", ".TXT", ".Y", ""};
+
+char *text_prefix[]={
+"AUTOEXEC.", "CHANGES", "COPYING", "CONFIG.", "CREDITS", "FAQ", "FILE_ID.DIZ",
+"MAKEFILE", "READ.ME", "README", "TERMCAP", ""};
+
+void hpfs_decide_conv(struct inode *inode, unsigned char *name, unsigned len)
+{
+ int i;
+ if (inode->i_hpfs_conv != CONV_AUTO) return;
+ for (i = 0; *text_postfix[i]; i++) {
+ int l = strlen(text_postfix[i]);
+ if (l <= len)
+ if (!hpfs_compare_names(inode->i_sb, text_postfix[i], l, name + len - l, l, 0))
+ goto text;
+ }
+ for (i = 0; *text_prefix[i]; i++) {
+ int l = strlen(text_prefix[i]);
+ if (l <= len)
+ if (!hpfs_compare_names(inode->i_sb, text_prefix[i], l, name, l, 0))
+ goto text;
+ }
+ inode->i_hpfs_conv = CONV_BINARY;
+ return;
+ text:
+ inode->i_hpfs_conv = CONV_TEXT;
+ return;
+}
+
+static inline int not_allowed_char(unsigned char c)
+{
+ return c<' ' || c=='"' || c=='*' || c=='/' || c==':' || c=='<' ||
+ c=='>' || c=='?' || c=='\\' || c=='|';
+}
+
+static inline int no_dos_char(unsigned char c)
+{ /* Characters that are allowed in HPFS but not in DOS */
+ return c=='+' || c==',' || c==';' || c=='=' || c=='[' || c==']';
+}
+
+static inline unsigned char upcase(unsigned char *dir, unsigned char a)
+{
+ if (a<128 || a==255) return a>='a' && a<='z' ? a - 0x20 : a;
+ if (!dir) return a;
+ return dir[a-128];
+}
+
+unsigned char hpfs_upcase(unsigned char *dir, unsigned char a)
+{
+ return upcase(dir, a);
+}
+
+static inline unsigned char locase(unsigned char *dir, unsigned char a)
+{
+ if (a<128 || a==255) return a>='A' && a<='Z' ? a + 0x20 : a;
+ if (!dir) return a;
+ return dir[a];
+}
+
+int hpfs_chk_name(unsigned char *name, unsigned *len)
+{
+ int i;
+ if (*len > 254) return -ENAMETOOLONG;
+ hpfs_adjust_length(name, len);
+ if (!*len) return -EINVAL;
+ for (i = 0; i < *len; i++) if (not_allowed_char(name[i])) return -EINVAL;
+ if (*len == 1) if (name[0] == '.') return -EINVAL;
+ if (*len == 2) if (name[0] == '.' && name[1] == '.') return -EINVAL;
+ return 0;
+}
+
+char *hpfs_translate_name(struct super_block *s, unsigned char *from,
+ unsigned len, int lc, int lng)
+{
+ char *to;
+ int i;
+ if (s->s_hpfs_chk >= 2) if (hpfs_is_name_long(from, len) != lng) {
+ printk("HPFS: Long name flag mismatch - name ");
+ for (i=0; i<len; i++) printk("%c", from[i]);
+ printk(" misidentified as %s.\n", lng ? "short" : "long");
+ printk("HPFS: It's nothing serious. It could happen because of bug in OS/2.\nHPFS: Set checks=normal to disable this message.\n");
+ }
+ if (!lc) return from;
+ if (!(to = kmalloc(len, GFP_KERNEL))) {
+ printk("HPFS: can't allocate memory for name conversion buffer\n");
+ return from;
+ }
+ for (i = 0; i < len; i++) to[i] = locase(s->s_hpfs_cp_table,from[i]);
+ return to;
+}
+
+int hpfs_compare_names(struct super_block *s, unsigned char *n1, unsigned l1,
+ unsigned char *n2, unsigned l2, int last)
+{
+ unsigned l = l1 < l2 ? l1 : l2;
+ unsigned i;
+ if (last) return -1;
+ for (i = 0; i < l; i++) {
+ unsigned char c1 = upcase(s->s_hpfs_cp_table,n1[i]);
+ unsigned char c2 = upcase(s->s_hpfs_cp_table,n2[i]);
+ if (c1 < c2) return -1;
+ if (c1 > c2) return 1;
+ }
+ if (l1 < l2) return -1;
+ if (l1 > l2) return 1;
+ return 0;
+}
+
+int hpfs_is_name_long(unsigned char *name, unsigned len)
+{
+ int i,j;
+ for (i = 0; i < len && name[i] != '.'; i++)
+ if (no_dos_char(name[i])) return 1;
+ if (!i || i > 8) return 1;
+ if (i == len) return 0;
+ for (j = i + 1; j < len; j++)
+ if (name[j] == '.' || no_dos_char(name[i])) return 1;
+ return j - i > 4;
+}
+
+/* OS/2 clears dots and spaces at the end of file name, so we have to */
+
+void hpfs_adjust_length(unsigned char *name, unsigned *len)
+{
+ if (!*len) return;
+ if (*len == 1 && name[0] == '.') return;
+ if (*len == 2 && name[0] == '.' && name[1] == '.') return;
+ while (*len && (name[*len - 1] == '.' || name[*len - 1] == ' '))
+ (*len)--;
+}
diff --git a/fs/hpfs/namei.c b/fs/hpfs/namei.c
new file mode 100644
index 000000000..fe9b66e3d
--- /dev/null
+++ b/fs/hpfs/namei.c
@@ -0,0 +1,549 @@
+/*
+ * linux/fs/hpfs/namei.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * adding & removing files & directories
+ */
+
+#include <linux/string.h>
+#include "hpfs_fn.h"
+
+int hpfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct quad_buffer_head qbh0;
+ struct buffer_head *bh;
+ struct hpfs_dirent *de;
+ struct fnode *fnode;
+ struct dnode *dnode;
+ struct inode *result;
+ fnode_secno fno;
+ dnode_secno dno;
+ int r;
+ struct hpfs_dirent dee;
+ int err;
+ if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
+ if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+ if (!(dnode = hpfs_alloc_dnode(dir->i_sb, fno, &dno, &qbh0, 1))) goto bail1;
+ memset(&dee, 0, sizeof dee);
+ dee.directory = 1;
+ if (!(mode & 0222)) dee.read_only = 1;
+ /*dee.archive = 0;*/
+ dee.hidden = name[0] == '.';
+ dee.fnode = fno;
+ dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+ hpfs_lock_inode(dir);
+ r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+ if (r == 1) goto bail2;
+ if (r == -1) {
+ brelse(bh);
+ hpfs_brelse4(&qbh0);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_free_dnode(dir->i_sb, dno);
+ hpfs_unlock_inode(dir);
+ return -EEXIST;
+ }
+ fnode->len = len;
+ memcpy(fnode->name, name, len > 15 ? 15 : len);
+ fnode->up = dir->i_ino;
+ fnode->dirflag = 1;
+ fnode->btree.n_free_nodes = 7;
+ fnode->btree.n_used_nodes = 1;
+ fnode->btree.first_free = 0x14;
+ fnode->u.external[0].disk_secno = dno;
+ fnode->u.external[0].file_secno = -1;
+ dnode->root_dnode = 1;
+ dnode->up = fno;
+ de = hpfs_add_de(dir->i_sb, dnode, "\001\001", 2, 0);
+ de->creation_date = de->write_date = de->read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+ if (!(mode & 0222)) de->read_only = 1;
+ de->first = de->directory = 1;
+ /*de->hidden = de->system = 0;*/
+ de->fnode = fno;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ hpfs_mark_4buffers_dirty(&qbh0);
+ hpfs_brelse4(&qbh0);
+ dir->i_nlink++;
+ hpfs_lock_iget(dir->i_sb, 1);
+ if ((result = iget(dir->i_sb, fno))) {
+ result->i_hpfs_parent_dir = dir->i_ino;
+ result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
+ result->i_hpfs_ea_size = 0;
+ if (dee.read_only) result->i_mode &= ~0222;
+ if (result->i_uid != current->fsuid ||
+ result->i_gid != current->fsgid ||
+ result->i_mode != (mode | S_IFDIR)) {
+ result->i_uid = current->fsuid;
+ result->i_gid = current->fsgid;
+ result->i_mode = mode | S_IFDIR;
+ hpfs_write_inode_nolock(result);
+ }
+ d_instantiate(dentry, result);
+ }
+ hpfs_unlock_iget(dir->i_sb);
+ hpfs_unlock_inode(dir);
+ return 0;
+ bail2:
+ hpfs_brelse4(&qbh0);
+ hpfs_free_dnode(dir->i_sb, dno);
+ hpfs_unlock_inode(dir);
+ bail1:
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ bail:
+ return -ENOSPC;
+}
+
+int hpfs_create(struct inode *dir, struct dentry *dentry, int mode)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct inode *result = NULL;
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ fnode_secno fno;
+ int r;
+ struct hpfs_dirent dee;
+ int err;
+ if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
+ if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+ memset(&dee, 0, sizeof dee);
+ if (!(mode & 0222)) dee.read_only = 1;
+ dee.archive = 1;
+ dee.hidden = name[0] == '.';
+ dee.fnode = fno;
+ dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+ hpfs_lock_inode(dir);
+ r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+ if (r == 1) goto bail1;
+ if (r == -1) {
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_unlock_inode(dir);
+ return -EEXIST;
+ }
+ fnode->len = len;
+ memcpy(fnode->name, name, len > 15 ? 15 : len);
+ fnode->up = dir->i_ino;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ hpfs_lock_iget(dir->i_sb, 2);
+ if ((result = iget(dir->i_sb, fno))) {
+ hpfs_decide_conv(result, (char *)name, len);
+ result->i_hpfs_parent_dir = dir->i_ino;
+ result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
+ result->i_hpfs_ea_size = 0;
+ if (dee.read_only) result->i_mode &= ~0222;
+ if (result->i_blocks == -1) result->i_blocks = 1;
+ if (result->i_size == -1) result->i_size = 0;
+ if (result->i_uid != current->fsuid ||
+ result->i_gid != current->fsgid ||
+ result->i_mode != (mode | S_IFREG)) {
+ result->i_uid = current->fsuid;
+ result->i_gid = current->fsgid;
+ result->i_mode = mode | S_IFREG;
+ hpfs_write_inode_nolock(result);
+ }
+ d_instantiate(dentry, result);
+ }
+ hpfs_unlock_iget(dir->i_sb);
+ hpfs_unlock_inode(dir);
+ return 0;
+ bail1:
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_unlock_inode(dir);
+ bail:
+ return -ENOSPC;
+}
+
+int hpfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ fnode_secno fno;
+ int r;
+ struct hpfs_dirent dee;
+ struct inode *result = NULL;
+ int err;
+ if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
+ if (dir->i_sb->s_hpfs_eas < 2) return -EPERM;
+ if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+ memset(&dee, 0, sizeof dee);
+ if (!(mode & 0222)) dee.read_only = 1;
+ dee.archive = 1;
+ dee.hidden = name[0] == '.';
+ dee.fnode = fno;
+ dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+ hpfs_lock_inode(dir);
+ r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+ if (r == 1) goto bail1;
+ if (r == -1) {
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_unlock_inode(dir);
+ return -EEXIST;
+ }
+ fnode->len = len;
+ memcpy(fnode->name, name, len > 15 ? 15 : len);
+ fnode->up = dir->i_ino;
+ mark_buffer_dirty(bh, 1);
+ hpfs_lock_iget(dir->i_sb, 2);
+ if ((result = iget(dir->i_sb, fno))) {
+ result->i_hpfs_parent_dir = dir->i_ino;
+ result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
+ result->i_hpfs_ea_size = 0;
+ /*if (result->i_blocks == -1) result->i_blocks = 1;
+ if (result->i_size == -1) result->i_size = 0;*/
+ result->i_uid = current->fsuid;
+ result->i_gid = current->fsgid;
+ result->i_nlink = 1;
+ result->i_size = 0;
+ result->i_blocks = 1;
+ init_special_inode(result, mode, rdev);
+ hpfs_write_inode_nolock(result);
+ d_instantiate(dentry, result);
+ }
+ hpfs_unlock_iget(dir->i_sb);
+ hpfs_unlock_inode(dir);
+ brelse(bh);
+ return 0;
+ bail1:
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_unlock_inode(dir);
+ bail:
+ return -ENOSPC;
+}
+
+extern const struct inode_operations hpfs_symlink_iops;
+
+int hpfs_symlink(struct inode *dir, struct dentry *dentry, const char *symlink)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ fnode_secno fno;
+ int r;
+ struct hpfs_dirent dee;
+ struct inode *result;
+ int err;
+ if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
+ if (dir->i_sb->s_hpfs_eas < 2) return -EPERM;
+ if (!(fnode = hpfs_alloc_fnode(dir->i_sb, dir->i_hpfs_dno, &fno, &bh))) goto bail;
+ memset(&dee, 0, sizeof dee);
+ dee.archive = 1;
+ dee.hidden = name[0] == '.';
+ dee.fnode = fno;
+ dee.creation_date = dee.write_date = dee.read_date = gmt_to_local(dir->i_sb, CURRENT_TIME);
+ hpfs_lock_inode(dir);
+ r = hpfs_add_dirent(dir, (char *)name, len, &dee, 0);
+ if (r == 1) goto bail1;
+ if (r == -1) {
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_unlock_inode(dir);
+ return -EEXIST;
+ }
+ fnode->len = len;
+ memcpy(fnode->name, name, len > 15 ? 15 : len);
+ fnode->up = dir->i_ino;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ hpfs_lock_iget(dir->i_sb, 2);
+ if ((result = iget(dir->i_sb, fno))) {
+ result->i_hpfs_parent_dir = dir->i_ino;
+ result->i_ctime = result->i_mtime = result->i_atime = local_to_gmt(dir->i_sb, dee.creation_date);
+ result->i_hpfs_ea_size = 0;
+ /*if (result->i_blocks == -1) result->i_blocks = 1;
+ if (result->i_size == -1) result->i_size = 0;*/
+ result->i_mode = S_IFLNK | 0777;
+ result->i_uid = current->fsuid;
+ result->i_gid = current->fsgid;
+ result->i_blocks = 1;
+ result->i_size = strlen(symlink);
+ result->i_op = (struct inode_operations *) &hpfs_symlink_iops;
+ if ((fnode = hpfs_map_fnode(dir->i_sb, fno, &bh))) {
+ hpfs_set_ea(result, fnode, "SYMLINK", (char *)symlink, strlen(symlink));
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ }
+ hpfs_write_inode_nolock(result);
+ d_instantiate(dentry, result);
+ }
+ hpfs_unlock_iget(dir->i_sb);
+ hpfs_unlock_inode(dir);
+ return 0;
+ bail1:
+ brelse(bh);
+ hpfs_free_sectors(dir->i_sb, fno, 1);
+ hpfs_unlock_inode(dir);
+ bail:
+ return -ENOSPC;
+}
+
+int hpfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de;
+ struct inode *inode = dentry->d_inode;
+ dnode_secno dno;
+ fnode_secno fno;
+ int r;
+ int rep = 0;
+ hpfs_adjust_length((char *)name, &len);
+ again:
+ hpfs_lock_2inodes(dir, inode);
+ if (!(de = map_dirent(dir, dir->i_hpfs_dno, (char *)name, len, &dno, &qbh))) {
+ hpfs_unlock_2inodes(dir, inode);
+ return -ENOENT;
+ }
+ if (de->first) {
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_2inodes(dir, inode);
+ return -EPERM;
+ }
+ if (de->directory) {
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_2inodes(dir, inode);
+ return -EISDIR;
+ }
+ fno = de->fnode;
+ if ((r = hpfs_remove_dirent(dir, dno, de, &qbh, 1)) == 1) hpfs_error(dir->i_sb, "there was error when removing dirent");
+ if (r != 2) {
+ inode->i_nlink--;
+ hpfs_unlock_2inodes(dir, inode);
+ d_delete(dentry);
+ } else { /* no space for deleting, try to truncate file */
+ struct iattr newattrs;
+ hpfs_unlock_2inodes(dir, inode);
+ if (rep || dentry->d_count > 1 || permission(inode, MAY_WRITE) || get_write_access(inode)) goto ret;
+ /*printk("HPFS: truncating file before delete.\n");*/
+ down(&inode->i_sem); /* do_truncate should be called here, but it's */
+ newattrs.ia_size = 0; /* not exported */
+ newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
+ if (notify_change(dentry, &newattrs)) {
+ up(&inode->i_sem);
+ put_write_access(inode);
+ goto ret;
+ }
+ vmtruncate(inode, 0);
+ if (inode->i_op && inode->i_op->truncate) inode->i_op->truncate(inode);
+ up(&inode->i_sem);
+ put_write_access(inode);
+ rep = 1;
+ goto again;
+ }
+ ret:
+ return r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0;
+}
+
+int hpfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ const char *name = dentry->d_name.name;
+ unsigned len = dentry->d_name.len;
+ struct quad_buffer_head qbh;
+ struct hpfs_dirent *de;
+ struct inode *inode = dentry->d_inode;
+ dnode_secno dno;
+ fnode_secno fno;
+ int n_items = 0;
+ int r;
+ hpfs_adjust_length((char *)name, &len);
+ hpfs_lock_2inodes(dir, inode);
+ if (!(de = map_dirent(dir, dir->i_hpfs_dno, (char *)name, len, &dno, &qbh))) {
+ hpfs_unlock_2inodes(dir, inode);
+ return -ENOENT;
+ }
+ if (de->first) {
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_2inodes(dir, inode);
+ return -EPERM;
+ }
+ if (!de->directory) {
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_2inodes(dir, inode);
+ return -ENOTDIR;
+ }
+ if (!list_empty(&dentry->d_hash)) {
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_2inodes(dir, inode);
+ return -EBUSY;
+ }
+ hpfs_count_dnodes(dir->i_sb, inode->i_hpfs_dno, NULL, NULL, &n_items);
+ if (n_items) {
+ hpfs_brelse4(&qbh);
+ hpfs_unlock_2inodes(dir, inode);
+ return -ENOTEMPTY;
+ }
+ fno = de->fnode;
+ if ((r = hpfs_remove_dirent(dir, dno, de, &qbh, 1)) == 1)
+ hpfs_error(dir->i_sb, "there was error when removing dirent");
+ if (r != 2) {
+ dir->i_nlink--;
+ inode->i_nlink = 0;
+ hpfs_unlock_2inodes(dir, inode);
+ d_delete(dentry);
+ } else hpfs_unlock_2inodes(dir, inode);
+ return r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0;
+}
+
+int hpfs_readlink(struct dentry *dentry, char *buf, int len)
+{
+ struct inode *i = dentry->d_inode;
+ struct fnode *fnode;
+ struct buffer_head *bh;
+ char *symlink;
+ int slen;
+ if (!S_ISLNK(i->i_mode)) {
+ return -EINVAL;
+ }
+ if (!(fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) {
+ return -EIO;
+ }
+ if (!(symlink = hpfs_get_ea(i->i_sb, fnode, "SYMLINK", &slen))) {
+ brelse(bh);
+ return -EFSERROR;
+ }
+ brelse(bh);
+ if (slen > len) slen = len;
+ memcpy_tofs(buf, symlink, slen);
+ kfree(symlink);
+ return slen;
+}
+
+struct dentry *hpfs_follow_link(struct dentry *dinode, struct dentry *ddir,
+ unsigned int follow)
+{
+ struct inode *inode = dinode->d_inode;
+ char *link;
+ unsigned len;
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ if (!(fnode = hpfs_map_fnode(inode->i_sb, inode->i_ino, &bh))) {
+ dput(dinode);
+ return ERR_PTR(-EIO);
+ }
+ if (!(link = hpfs_get_ea(inode->i_sb, fnode, "SYMLINK", &len))) {
+ brelse(bh);
+ dput(dinode);
+ return ERR_PTR(-EIO);
+ }
+ brelse(bh);
+ UPDATE_ATIME(inode);
+ ddir = lookup_dentry(link, ddir, follow);
+ kfree(link);
+ return ddir;
+}
+
+int hpfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ char *old_name = (char *)old_dentry->d_name.name;
+ int old_len = old_dentry->d_name.len;
+ char *new_name = (char *)new_dentry->d_name.name;
+ int new_len = new_dentry->d_name.len;
+ struct inode *i = old_dentry->d_inode;
+ struct inode *new_inode = new_dentry->d_inode;
+ struct quad_buffer_head qbh, qbh1;
+ struct hpfs_dirent *dep, *nde;
+ struct hpfs_dirent de;
+ dnode_secno dno;
+ int r;
+ struct buffer_head *bh;
+ struct fnode *fnode;
+ int err;
+ if ((err = hpfs_chk_name((char *)new_name, &new_len))) return err;
+ err = 0;
+ hpfs_adjust_length((char *)old_name, &old_len);
+
+ hpfs_lock_3inodes(old_dir, new_dir, i);
+
+ /* Erm? Moving over the empty non-busy directory is perfectly legal */
+ if (new_inode && S_ISDIR(new_inode->i_mode)) {
+ err = -EINVAL;
+ goto end1;
+ }
+
+ if (!(dep = map_dirent(old_dir, old_dir->i_hpfs_dno, (char *)old_name, old_len, &dno, &qbh))) {
+ hpfs_error(i->i_sb, "lookup succeeded but map dirent failed");
+ err = -ENOENT;
+ goto end1;
+ }
+ copy_de(&de, dep);
+ de.hidden = new_name[0] == '.';
+
+ if (new_inode) {
+ int r;
+ if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 1)) != 2) {
+ if ((nde = map_dirent(new_dir, new_dir->i_hpfs_dno, (char *)new_name, new_len, NULL, &qbh1))) {
+ new_inode->i_nlink = 0;
+ copy_de(nde, &de);
+ memcpy(nde->name, new_name, new_len);
+ hpfs_mark_4buffers_dirty(&qbh1);
+ hpfs_brelse4(&qbh1);
+ goto end;
+ }
+ hpfs_error(new_dir->i_sb, "hpfs_rename: could not find dirent");
+ err = -EFSERROR;
+ goto end1;
+ }
+ err = r == 2 ? -ENOSPC : r == 1 ? -EFSERROR : 0;
+ goto end1;
+ }
+
+ if (new_dir == old_dir) hpfs_brelse4(&qbh);
+
+ hpfs_lock_creation(i->i_sb);
+ if ((r = hpfs_add_dirent(new_dir, new_name, new_len, &de, 1))) {
+ hpfs_unlock_creation(i->i_sb);
+ if (r == -1) hpfs_error(new_dir->i_sb, "hpfs_rename: dirent already exists!");
+ err = r == 1 ? -ENOSPC : -EFSERROR;
+ if (new_dir != old_dir) hpfs_brelse4(&qbh);
+ goto end1;
+ }
+
+ if (new_dir == old_dir)
+ if (!(dep = map_dirent(old_dir, old_dir->i_hpfs_dno, (char *)old_name, old_len, &dno, &qbh))) {
+ hpfs_unlock_creation(i->i_sb);
+ hpfs_error(i->i_sb, "lookup succeeded but map dirent failed at #2");
+ err = -ENOENT;
+ goto end1;
+ }
+
+ if ((r = hpfs_remove_dirent(old_dir, dno, dep, &qbh, 0))) {
+ hpfs_unlock_creation(i->i_sb);
+ hpfs_error(i->i_sb, "hpfs_rename: could not remove dirent");
+ err = r == 2 ? -ENOSPC : -EFSERROR;
+ goto end1;
+ }
+ hpfs_unlock_creation(i->i_sb);
+
+ end:
+ i->i_hpfs_parent_dir = new_dir->i_ino;
+ if (S_ISDIR(i->i_mode)) {
+ new_dir->i_nlink++;
+ old_dir->i_nlink--;
+ }
+ if ((fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) {
+ fnode->up = new_dir->i_ino;
+ fnode->len = new_len;
+ memcpy(fnode->name, new_name, new_len>15?15:new_len);
+ if (new_len < 15) memset(&fnode->name[new_len], 0, 15 - new_len);
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ }
+ i->i_hpfs_conv = i->i_sb->s_hpfs_conv;
+ hpfs_decide_conv(i, (char *)new_name, new_len);
+ end1:
+ hpfs_unlock_3inodes(old_dir, new_dir, i);
+ return err;
+}
diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c
new file mode 100644
index 000000000..a50f2a49b
--- /dev/null
+++ b/fs/hpfs/super.c
@@ -0,0 +1,598 @@
+/*
+ * linux/fs/hpfs/super.c
+ *
+ * Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
+ *
+ * mouning, unmounting, error handling
+ */
+
+#include <linux/string.h>
+#include "hpfs_fn.h"
+#include <linux/module.h>
+
+/* Mark the filesystem dirty, so that chkdsk checks it when os/2 booted */
+
+static void mark_dirty(struct super_block *s)
+{
+ if (s->s_hpfs_chkdsk && !(s->s_flags & MS_RDONLY)) {
+ struct buffer_head *bh;
+ struct hpfs_spare_block *sb;
+ if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
+ sb->dirty = 1;
+ sb->old_wrote = 0;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ }
+ }
+}
+
+/* Mark the filesystem clean (mark it dirty for chkdsk if chkdsk==2 or if there
+ were errors) */
+
+static void unmark_dirty(struct super_block *s)
+{
+ struct buffer_head *bh;
+ struct hpfs_spare_block *sb;
+ if (s->s_flags & MS_RDONLY) return;
+ if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
+ sb->dirty = s->s_hpfs_chkdsk > 1 - s->s_hpfs_was_error;
+ sb->old_wrote = s->s_hpfs_chkdsk >= 2 && !s->s_hpfs_was_error;
+ mark_buffer_dirty(bh, 1);
+ brelse(bh);
+ }
+}
+
+/* Filesystem error... */
+
+#define ERR_BUF_SIZE 1024
+
+void hpfs_error(struct super_block *s, char *m,...)
+{
+ char *buf;
+ va_list l;
+ va_start(l, m);
+ if (!(buf = kmalloc(ERR_BUF_SIZE, GFP_KERNEL)))
+ printk("HPFS: No memory for error message '%s'\n",m);
+ else if (vsprintf(buf, m, l) >= ERR_BUF_SIZE)
+ printk("HPFS: Grrrr... Kernel memory corrupted ... going on, but it'll crash very soon :-(\n");
+ printk("HPFS: filesystem error: ");
+ if (buf) printk("%s", buf);
+ else printk("%s\n",m);
+ if (!s->s_hpfs_was_error) {
+ if (s->s_hpfs_err == 2) {
+ printk("; crashing the system because you wanted it\n");
+ mark_dirty(s);
+ panic("HPFS panic");
+ } else if (s->s_hpfs_err == 1) {
+ if (s->s_flags & MS_RDONLY) printk("; already mounted read-only\n");
+ else {
+ printk("; remounting read-only\n");
+ mark_dirty(s);
+ s->s_flags |= MS_RDONLY;
+ }
+ } else if (s->s_flags & MS_RDONLY) printk("; going on - but anything won't be destroyed because it's read-only\n");
+ else printk("; corrupted filesystem mounted read/write - your computer will explode within 20 seconds ... but you wanted it so!\n");
+ } else printk("\n");
+ if (buf) kfree(buf);
+ s->s_hpfs_was_error = 1;
+}
+
+/*
+ * A little trick to detect cycles in many hpfs structures and don't let the
+ * kernel crash on corrupted filesystem. When first called, set c2 to 0.
+ *
+ * BTW. chkdsk doesn't detect cycles correctly. When I had 2 lost directories
+ * nested each in other, chkdsk locked up happilly.
+ */
+
+int hpfs_stop_cycles(struct super_block *s, int key, int *c1, int *c2,
+ char *msg)
+{
+ if (*c2 && *c1 == key) {
+ hpfs_error(s, "cycle detected on key %08x in %s", key, msg);
+ return 1;
+ }
+ (*c2)++;
+ if (!((*c2 - 1) & *c2)) *c1 = key;
+ return 0;
+}
+
+void hpfs_put_super(struct super_block *s)
+{
+ if (s->s_hpfs_cp_table) kfree(s->s_hpfs_cp_table);
+ if (s->s_hpfs_bmp_dir) kfree(s->s_hpfs_bmp_dir);
+ unmark_dirty(s);
+ s->s_dev = 0;
+ MOD_DEC_USE_COUNT;
+}
+
+unsigned hpfs_count_one_bitmap(struct super_block *s, secno secno)
+{
+ struct quad_buffer_head qbh;
+ unsigned *bits;
+ unsigned i, count;
+ if (!(bits = hpfs_map_4sectors(s, secno, &qbh, 4))) return 0;
+ count = 0;
+ for (i = 0; i < 2048 / sizeof(unsigned); i++) {
+ unsigned b;
+ if (!bits[i]) continue;
+ for (b = bits[i]; b; b>>=1) count += b & 1;
+ }
+ hpfs_brelse4(&qbh);
+ return count;
+}
+
+static unsigned count_bitmaps(struct super_block *s)
+{
+ unsigned n, count, n_bands;
+ n_bands = (s->s_hpfs_fs_size + 0x3fff) >> 14;
+ count = 0;
+ for (n = 0; n < n_bands; n++)
+ count += hpfs_count_one_bitmap(s, s->s_hpfs_bmp_dir[n]);
+ return count;
+}
+
+int hpfs_statfs(struct super_block *s, struct statfs *buf, int bufsiz)
+{
+ struct statfs tmp;
+ /*if (s->s_hpfs_n_free == -1) {*/
+ s->s_hpfs_n_free = count_bitmaps(s);
+ s->s_hpfs_n_free_dnodes = hpfs_count_one_bitmap(s, s->s_hpfs_dmap);
+ /*}*/
+ tmp.f_type = s->s_magic;
+ tmp.f_bsize = 512;
+ tmp.f_blocks = s->s_hpfs_fs_size;
+ tmp.f_bfree = s->s_hpfs_n_free;
+ tmp.f_bavail = s->s_hpfs_n_free;
+ tmp.f_files = s->s_hpfs_dirband_size / 4;
+ tmp.f_ffree = s->s_hpfs_n_free_dnodes;
+ tmp.f_namelen = 254;
+ return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0;
+}
+
+/* Super operations */
+
+static const struct super_operations hpfs_sops =
+{
+ hpfs_read_inode, /* read_inode */
+ NULL, /* write_inode */
+ NULL, /* put_inode */
+ hpfs_delete_inode, /* delete inode */
+ hpfs_notify_change, /* notify_change */
+ hpfs_put_super, /* put_super */
+ NULL, /* write_super */
+ hpfs_statfs, /* statfs */
+ hpfs_remount_fs, /* remount_fs */
+ NULL, /* clear inode */
+ NULL, /* umount_begin */
+};
+
+/*
+ * A tiny parser for option strings, stolen from dosfs.
+ *
+ * Stolen again from read-only hpfs.
+ */
+
+int parse_opts(char *opts, uid_t *uid, gid_t *gid, umode_t *umask,
+ int *lowercase, int *conv, int *eas, int *chk, int *errs,
+ int *chkdsk, int *timeshift)
+{
+ char *p, *rhs;
+
+ if (!opts)
+ return 1;
+
+ /*printk("Parsing opts: '%s'\n",opts);*/
+
+ for (p = strtok(opts, ","); p != 0; p = strtok(0, ",")) {
+ if ((rhs = strchr(p, '=')) != 0)
+ *rhs++ = '\0';
+ if (!strcmp(p, "help")) return 2;
+ if (!strcmp(p, "uid")) {
+ if (!rhs || !*rhs)
+ return 0;
+ *uid = simple_strtoul(rhs, &rhs, 0);
+ if (*rhs)
+ return 0;
+ }
+ else if (!strcmp(p, "gid")) {
+ if (!rhs || !*rhs)
+ return 0;
+ *gid = simple_strtoul(rhs, &rhs, 0);
+ if (*rhs)
+ return 0;
+ }
+ else if (!strcmp(p, "umask")) {
+ if (!rhs || !*rhs)
+ return 0;
+ *umask = simple_strtoul(rhs, &rhs, 8);
+ if (*rhs)
+ return 0;
+ }
+ else if (!strcmp(p, "timeshift")) {
+ int m = 1;
+ if (!rhs || !*rhs)
+ return 0;
+ if (*rhs == '-') m = -1;
+ if (*rhs == '+' || *rhs == '-') rhs++;
+ *timeshift = simple_strtoul(rhs, &rhs, 0) * m;
+ if (*rhs)
+ return 0;
+ }
+ else if (!strcmp(p, "case")) {
+ if (!strcmp(rhs, "lower"))
+ *lowercase = 1;
+ else if (!strcmp(rhs, "asis"))
+ *lowercase = 0;
+ else
+ return 0;
+ }
+ else if (!strcmp(p, "conv")) {
+ if (!strcmp(rhs, "binary"))
+ *conv = CONV_BINARY;
+ else if (!strcmp(rhs, "text"))
+ *conv = CONV_TEXT;
+ else if (!strcmp(rhs, "auto"))
+ *conv = CONV_AUTO;
+ else
+ return 0;
+ }
+ else if (!strcmp(p, "check")) {
+ if (!strcmp(rhs, "none"))
+ *chk = 0;
+ else if (!strcmp(rhs, "normal"))
+ *chk = 1;
+ else if (!strcmp(rhs, "strict"))
+ *chk = 2;
+ else
+ return 0;
+ }
+ else if (!strcmp(p, "errors")) {
+ if (!strcmp(rhs, "continue"))
+ *errs = 0;
+ else if (!strcmp(rhs, "remount-ro"))
+ *errs = 1;
+ else if (!strcmp(rhs, "panic"))
+ *errs = 2;
+ else
+ return 0;
+ }
+ else if (!strcmp(p, "eas")) {
+ if (!strcmp(rhs, "no"))
+ *eas = 0;
+ else if (!strcmp(rhs, "ro"))
+ *eas = 1;
+ else if (!strcmp(rhs, "rw"))
+ *eas = 2;
+ else
+ return 0;
+ }
+ else if (!strcmp(p, "chkdsk")) {
+ if (!strcmp(rhs, "no"))
+ *chkdsk = 0;
+ else if (!strcmp(rhs, "errors"))
+ *chkdsk = 1;
+ else if (!strcmp(rhs, "always"))
+ *chkdsk = 2;
+ else
+ return 0;
+ }
+ else
+ return 0;
+ }
+ return 1;
+}
+
+static inline void hpfs_help()
+{
+ printk("\n\
+HPFS filesystem options:\n\
+ help do not mount and display this text\n\
+ uid=xxx set uid of files that don't have uid specified in eas\n\
+ gid=xxx set gid of files that don't have gid specified in eas\n\
+ umask=xxx set mode of files that don't have mode specified in eas\n\
+ case=lower lowercase all files\n\
+ case=asis do not lowercase files (default)\n\
+ conv=binary do not convert CR/LF -> LF (default)\n\
+ conv=auto convert only files with known text extensions\n\
+ conv=text convert all files\n\
+ check=none no fs checks - kernel may crash on corrupted filesystem\n\
+ check=normal do some checks - it should not crash (default)\n\
+ check=strict do extra time-consuming checks, used for debugging\n\
+ errors=continue continue on errors\n\
+ errors=remount-ro remount read-only if errors found (default)\n\
+ errors=panic panic on errors\n\
+ chkdsk=no do not mark fs for chkdsking even if there were errors\n\
+ chkdsk=errors mark fs dirty if errors found (default)\n\
+ chkdsk=always always mark fs dirty - used for debugging\n\
+ eas=no ignore extended attributes\n\
+ eas=ro read but do not write extended attributes\n\
+ eas=rw r/w eas => enables chmod, chown, mknod, ln -s (default)\n\
+ timeshift=nnn add nnn seconds to file times\n\
+\n");
+}
+
+int hpfs_remount_fs(struct super_block *s, int *flags, char *data)
+{
+ uid_t uid;
+ gid_t gid;
+ umode_t umask;
+ int lowercase, conv, eas, chk, errs, chkdsk, timeshift;
+ int o;
+
+ *flags |= MS_NOATIME;
+
+ uid = s->s_hpfs_uid; gid = s->s_hpfs_gid;
+ umask = 0777 & ~s->s_hpfs_mode;
+ lowercase = s->s_hpfs_lowercase; conv = s->s_hpfs_conv;
+ eas = s->s_hpfs_eas; chk = s->s_hpfs_chk; chkdsk = s->s_hpfs_chkdsk;
+ errs = s->s_hpfs_err; timeshift = s->s_hpfs_timeshift;
+
+ if (!(o = parse_opts(data, &uid, &gid, &umask, &lowercase, &conv,
+ &eas, &chk, &errs, &chkdsk, &timeshift))) {
+ printk("HPFS: bad mount options.\n");
+ return 1;
+ }
+ if (o == 2) {
+ hpfs_help();
+ return 1;
+ }
+ if (timeshift != s->s_hpfs_timeshift) {
+ printk("HPFS: timeshift can't be changed using remount.\n");
+ return 1;
+ }
+
+ unmark_dirty(s);
+
+ s->s_hpfs_uid = uid; s->s_hpfs_gid = gid;
+ s->s_hpfs_mode = 0777 & ~umask;
+ s->s_hpfs_lowercase = lowercase; s->s_hpfs_conv = conv;
+ s->s_hpfs_eas = eas; s->s_hpfs_chk = chk; s->s_hpfs_chkdsk = chkdsk;
+ s->s_hpfs_err = errs; s->s_hpfs_timeshift = timeshift;
+
+ if (!(*flags & MS_RDONLY)) mark_dirty(s);
+
+ return 0;
+}
+
+struct super_block *hpfs_read_super(struct super_block *s, void *options,
+ int silent)
+{
+ kdev_t dev;
+ struct buffer_head *bh0, *bh1, *bh2;
+ struct hpfs_boot_block *bootblock;
+ struct hpfs_super_block *superblock;
+ struct hpfs_spare_block *spareblock;
+
+ uid_t uid;
+ gid_t gid;
+ umode_t umask;
+ int lowercase, conv, eas, chk, errs, chkdsk, timeshift;
+
+ dnode_secno root_dno;
+ struct hpfs_dirent *de = NULL;
+ struct quad_buffer_head qbh;
+
+ int o;
+
+ MOD_INC_USE_COUNT;
+
+ s->s_hpfs_bmp_dir = NULL;
+ s->s_hpfs_cp_table = NULL;
+
+ s->s_hpfs_creation_de_lock = s->s_hpfs_rd_inode = 0;
+ init_waitqueue_head(&s->s_hpfs_creation_de);
+ init_waitqueue_head(&s->s_hpfs_iget_q);
+
+ uid = current->uid;
+ gid = current->gid;
+ umask = current->fs->umask;
+ lowercase = 0;
+ conv = CONV_BINARY;
+ eas = 2;
+ chk = 1;
+ errs = 1;
+ chkdsk = 1;
+ timeshift = 0;
+
+ if (!(o = parse_opts(options, &uid, &gid, &umask, &lowercase, &conv,
+ &eas, &chk, &errs, &chkdsk, &timeshift))) {
+ printk("HPFS: bad mount options.\n");
+ goto bail0;
+ }
+ if (o==2) {
+ hpfs_help();
+ goto bail0;
+ }
+
+ /*s->s_hpfs_mounting = 1;*/
+ lock_super(s);
+ dev = s->s_dev;
+ set_blocksize(dev, 512);
+ s->s_hpfs_fs_size = -1;
+ if (!(bootblock = hpfs_map_sector(s, 0, &bh0, 0))) goto bail1;
+ if (!(superblock = hpfs_map_sector(s, 16, &bh1, 1))) goto bail2;
+ if (!(spareblock = hpfs_map_sector(s, 17, &bh2, 0))) goto bail3;
+
+ /* Check magics */
+ if (/*bootblock->magic != BB_MAGIC
+ ||*/ superblock->magic != SB_MAGIC
+ || spareblock->magic != SP_MAGIC) {
+ if (!silent) printk("HPFS: Bad magic ... probably not HPFS\n");
+ goto bail4;
+ }
+
+ /* Check version */
+ if (!(s->s_flags & MS_RDONLY) &&
+ superblock->funcversion != 2 && superblock->funcversion != 3) {
+ printk("HPFS: Bad version %d,%d. Mount readonly to go around\n",
+ (int)superblock->version, (int)superblock->funcversion);
+ printk("HPFS: please try recent version of HPFS driver at http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi and if it still can't understand this format, contact author - mikulas@artax.karlin.mff.cuni.cz\n");
+ goto bail4;
+ }
+
+ s->s_flags |= MS_NOATIME;
+
+ /* Fill superblock stuff */
+ s->s_magic = HPFS_SUPER_MAGIC;
+ s->s_blocksize = 512;
+ s->s_blocksize_bits = 9;
+ s->s_op = (struct super_operations *) &hpfs_sops;
+
+ s->s_hpfs_root = superblock->root;
+ s->s_hpfs_fs_size = superblock->n_sectors;
+ s->s_hpfs_bitmaps = superblock->bitmaps;
+ s->s_hpfs_dirband_start = superblock->dir_band_start;
+ s->s_hpfs_dirband_size = superblock->n_dir_band;
+ s->s_hpfs_dmap = superblock->dir_band_bitmap;
+ s->s_hpfs_uid = uid;
+ s->s_hpfs_gid = gid;
+ s->s_hpfs_mode = 0777 & ~umask;
+ s->s_hpfs_n_free = -1;
+ s->s_hpfs_n_free_dnodes = -1;
+ s->s_hpfs_lowercase = lowercase;
+ s->s_hpfs_conv = conv;
+ s->s_hpfs_eas = eas;
+ s->s_hpfs_chk = chk;
+ s->s_hpfs_chkdsk = chkdsk;
+ s->s_hpfs_err = errs;
+ s->s_hpfs_timeshift = timeshift;
+ s->s_hpfs_was_error = 0;
+ s->s_hpfs_cp_table = NULL;
+ s->s_hpfs_c_bitmap = -1;
+
+ /* Load bitmap directory */
+ if (!(s->s_hpfs_bmp_dir = hpfs_load_bitmap_directory(s, superblock->bitmaps)))
+ goto bail4;
+
+ /* Check for general fs errors*/
+ if (spareblock->dirty && !spareblock->old_wrote) {
+ if (errs == 2) {
+ printk("HPFS: Improperly stopped, not mounted\n");
+ goto bail4;
+ }
+ hpfs_error(s, "improperly stopped");
+ }
+
+ if (!(s->s_flags & MS_RDONLY)) {
+ spareblock->dirty = 1;
+ spareblock->old_wrote = 0;
+ mark_buffer_dirty(bh2, 1);
+ }
+
+ if (spareblock->hotfixes_used || spareblock->n_spares_used) {
+ if (errs >= 2) {
+ printk("HPFS: Hotfixes not supported here, try chkdsk\n");
+ mark_dirty(s);
+ goto bail4;
+ }
+ hpfs_error(s, "hotfixes not supported here, try chkdsk");
+ if (errs == 0) printk("HPFS: Proceeding, but your filesystem will be probably corrupted by this driver...\n");
+ else printk("HPFS: This driver may read bad files or crash when operating on disk with hotfixes.\n");
+ }
+ if (spareblock->n_dnode_spares != spareblock->n_dnode_spares_free) {
+ if (errs >= 2) {
+ printk("HPFS: Spare dnodes used, try chkdsk\n");
+ mark_dirty(s);
+ goto bail4;
+ }
+ hpfs_error(s, "warning: spare dnodes used, try chkdsk");
+ if (errs == 0) printk("HPFS: Proceeding, but your filesystem could be corrupted if you delete files or directories\n");
+ }
+ if (chk) {
+ unsigned a;
+ if (superblock->dir_band_end - superblock->dir_band_start + 1 != superblock->n_dir_band ||
+ superblock->dir_band_end < superblock->dir_band_start || superblock->n_dir_band > 0x4000) {
+ hpfs_error(s, "dir band size mismatch: dir_band_start==%08x, dir_band_end==%08x, n_dir_band==%08x",
+ superblock->dir_band_start, superblock->dir_band_end, superblock->n_dir_band);
+ goto bail4;
+ }
+ a = s->s_hpfs_dirband_size;
+ s->s_hpfs_dirband_size = 0;
+ if (hpfs_chk_sectors(s, superblock->dir_band_start, superblock->n_dir_band, "dir_band") ||
+ hpfs_chk_sectors(s, superblock->dir_band_bitmap, 4, "dir_band_bitmap") ||
+ hpfs_chk_sectors(s, superblock->bitmaps, 4, "bitmaps")) {
+ mark_dirty(s);
+ goto bail4;
+ }
+ s->s_hpfs_dirband_size = a;
+ } else printk("HPFS: You really don't want any checks? You are crazy...\n");
+
+ /* Load code page table */
+ if (spareblock->n_code_pages)
+ if (!(s->s_hpfs_cp_table = hpfs_load_code_page(s, spareblock->code_page_dir)))
+ printk("HPFS: Warning: code page support is disabled\n");
+
+ brelse(bh2);
+ brelse(bh1);
+ brelse(bh0);
+
+ hpfs_lock_iget(s, 1);
+ s->s_root = d_alloc_root(iget(s, s->s_hpfs_root));
+ hpfs_unlock_iget(s);
+ unlock_super(s);
+ if (!s->s_root || !s->s_root->d_inode) {
+ printk("HPFS: iget failed. Why???\n");
+ goto bail0;
+ }
+ hpfs_set_dentry_operations(s->s_root);
+
+ /*
+ * find the root directory's . pointer & finish filling in the inode
+ */
+
+ root_dno = hpfs_fnode_dno(s, s->s_hpfs_root);
+ if (root_dno)
+ de = map_dirent(s->s_root->d_inode, root_dno, "\001\001", 2, NULL, &qbh);
+ if (!root_dno || !de) hpfs_error(s, "unable to find root dir");
+ else {
+ s->s_root->d_inode->i_atime = local_to_gmt(s, de->read_date);
+ s->s_root->d_inode->i_mtime = local_to_gmt(s, de->write_date);
+ s->s_root->d_inode->i_ctime = local_to_gmt(s, de->creation_date);
+ s->s_root->d_inode->i_hpfs_ea_size = de->ea_size;
+ s->s_root->d_inode->i_hpfs_parent_dir = s->s_root->d_inode->i_ino;
+ if (s->s_root->d_inode->i_size == -1) s->s_root->d_inode->i_size = 2048;
+ if (s->s_root->d_inode->i_blocks == -1) s->s_root->d_inode->i_blocks = 5;
+ }
+ if (de) hpfs_brelse4(&qbh);
+
+ return s;
+
+bail4: brelse(bh2);
+bail3: brelse(bh1);
+bail2: brelse(bh0);
+bail1: unlock_super(s);
+bail0: s->s_dev = 0;
+ if (s->s_hpfs_bmp_dir) kfree(s->s_hpfs_bmp_dir);
+ if (s->s_hpfs_cp_table) kfree(s->s_hpfs_cp_table);
+ MOD_DEC_USE_COUNT;
+ return NULL;
+}
+
+struct file_system_type hpfs_fs_type = {
+ "hpfs", FS_REQUIRES_DEV, hpfs_read_super, NULL
+};
+
+int init_hpfs_fs(void)
+{
+ return register_filesystem(&hpfs_fs_type);
+}
+
+#ifdef MODULE
+
+/*int register_symtab_from(struct symbol_table *, long *);*/
+
+int init_module(void)
+{
+ /*int status;
+ if (!(status = init_hpfs_fs())) register_symtab(NULL);
+ return status;*/
+ return init_hpfs_fs();
+}
+
+void cleanup_module(void)
+{
+ unregister_filesystem(&hpfs_fs_type);
+}
+
+#endif
diff --git a/fs/proc/sysvipc.c b/fs/proc/sysvipc.c
new file mode 100644
index 000000000..eab3e3186
--- /dev/null
+++ b/fs/proc/sysvipc.c
@@ -0,0 +1,138 @@
+/*
+ * linux/fs/proc/sysvipc.c
+ *
+ * Copyright (c) 1999 Dragos Acostachioaie
+ *
+ * This code is derived from linux/fs/proc/generic.c,
+ * which is Copyright (C) 1991, 1992 Linus Torvalds.
+ *
+ * /proc/sysvipc directory handling functions
+ */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+
+#include <asm/uaccess.h>
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/* 4K page size but our output routines use some slack for overruns */
+#define PROC_BLOCK_SIZE (3*1024)
+
+static ssize_t
+proc_sysvipc_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
+{
+ struct inode * inode = file->f_dentry->d_inode;
+ char *page;
+ ssize_t retval=0;
+ int eof=0;
+ ssize_t n, count;
+ char *start;
+ struct proc_dir_entry * dp;
+
+ dp = (struct proc_dir_entry *) inode->u.generic_ip;
+ if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+ return -ENOMEM;
+
+ while ((nbytes > 0) && !eof)
+ {
+ count = MIN(PROC_BLOCK_SIZE, nbytes);
+
+ start = NULL;
+ if (dp->get_info) {
+ /*
+ * Handle backwards compatibility with the old net
+ * routines.
+ *
+ * XXX What gives with the file->f_flags & O_ACCMODE
+ * test? Seems stupid to me....
+ */
+ n = dp->get_info(page, &start, *ppos, count,
+ (file->f_flags & O_ACCMODE) == O_RDWR);
+ if (n < count)
+ eof = 1;
+ } else if (dp->read_proc) {
+ n = dp->read_proc(page, &start, *ppos,
+ count, &eof, dp->data);
+ } else
+ break;
+
+ if (!start) {
+ /*
+ * For proc files that are less than 4k
+ */
+ start = page + *ppos;
+ n -= *ppos;
+ if (n <= 0)
+ break;
+ if (n > count)
+ n = count;
+ }
+ if (n == 0)
+ break; /* End of file */
+ if (n < 0) {
+ if (retval == 0)
+ retval = n;
+ break;
+ }
+
+ /* This is a hack to allow mangling of file pos independent
+ * of actual bytes read. Simply place the data at page,
+ * return the bytes, and set `start' to the desired offset
+ * as an unsigned int. - Paul.Russell@rustcorp.com.au
+ */
+ n -= copy_to_user(buf, start < page ? page : start, n);
+ if (n == 0) {
+ if (retval == 0)
+ retval = -EFAULT;
+ break;
+ }
+
+ *ppos += start < page ? (long)start : n; /* Move down the file */
+ nbytes -= n;
+ buf += n;
+ retval += n;
+ }
+ free_page((unsigned long) page);
+ return retval;
+}
+
+static struct file_operations proc_sysvipc_operations = {
+ NULL, /* lseek */
+ proc_sysvipc_read, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* poll */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* can't fsync */
+};
+
+/*
+ * proc directories can do almost nothing..
+ */
+struct inode_operations proc_sysvipc_inode_operations = {
+ &proc_sysvipc_operations, /* default net file-ops */
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
diff --git a/include/asm-alpha/hdreg.h b/include/asm-alpha/hdreg.h
new file mode 100644
index 000000000..2d9454754
--- /dev/null
+++ b/include/asm-alpha/hdreg.h
@@ -0,0 +1,12 @@
+/*
+ * linux/include/asm-alpha/hdreg.h
+ *
+ * Copyright (C) 1994-1996 Linus Torvalds & authors
+ */
+
+#ifndef __ASMalpha_HDREG_H
+#define __ASMalpha_HDREG_H
+
+typedef unsigned short ide_ioreg_t;
+
+#endif /* __ASMalpha_HDREG_H */
diff --git a/include/asm-arm/hdreg.h b/include/asm-arm/hdreg.h
new file mode 100644
index 000000000..81bc05e16
--- /dev/null
+++ b/include/asm-arm/hdreg.h
@@ -0,0 +1,13 @@
+/*
+ * linux/include/asm-arm/hdreg.h
+ *
+ * Copyright (C) 1994-1996 Linus Torvalds & authors
+ */
+
+#ifndef __ASMARM_HDREG_H
+#define __ASMARM_HDREG_H
+
+typedef unsigned long ide_ioreg_t;
+
+#endif /* __ASMARM_HDREG_H */
+
diff --git a/include/asm-i386/hdreg.h b/include/asm-i386/hdreg.h
new file mode 100644
index 000000000..1ad5c0739
--- /dev/null
+++ b/include/asm-i386/hdreg.h
@@ -0,0 +1,12 @@
+/*
+ * linux/include/asm-i386/hdreg.h
+ *
+ * Copyright (C) 1994-1996 Linus Torvalds & authors
+ */
+
+#ifndef __ASMi386_HDREG_H
+#define __ASMi386_HDREG_H
+
+typedef unsigned short ide_ioreg_t;
+
+#endif /* __ASMi386_HDREG_H */
diff --git a/include/asm-i386/page_offset.h b/include/asm-i386/page_offset.h
new file mode 100644
index 000000000..bb3a64dc1
--- /dev/null
+++ b/include/asm-i386/page_offset.h
@@ -0,0 +1,8 @@
+#include <linux/config.h>
+#ifdef CONFIG_1GB
+#define PAGE_OFFSET_RAW 0xC0000000
+#elif defined(CONFIG_2GB)
+#define PAGE_OFFSET_RAW 0x80000000
+#elif defined(CONFIG_3GB)
+#define PAGE_OFFSET_RAW 0x40000000
+#endif
diff --git a/include/asm-m68k/hdreg.h b/include/asm-m68k/hdreg.h
new file mode 100644
index 000000000..16249bd89
--- /dev/null
+++ b/include/asm-m68k/hdreg.h
@@ -0,0 +1,13 @@
+/*
+ * linux/include/asm-m68k/hdreg.h
+ *
+ * Copyright (C) 1994-1996 Linus Torvalds & authors
+ */
+
+#ifndef _M68K_HDREG_H
+#define _M68K_HDREG_H
+
+typedef unsigned int q40ide_ioreg_t;
+typedef unsigned char * ide_ioreg_t;
+
+#endif /* _M68K_HDREG_H */
diff --git a/include/asm-mips/hdreg.h b/include/asm-mips/hdreg.h
new file mode 100644
index 000000000..189dfc55c
--- /dev/null
+++ b/include/asm-mips/hdreg.h
@@ -0,0 +1,18 @@
+/* $Id: hdreg.h,v 1.4 1998/05/08 21:05:26 davem Exp $
+ *
+ * linux/include/asm-mips/hdreg.h
+ *
+ * Copyright (C) 1994-1996 Linus Torvalds & authors
+ */
+
+/*
+ * This file contains the MIPS architecture specific IDE code.
+ */
+
+#ifndef __ASM_MIPS_HDREG_H
+#define __ASM_MIPS_HDREG_H
+
+typedef unsigned short ide_ioreg_t;
+
+#endif /* __ASM_MIPS_HDREG_H */
+
diff --git a/include/asm-ppc/hdreg.h b/include/asm-ppc/hdreg.h
new file mode 100644
index 000000000..58123536b
--- /dev/null
+++ b/include/asm-ppc/hdreg.h
@@ -0,0 +1,17 @@
+/*
+ * linux/include/asm-ppc/hdreg.h
+ *
+ * Copyright (C) 1994-1996 Linus Torvalds & authors
+ */
+
+/*
+ * This file contains the ppc architecture specific IDE code.
+ */
+
+#ifndef __ASMPPC_HDREG_H
+#define __ASMPPC_HDREG_H
+
+typedef unsigned int ide_ioreg_t;
+
+#endif /* __ASMPPC_HDREG_H */
+
diff --git a/include/asm-sparc64/hdreg.h b/include/asm-sparc64/hdreg.h
new file mode 100644
index 000000000..29b7cfe6d
--- /dev/null
+++ b/include/asm-sparc64/hdreg.h
@@ -0,0 +1,13 @@
+/* $Id: hdreg.h,v 1.1 1999/05/14 07:23:13 davem Exp $
+ * hdreg.h: Ultra/PCI specific IDE glue.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
+ */
+
+#ifndef __SPARC64_HDREG_H
+#define __SPARC64_HDREG_H
+
+typedef unsigned long ide_ioreg_t;
+
+#endif /* __SPARC64_HDREG_H */
diff --git a/include/linux/blkpg.h b/include/linux/blkpg.h
new file mode 100644
index 000000000..2a5e4fb82
--- /dev/null
+++ b/include/linux/blkpg.h
@@ -0,0 +1,64 @@
+#ifndef _LINUX_BLKPG_H
+#define _LINUX_BLKPG_H
+
+/*
+ * Partition table and disk geometry handling
+ *
+ * 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/ioctl.h>
+
+#define BLKPG _IO(0x12,105)
+
+/* The argument structure */
+struct blkpg_ioctl_arg {
+ int op;
+ int flags;
+ int datalen;
+ void *data;
+};
+
+/* The subfunctions (for the op field) */
+#define BLKPG_ADD_PARTITION 1
+#define BLKPG_DEL_PARTITION 2
+
+/* Sizes of name fields. Unused at present. */
+#define BLKPG_DEVNAMELTH 64
+#define BLKPG_VOLNAMELTH 64
+
+/* The data structure for ADD_PARTITION and DEL_PARTITION */
+struct blkpg_partition {
+ long long start; /* starting offset in bytes */
+ long long length; /* length in bytes */
+ int pno; /* partition number */
+ char devname[BLKPG_DEVNAMELTH]; /* partition name, like sda5 or c0d1p2,
+ to be used in kernel messages */
+ char volname[BLKPG_VOLNAMELTH]; /* volume label */
+};
+
+#ifdef __KERNEL__
+
+extern char * partition_name(kdev_t dev);
+extern int blk_ioctl(kdev_t dev, unsigned int cmd, unsigned long arg);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_BLKPG_H */
diff --git a/include/linux/cyclomx.h b/include/linux/cyclomx.h
new file mode 100644
index 000000000..721056e32
--- /dev/null
+++ b/include/linux/cyclomx.h
@@ -0,0 +1,91 @@
+/*
+* cyclomx.h CYCLOM X Multiprotocol WAN Link Driver.
+* User-level API definitions.
+*
+* Author: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+*
+* Copyright: (c) 1998, 1999 Arnaldo Carvalho de Melo
+*
+* Based on wanpipe.h 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/19 acme wait_queue_head_t wait_stats(support for 2.3.*)
+* 1999/01/03 acme judicious use of data types
+* Dec 27, 1998 Arnaldo cleanup: PACKED not needed
+* Aug 08, 1998 Arnaldo Version 0.0.1
+*/
+#ifndef _CYCLOMX_H
+#define _CYCLOMX_H
+
+#include <linux/wanrouter.h>
+#include <asm/spinlock.h>
+
+#ifdef __KERNEL__
+/* Kernel Interface */
+
+#include <linux/cycx_drv.h> /* CYCLOM X support module API definitions */
+#include <linux/cycx_cfm.h> /* CYCLOM X firmware module definitions */
+#ifdef CONFIG_CYCLOMX_X25
+#include <linux/cycx_x25.h>
+#endif
+
+#ifndef min
+#define min(a,b) (((a)<(b))?(a):(b))
+#endif
+#ifndef max
+#define max(a,b) (((a)>(b))?(a):(b))
+#endif
+
+#define is_digit(ch) (((ch)>=(unsigned)'0'&&(ch)<=(unsigned)'9')?1:0)
+
+/* Adapter Data Space.
+ * This structure is needed because we handle multiple cards, otherwise
+ * static data would do it.
+ */
+typedef struct cycx {
+ char devname[WAN_DRVNAME_SZ+1]; /* card name */
+ cycxhw_t hw; /* hardware configuration */
+ wan_device_t wandev; /* WAN device data space */
+ u32 open_cnt; /* number of open interfaces */
+ u32 state_tick; /* link state timestamp */
+ spinlock_t lock;
+ char in_isr; /* interrupt-in-service flag */
+ char buff_int_mode_unbusy; /* flag for carrying out dev_tint */
+ u16 irq_dis_if_send_count; /* Disabling irqs in if_send*/
+#if LINUX_VERSION_CODE >= 0x020300
+ wait_queue_head_t wait_stats; /* to wait for the STATS indication */
+#else
+ struct wait_queue* wait_stats; /* to wait for the STATS indication */
+#endif
+ u32 mbox; /* -> mailbox */
+ void (*isr)(struct cycx* card); /* interrupt service routine */
+ int (*exec)(struct cycx* card, void* u_cmd, void* u_data);
+ union {
+#ifdef CONFIG_CYCLOMX_X25
+ struct { /* X.25 specific data */
+ u32 lo_pvc;
+ u32 hi_pvc;
+ u32 lo_svc;
+ u32 hi_svc;
+ TX25Stats stats;
+ unsigned critical; /* critical section flag */
+ u32 connection_keys;
+ } x;
+#endif
+ } u;
+} cycx_t;
+
+/* Public Functions */
+void cyclomx_open (cycx_t* card); /* cycx_main.c */
+void cyclomx_close (cycx_t* card); /* cycx_main.c */
+void cyclomx_set_state (cycx_t* card, int state); /* cycx_main.c */
+
+#ifdef CONFIG_CYCLOMX_X25
+int cyx_init (cycx_t* card, wandev_conf_t* conf); /* cycx_x25.c */
+#endif
+#endif /* __KERNEL__ */
+#endif /* _CYCLOMX_H */
diff --git a/include/linux/cycx_cfm.h b/include/linux/cycx_cfm.h
new file mode 100644
index 000000000..3ea78dc9e
--- /dev/null
+++ b/include/linux/cycx_cfm.h
@@ -0,0 +1,81 @@
+/*
+* cycx_cfm.h CYCLOM X Multiprotocol WAN Link Driver.
+* Definitions for the CYCLOM X Firmware Module (CFM).
+*
+* Author: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+* Copyright: (c) 1998, 1999 Arnaldo Carvalho de Melo
+*
+* Based on sdlasfm.h by Gene Kozin <74604.152@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.
+* ============================================================================
+* Aug 08, 1998 Arnaldo Initial version.
+*/
+#ifndef _CYCX_CFM_H
+#define _CYCX_CFM_H
+
+/* Defines */
+
+#define CFM_VERSION 2
+#define CFM_SIGNATURE "CFM - Cyclades CYCX Firmware Module"
+
+/* min/max */
+#define CFM_IMAGE_SIZE 0x20000 /* max size of CYCX code image file */
+#define CFM_DESCR_LEN 256 /* max length of description string */
+#define CFM_MAX_CYCX 1 /* max number of compatible adapters */
+#define CFM_LOAD_BUFSZ 0x400 /* buffer size for reset code (buffer_load) */
+
+/* Firmware Commands */
+#define GEN_POWER_ON 0x1280
+
+#define GEN_SET_SEG 0x1401 /* boot segment setting. */
+#define GEN_BOOT_DAT 0x1402 /* boot data. */
+#define GEN_START 0x1403 /* board start. */
+#define GEN_DEFPAR 0x1404 /* buffer length for boot. */
+
+/* Adapter types */
+#define CYCX_2X 2
+#define CYCX_8X 8
+#define CYCX_16X 16
+
+#define CFID_X25_2X 5200
+
+
+/* Data Types */
+
+typedef struct cfm_info /* firmware module information */
+{
+ unsigned short codeid; /* firmware ID */
+ unsigned short version; /* firmaware version number */
+ unsigned short adapter[CFM_MAX_CYCX]; /* compatible adapter types */
+ unsigned long memsize; /* minimum memory size */
+ unsigned short reserved[2]; /* reserved */
+ unsigned short startoffs; /* entry point offset */
+ unsigned short winoffs; /* dual-port memory window offset */
+ unsigned short codeoffs; /* code load offset */
+ unsigned long codesize; /* code size */
+ unsigned short dataoffs; /* configuration data load offset */
+ unsigned long datasize; /* configuration data size */
+} cfm_info_t;
+
+typedef struct cfm /* CYCX firmware file structire */
+{
+ char signature[80]; /* CFM file signature */
+ unsigned short version; /* file format version */
+ unsigned short checksum; /* info + image */
+ unsigned short reserved[6]; /* reserved */
+ char descr[CFM_DESCR_LEN]; /* description string */
+ cfm_info_t info; /* firmware module info */
+ unsigned char image[1]; /* code image (variable size) */
+} cfm_t;
+
+typedef struct cycx_header_s {
+ unsigned long reset_size;
+ unsigned long data_size;
+ unsigned long code_size;
+} cycx_header_t;
+
+#endif /* _CYCX_CFM_H */
diff --git a/include/linux/cycx_drv.h b/include/linux/cycx_drv.h
new file mode 100644
index 000000000..3448b9066
--- /dev/null
+++ b/include/linux/cycx_drv.h
@@ -0,0 +1,66 @@
+/*
+* cycx_drv.h CYCX Support Module. Kernel API Definitions.
+*
+* Author: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+* Copyright: (c) 1998, 1999 Arnaldo Carvalho de Melo
+*
+* Based on sdladrv.h 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/01/03 acme more judicious use of data types...
+* uclong, ucchar, etc deleted, the u8, u16, u32
+* types are the portable way to go.
+* 1999/01/03 acme judicious use of data types... u16, u32, etc
+* Dec 26, 1998 Arnaldo FIXED_BUFFERS, CONF_OFFSET,
+* removal of cy_read{bwl}
+* Aug 08, 1998 Arnaldo Initial version.
+*/
+#ifndef _CYCX_DRV_H
+#define _CYCX_DRV_H
+
+#define CYCX_WINDOWSIZE 0x4000 /* default dual-port memory window size */
+#define GEN_CYCX_INTR 0x02
+#define RST_ENABLE 0x04
+#define START_CPU 0x06
+#define RST_DISABLE 0x08
+#define FIXED_BUFFERS 0x08
+#define TEST_PATTERN 0xaa55
+#define CMD_OFFSET 0x20
+#define CONF_OFFSET 0x0380
+#define RESET_OFFSET 0x3c00 /* For reset file load */
+#define DATA_OFFSET 0x0100 /* For code and data files load */
+#define START_OFFSET 0x3ff0 /* 80186 starts here */
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/* Data Structures */
+/* Adapter hardware configuration. Pointer to this structure is passed to all
+ * APIs. */
+typedef struct cycxhw {
+ u32 type; /* adapter type */
+ u32 fwid; /* firmware ID */
+ int irq; /* interrupt request level */
+ u32 dpmbase; /* dual-port memory base */
+ u32 dpmsize; /* dual-port memory size */
+ u32 pclk; /* CPU clock rate, kHz */
+ u32 memory; /* memory size */
+ u32 reserved[5];
+} cycxhw_t;
+
+/* Function Prototypes */
+extern int cycx_setup (cycxhw_t* hw, void* sfm, u32 len);
+extern int cycx_down (cycxhw_t* hw);
+extern int cycx_inten (cycxhw_t* hw);
+extern int cycx_intde (cycxhw_t* hw);
+extern int cycx_intack (cycxhw_t* hw);
+extern int cycx_intr (cycxhw_t* hw);
+extern int cycx_peek (cycxhw_t* hw, u32 addr, void* buf, u32 len);
+extern int cycx_poke (cycxhw_t* hw, u32 addr, void* buf, u32 len);
+extern int cycx_exec (u32 addr);
+#endif /* _CYCX_DRV_H */
diff --git a/include/linux/cycx_x25.h b/include/linux/cycx_x25.h
new file mode 100644
index 000000000..e242e5795
--- /dev/null
+++ b/include/linux/cycx_x25.h
@@ -0,0 +1,103 @@
+/*
+* cycx_x25.h Cyclom X.25 firmware API definitions.
+*
+* Author: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+*
+* Copyright: (c) 1998, 1999 Arnaldo Carvalho de Melo
+*
+* Based on sdla_x25.h by Gene Kozin <74604.152@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/01/03 acme judicious use of data types
+*
+* 1999/01/02 acme #define X25_ACK_N3 0x4411
+* Dec 28, 1998 Arnaldo cleanup: lot'o'things removed
+* commands listed,
+* TX25Cmd & TX25Config structs
+* typedef'ed
+*/
+#ifndef _CYCX_X25_H
+#define _CYCX_X25_H
+
+#ifndef PACKED
+#define PACKED __attribute__((packed))
+#endif
+
+/* X.25 shared memory layout. */
+#define X25_MBOX_OFFS 0x300 /* general mailbox block */
+#define X25_RXMBOX_OFFS 0x340 /* receive mailbox */
+
+/* DATA STRUCTURES */
+/* X.25 Command Block. */
+typedef struct X25Cmd
+{
+ u16 command PACKED;
+ u16 link PACKED; /* values: 0 or 1 */
+ u16 len PACKED; /* values: 0 thru 0x205 (517) */
+ u32 buf PACKED;
+} TX25Cmd;
+
+/* Defines for the 'command' field. */
+#define X25_CONNECT_REQUEST 0x4401
+#define X25_CONNECT_RESPONSE 0x4402
+#define X25_DISCONNECT_REQUEST 0x4403
+#define X25_DISCONNECT_RESPONSE 0x4404
+#define X25_DATA_REQUEST 0x4405
+#define X25_ACK_TO_VC 0x4406
+#define X25_INTERRUPT_RESPONSE 0x4407
+#define X25_CONFIG 0x4408
+#define X25_CONNECT_INDICATION 0x4409
+#define X25_CONNECT_CONFIRM 0x440A
+#define X25_DISCONNECT_INDICATION 0x440B
+#define X25_DISCONNECT_CONFIRM 0x440C
+#define X25_DATA_INDICATION 0x440E
+#define X25_INTERRUPT_INDICATION 0x440F
+#define X25_ACK_FROM_VC 0x4410
+#define X25_ACK_N3 0x4411
+#define X25_CONNECT_COLLISION 0x4413
+#define X25_N3WIN 0x4414
+#define X25_LINE_ON 0x4415
+#define X25_LINE_OFF 0x4416
+#define X25_RESET_REQUEST 0x4417
+#define X25_LOG 0x4500
+#define X25_STATISTIC 0x4600
+#define X25_TRACE 0x4700
+#define X25_N2TRACEXC 0x4702
+#define X25_N3TRACEXC 0x4703
+
+typedef struct X25Config {
+ u8 link PACKED; /* link number */
+ u8 speed PACKED; /* line speed */
+ u8 clock PACKED; /* internal/external */
+ u8 n2 PACKED; /* # of level 2 retransm.(values: 1 thru FF) */
+ u8 n2win PACKED; /* level 2 window (values: 1 thru 7) */
+ u8 n3win PACKED; /* level 3 window (values: 1 thru 7) */
+ u8 nvc PACKED; /* # of logical channels (values: 1 thru 64) */
+ u8 pktlen PACKED; /* level 3 packet lenght - log base 2 of size */
+ u8 locaddr PACKED; /* my address */
+ u8 remaddr PACKED; /* remote address */
+ u16 t1 PACKED; /* time, in seconds */
+ u16 t2 PACKED; /* time, in seconds */
+ u8 t21 PACKED; /* time, in seconds */
+ u8 npvc PACKED; /* # of permanent virt. circuits (1 thru nvc) */
+ u8 t23 PACKED; /* time, in seconds */
+ u8 flags PACKED; /* see dosx25.doc, in portuguese, for details */
+} TX25Config;
+
+typedef struct X25Stats {
+ u16 rx_crc_errors PACKED;
+ u16 rx_over_errors PACKED;
+ u16 n2_tx_frames PACKED;
+ u16 n2_rx_frames PACKED;
+ u16 tx_timeouts PACKED;
+ u16 rx_timeouts PACKED;
+ u16 n3_tx_packets PACKED;
+ u16 n3_rx_packets PACKED;
+ u16 tx_aborts PACKED;
+ u16 rx_aborts PACKED;
+} TX25Stats;
+#endif /* _CYCX_X25_H */
diff --git a/include/linux/dn.h b/include/linux/dn.h
new file mode 100644
index 000000000..266dc0f25
--- /dev/null
+++ b/include/linux/dn.h
@@ -0,0 +1,161 @@
+#ifndef _LINUX_DN_H
+#define _LINUX_DN_H
+
+/*
+
+ DECnet Data Structures and Constants
+
+*/
+
+/*
+ * DNPROTO_NSP can't be the same as SOL_SOCKET,
+ * so increment each by one (compared to ULTRIX)
+ */
+#define DNPROTO_NSP 2 /* NSP protocol number */
+#define DNPROTO_ROU 3 /* Routing protocol number */
+#define DNPROTO_NML 4 /* Net mgt protocol number */
+#define DNPROTO_EVL 5 /* Evl protocol number (usr) */
+#define DNPROTO_EVR 6 /* Evl protocol number (evl) */
+#define DNPROTO_NSPT 7 /* NSP trace protocol number */
+
+
+#define DN_ADDL 2
+#define DN_MAXADDL 2 /* ULTRIX headers have 20 here, but pathworks has 2 */
+#define DN_MAXOPTL 16
+#define DN_MAXOBJL 16
+#define DN_MAXACCL 40
+#define DN_MAXALIASL 128
+#define DN_MAXNODEL 256
+#define DNBUFSIZE 65023
+
+/*
+ * SET/GET Socket options - must match the DSO_ numbers below
+ */
+#define SO_CONDATA 1
+#define SO_CONACCESS 2
+#define SO_PROXYUSR 3
+#define SO_LINKINFO 7
+
+#define DSO_CONDATA 1 /* Set/Get connect data */
+#define DSO_DISDATA 10 /* Set/Get disconnect data */
+#define DSO_CONACCESS 2 /* Set/Get connect access data */
+#define DSO_ACCEPTMODE 4 /* Set/Get accept mode */
+#define DSO_CONACCEPT 5 /* Accept deferred connection */
+#define DSO_CONREJECT 6 /* Reject deferred connection */
+#define DSO_LINKINFO 7 /* Set/Get link information */
+#define DSO_STREAM 8 /* Set socket type to stream */
+#define DSO_SEQPACKET 9 /* Set socket type to sequenced packet */
+#define DSO_MAX 10 /* Maximum option number */
+
+
+/* LINK States */
+#define LL_INACTIVE 0
+#define LL_CONNECTING 1
+#define LL_RUNNING 2
+#define LL_DISCONNECTING 3
+
+#define ACC_IMMED 0
+#define ACC_DEFER 1
+
+#define SDF_WILD 1 /* Wild card object */
+#define SDF_PROXY 2 /* Addr eligible for proxy */
+#define SDF_UICPROXY 4 /* Use uic-based proxy */
+
+/* Structures */
+
+
+struct dn_naddr
+{
+ unsigned short a_len;
+ unsigned char a_addr[DN_MAXADDL];
+};
+
+struct sockaddr_dn
+{
+ unsigned short sdn_family;
+ unsigned char sdn_flags;
+ unsigned char sdn_objnum;
+ unsigned short sdn_objnamel;
+ unsigned char sdn_objname[DN_MAXOBJL];
+ struct dn_naddr sdn_add;
+};
+#define sdn_nodeaddrl sdn_add.a_len /* Node address length */
+#define sdn_nodeaddr sdn_add.a_addr /* Node address */
+
+
+
+/*
+ * DECnet set/get DSO_CONDATA, DSO_DISDATA (optional data) structure
+ */
+struct optdata_dn {
+ unsigned short opt_status; /* Extended status return */
+#define opt_sts opt_status
+ unsigned short opt_optl; /* Length of user data */
+ unsigned char opt_data[16]; /* User data */
+};
+
+struct accessdata_dn
+{
+ unsigned char acc_accl;
+ unsigned char acc_acc[DN_MAXACCL];
+ unsigned char acc_passl;
+ unsigned char acc_pass[DN_MAXACCL];
+ unsigned char acc_userl;
+ unsigned char acc_user[DN_MAXACCL];
+};
+
+/*
+ * DECnet logical link information structure
+ */
+struct linkinfo_dn {
+ unsigned short idn_segsize; /* Segment size for link */
+ unsigned char idn_linkstate; /* Logical link state */
+};
+
+/*
+ * Ethernet address format (for DECnet)
+ */
+union etheraddress {
+ unsigned char dne_addr[6]; /* Full ethernet address */
+ struct {
+ unsigned char dne_hiord[4]; /* DECnet HIORD prefix */
+ unsigned char dne_nodeaddr[2]; /* DECnet node address */
+ } dne_remote;
+};
+
+
+/*
+ * DECnet physical socket address format
+ */
+struct dn_addr {
+ unsigned short dna_family; /* AF_DECnet */
+ union etheraddress dna_netaddr; /* DECnet ethernet address */
+};
+
+#define DECNET_IOCTL_BASE 0x89 /* PROTOPRIVATE range */
+
+#define SIOCSNETADDR _IOW(DECNET_IOCTL_BASE, 0xe0, struct dn_naddr)
+#define SIOCGNETADDR _IOR(DECNET_IOCTL_BASE, 0xe1, struct dn_naddr)
+#define OSIOCSNETADDR _IOW(DECNET_IOCTL_BASE, 0xe0, int)
+#define OSIOCGNETADDR _IOR(DECNET_IOCTL_BASE, 0xe1, int)
+
+/*
+ * An unofficial structure used to set/get routes.
+ * Be warned, this will probably change as the routing
+ * evolves. Also this is only for use with the ioctl()
+ * and the routing will use rtnetlink eventually.
+ */
+struct dn_fib_rtinfo {
+ unsigned long flags; /* Flags */
+#define DN_FIB_RTINFO_F_REPLACE 0x0001 /* Replace any existing route */
+#define DN_FIB_RTINFO_F_DEVCOST 0x0002 /* Add cost of device */
+ unsigned long timeout; /* Time in seconds route should last */
+ unsigned short src; /* Source Address, 0 = any */
+ unsigned short dst; /* Destination Address */
+ unsigned short nhp; /* Next Hop Address */
+ unsigned short hops; /* Hops on path */
+ unsigned short cost; /* Cost of path */
+ char device[16];
+};
+
+#endif /* _LINUX_DN_H */
diff --git a/include/linux/hdsmart.h b/include/linux/hdsmart.h
new file mode 100644
index 000000000..10bd7249d
--- /dev/null
+++ b/include/linux/hdsmart.h
@@ -0,0 +1,61 @@
+#ifndef _LINUX_HDSMART_H
+#define _LINUX_HDSMART_H
+
+/*
+ * This file contains some defines for the AT-hd-controller.
+ * Various sources.
+ */
+
+#define NR_ATTRIBUTES 30
+
+typedef struct threshold_s {
+ unsigned char id;
+ unsigned char threshold;
+ unsigned char reserved[10];
+} __attribute__ ((packed)) threshold_t;
+
+typedef struct thresholds_s {
+ unsigned short revision;
+ threshold_t thresholds[NR_ATTRIBUTES];
+ unsigned char reserved[18];
+ unsigned char vendor[131];
+ unsigned char checksum;
+} __attribute__ ((packed)) thresholds_t;
+
+typedef struct value_s {
+ unsigned char id;
+ unsigned short status;
+ unsigned char value;
+ unsigned char vendor[8];
+} __attribute__ ((packed)) value_t;
+
+typedef struct values_s {
+ unsigned short revision;
+ value_t values[NR_ATTRIBUTES];
+ unsigned char offline_status;
+ unsigned char vendor1;
+ unsigned short offline_timeout;
+ unsigned char vendor2;
+ unsigned char offline_capability;
+ unsigned short smart_capability;
+ unsigned char reserved[16];
+ unsigned char vendor[125];
+ unsigned char checksum;
+} __attribute__ ((packed)) values_t;
+
+#if !defined(__KERNEL__) || defined(_IDE_DISK_C)
+
+#define NR_OFFLINE_TEXTS 5
+struct {
+ unsigned char value;
+ char *text;
+} offline_status_text[NR_OFFLINE_TEXTS] = {
+ { 0x00, "NeverStarted" },
+ { 0x02, "Completed" },
+ { 0x04, "Suspended" },
+ { 0x05, "Aborted" },
+ { 0x06, "Failed" }
+};
+#endif /* !defined(__KERNEL__) || defined(_IDE_DISK_C) */
+
+#endif /* _LINUX_HDSMART_H */
diff --git a/include/linux/i2o.h b/include/linux/i2o.h
new file mode 100644
index 000000000..04079577e
--- /dev/null
+++ b/include/linux/i2o.h
@@ -0,0 +1,588 @@
+#ifndef _I2O_H
+#define _I2O_H
+
+/*
+ * Tunable parameters first
+ */
+
+/* How many different OSM's are we allowing */
+#define MAX_I2O_MODULES 64
+/* How many controllers are we allowing */
+#define MAX_I2O_CONTROLLERS 32
+
+
+#ifdef __KERNEL__ /* ioctl stuff only thing exported to users */
+
+/*
+ * I2O Interface Objects
+ */
+
+#include <linux/notifier.h>
+#include <asm/atomic.h>
+
+/*
+ * message structures
+ */
+
+#define TID_SZ 12
+#define FUNCTION_SZ 8
+
+struct i2o_message
+{
+ u32 version_size;
+ u32 function_addr;
+ u32 initiator_context;
+ /* List follows */
+};
+
+
+/*
+ * Each I2O device entity has one or more of these. There is one
+ * per device. *FIXME* how to handle multiple types on one unit.
+ */
+
+struct i2o_device
+{
+ int class; /* Block, Net, SCSI etc (from spec) */
+ int subclass; /* eth, fddi, tr etc (from spec) */
+ int id; /* I2O ID assigned by the controller */
+ int parent; /* Parent device */
+ int flags; /* Control flags */
+ int i2oversion; /* I2O version supported. Actually there
+ * should be high and low version */
+ struct proc_dir_entry* proc_entry; /* /proc dir */
+ struct i2o_driver *owner; /* Owning device */
+ struct i2o_controller *controller; /* Controlling IOP */
+ struct i2o_device *next; /* Chain */
+ char dev_name[8]; /* linux /dev name if available */
+};
+
+/*
+ * Resource data for each PCI I2O controller
+ */
+
+struct i2o_pci
+{
+ int irq;
+};
+
+/*
+ * Each I2O controller has one of these objects
+ */
+
+struct i2o_controller
+{
+ char name[16];
+ int unit;
+ int status; /* I2O status */
+ int i2oversion;
+ int type;
+#define I2O_TYPE_PCI 0x01 /* PCI I2O controller */
+ struct notifier_block *event_notifer; /* Events */
+ atomic_t users;
+ struct i2o_device *devices; /* I2O device chain */
+ struct i2o_controller *next; /* Controller chain */
+ volatile u32 *post_port; /* Messaging ports */
+ volatile u32 *reply_port;
+ volatile u32 *irq_mask; /* Interrupt port */
+ u32 mem_offset; /* MFA offset */
+ u32 mem_phys; /* MFA physical */
+ u32 priv_mem;
+ u32 priv_mem_size;
+ u32 priv_io;
+ u32 priv_io_size;
+
+ struct proc_dir_entry* proc_entry; /* /proc dir */
+
+ union
+ { /* Bus information */
+ struct i2o_pci pci;
+ } bus;
+ void (*destructor)(struct i2o_controller *); /* Bus specific destructor */
+ int (*bind)(struct i2o_controller *, struct i2o_device *); /* Bus specific attach/detach */
+ int (*unbind)(struct i2o_controller *, struct i2o_device *);
+ void *page_frame; /* Message buffers */
+ int inbound_size; /* Inbound queue size */
+};
+
+struct i2o_handler
+{
+ void (*reply)(struct i2o_handler *, struct i2o_controller *, struct i2o_message *);
+ char *name;
+ int context; /* Low 8 bits of the transaction info */
+ /* User data follows */
+};
+
+/*
+ * Messenger inlines
+ */
+
+extern inline u32 I2O_POST_READ32(struct i2o_controller *c)
+{
+ return *c->post_port;
+}
+
+extern inline void I2O_POST_WRITE32(struct i2o_controller *c, u32 Val)
+{
+ *c->post_port = Val;
+}
+
+
+extern inline u32 I2O_REPLY_READ32(struct i2o_controller *c)
+{
+ return *c->reply_port;
+}
+
+extern inline void I2O_REPLY_WRITE32(struct i2o_controller *c, u32 Val)
+{
+ *c->reply_port= Val;
+}
+
+
+extern inline u32 I2O_IRQ_READ32(struct i2o_controller *c)
+{
+ return *c->irq_mask;
+}
+
+extern inline void I2O_IRQ_WRITE32(struct i2o_controller *c, u32 Val)
+{
+ *c->irq_mask = Val;
+}
+
+
+extern inline void i2o_post_message(struct i2o_controller *c, u32 m)
+{
+ /* The second line isnt spurious - thats forcing PCI posting */
+ I2O_POST_WRITE32(c,m);
+ (void) I2O_IRQ_READ32(c);
+}
+
+extern inline void i2o_flush_reply(struct i2o_controller *c, u32 m)
+{
+ I2O_REPLY_WRITE32(c,m);
+}
+
+
+struct i2o_controller *i2o_controller_chain;
+
+extern int i2o_quiesce_controller(struct i2o_controller *);
+extern int i2o_clear_controller(struct i2o_controller *);
+extern int i2o_install_controller(struct i2o_controller *);
+extern int i2o_delete_controller(struct i2o_controller *);
+extern int i2o_activate_controller(struct i2o_controller *);
+extern void i2o_unlock_controller(struct i2o_controller *);
+extern struct i2o_controller *i2o_find_controller(int);
+extern int i2o_num_controllers;
+
+extern int i2o_install_handler(struct i2o_handler *);
+extern int i2o_remove_handler(struct i2o_handler *);
+
+extern int i2o_install_device(struct i2o_controller *, struct i2o_device *);
+extern int i2o_delete_device(struct i2o_device *);
+extern int i2o_claim_device(struct i2o_device *, struct i2o_driver *);
+extern int i2o_release_device(struct i2o_device *);
+
+extern int i2o_post_this(struct i2o_controller *, int, u32 *, int);
+extern int i2o_post_wait(struct i2o_controller *, int, u32 *, int, int *, int);
+extern int i2o_issue_claim(struct i2o_controller *, int, int, int, int *);
+extern int i2o_query_scalar(struct i2o_controller *, int, int, int, int, void *,
+ int, int *);
+extern int i2o_params_set(struct i2o_controller *c, int, int, int, int, void *,
+ int, int *);
+
+extern void i2o_run_queue(struct i2o_controller *);
+
+extern void i2o_report_status(const char *, const char *, u8, u8, u16);
+extern void report_common_status(u8);
+extern void report_lan_dsc(u16);
+
+extern u32 i2o_wait_message(struct i2o_controller *, char *);
+
+extern const char *i2o_get_class_name(int);
+
+
+/*
+ * I2O classes / subclasses
+ */
+
+/* Class ID and Code Assignments
+ * (LCT.ClassID.Version field)
+ */
+#define I2O_CLASS_VERSION_10 0x00
+#define I2O_CLASS_VERSION_11 0x01
+
+/* Class code names
+ * (from v1.5 Table 6-1 Class Code Assignments.)
+ */
+
+#define I2O_CLASS_EXECUTIVE 0x000
+#define I2O_CLASS_DDM 0x001
+#define I2O_CLASS_RANDOM_BLOCK_STORAGE 0x010
+#define I2O_CLASS_SEQUENTIAL_STORAGE 0x011
+#define I2O_CLASS_LAN 0x020
+#define I2O_CLASS_WAN 0x030
+#define I2O_CLASS_FIBRE_CHANNEL_PORT 0x040
+#define I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL 0x041
+#define I2O_CLASS_SCSI_PERIPHERAL 0x051
+#define I2O_CLASS_ATE_PORT 0x060
+#define I2O_CLASS_ATE_PERIPHERAL 0x061
+#define I2O_CLASS_FLOPPY_CONTROLLER 0x070
+#define I2O_CLASS_FLOPPY_DEVICE 0x071
+#define I2O_CLASS_BUS_ADAPTER_PORT 0x080
+#define I2O_CLASS_PEER_TRANSPORT_AGENT 0x090
+#define I2O_CLASS_PEER_TRANSPORT 0x091
+
+/* Rest of 0x092 - 0x09f reserved for peer-to-peer classes
+ */
+
+#define I2O_CLASS_MATCH_ANYCLASS 0xffffffff
+
+/* Subclasses
+ */
+
+#define I2O_SUBCLASS_i960 0x001
+#define I2O_SUBCLASS_HDM 0x020
+#define I2O_SUBCLASS_ISM 0x021
+
+/* Operation functions */
+
+#define I2O_PARAMS_FIELD_GET 0x0001
+#define I2O_PARAMS_LIST_GET 0x0002
+#define I2O_PARAMS_MORE_GET 0x0003
+#define I2O_PARAMS_SIZE_GET 0x0004
+#define I2O_PARAMS_TABLE_GET 0x0005
+#define I2O_PARAMS_FIELD_SET 0x0006
+#define I2O_PARAMS_LIST_SET 0x0007
+#define I2O_PARAMS_ROW_ADD 0x0008
+#define I2O_PARAMS_ROW_DELETE 0x0009
+#define I2O_PARAMS_TABLE_CLEAR 0x000A
+
+/*
+ * I2O serial number conventions / formats
+ * (circa v1.5)
+ */
+
+#define I2O_SNFORMAT_UNKNOWN 0
+#define I2O_SNFORMAT_BINARY 1
+#define I2O_SNFORMAT_ASCII 2
+#define I2O_SNFORMAT_UNICODE 3
+#define I2O_SNFORMAT_LAN48_MAC 4
+#define I2O_SNFORMAT_WAN 5
+
+/* Plus new in v2.0 (Yellowstone pdf doc)
+ */
+
+#define I2O_SNFORMAT_LAN64_MAC 6
+#define I2O_SNFORMAT_DDM 7
+#define I2O_SNFORMAT_IEEE_REG64 8
+#define I2O_SNFORMAT_IEEE_REG128 9
+#define I2O_SNFORMAT_UNKNOWN2 0xff
+
+
+/*
+ * "Special" TID assignments
+ */
+#define I2O_IOP_TID 0
+#define I2O_HOST_TID 1
+
+
+/* Transaction Reply Lists (TRL) Control Word structure */
+
+#define TRL_SINGLE_FIXED_LENGTH 0x00
+#define TRL_SINGLE_VARIABLE_LENGTH 0x40
+#define TRL_MULTIPLE_FIXED_LENGTH 0x80
+
+/* LAN Class specific functions */
+
+#define LAN_PACKET_SEND 0x3B
+#define LAN_SDU_SEND 0x3D
+#define LAN_RECEIVE_POST 0x3E
+#define LAN_RESET 0x35
+#define LAN_SUSPEND 0x37
+
+/*
+ * Messaging API values
+ */
+
+#define I2O_CMD_ADAPTER_ASSIGN 0xB3
+#define I2O_CMD_ADAPTER_READ 0xB2
+#define I2O_CMD_ADAPTER_RELEASE 0xB5
+#define I2O_CMD_BIOS_INFO_SET 0xA5
+#define I2O_CMD_BOOT_DEVICE_SET 0xA7
+#define I2O_CMD_CONFIG_VALIDATE 0xBB
+#define I2O_CMD_CONN_SETUP 0xCA
+#define I2O_CMD_DDM_DESTROY 0xB1
+#define I2O_CMD_DDM_ENABLE 0xD5
+#define I2O_CMD_DDM_QUIESCE 0xC7
+#define I2O_CMD_DDM_RESET 0xD9
+#define I2O_CMD_DDM_SUSPEND 0xAF
+#define I2O_CMD_DEVICE_ASSIGN 0xB7
+#define I2O_CMD_DEVICE_RELEASE 0xB9
+#define I2O_CMD_HRT_GET 0xA8
+#define I2O_CMD_ADAPTER_CLEAR 0xBE
+#define I2O_CMD_ADAPTER_CONNECT 0xC9
+#define I2O_CMD_ADAPTER_RESET 0xBD
+#define I2O_CMD_LCT_NOTIFY 0xA2
+#define I2O_CMD_OUTBOUND_INIT 0xA1
+#define I2O_CMD_PATH_ENABLE 0xD3
+#define I2O_CMD_PATH_QUIESCE 0xC5
+#define I2O_CMD_PATH_RESET 0xD7
+#define I2O_CMD_STATIC_MF_CREATE 0xDD
+#define I2O_CMD_STATIC_MF_RELEASE 0xDF
+#define I2O_CMD_STATUS_GET 0xA0
+#define I2O_CMD_SW_DOWNLOAD 0xA9
+#define I2O_CMD_SW_UPLOAD 0xAB
+#define I2O_CMD_SW_REMOVE 0xAD
+#define I2O_CMD_SYS_ENABLE 0xD1
+#define I2O_CMD_SYS_MODIFY 0xC1
+#define I2O_CMD_SYS_QUIESCE 0xC3
+#define I2O_CMD_SYS_TAB_SET 0xA3
+
+#define I2O_CMD_UTIL_NOP 0x00
+#define I2O_CMD_UTIL_ABORT 0x01
+#define I2O_CMD_UTIL_CLAIM 0x09
+#define I2O_CMD_UTIL_RELEASE 0x0B
+#define I2O_CMD_UTIL_PARAMS_GET 0x06
+#define I2O_CMD_UTIL_PARAMS_SET 0x05
+#define I2O_CMD_UTIL_EVT_REGISTER 0x13
+#define I2O_CMD_UTIL_ACK 0x14
+#define I2O_CMD_UTIL_CONFIG_DIALOG 0x10
+#define I2O_CMD_UTIL_DEVICE_RESERVE 0x0D
+#define I2O_CMD_UTIL_DEVICE_RELEASE 0x0F
+#define I2O_CMD_UTIL_LOCK 0x17
+#define I2O_CMD_UTIL_LOCK_RELEASE 0x19
+#define I2O_CMD_UTIL_REPLY_FAULT_NOTIFY 0x15
+
+#define I2O_CMD_SCSI_EXEC 0x81
+#define I2O_CMD_SCSI_ABORT 0x83
+#define I2O_CMD_SCSI_BUSRESET 0x27
+
+#define I2O_CMD_BLOCK_READ 0x30
+#define I2O_CMD_BLOCK_WRITE 0x31
+#define I2O_CMD_BLOCK_CFLUSH 0x37
+#define I2O_CMD_BLOCK_MLOCK 0x49
+#define I2O_CMD_BLOCK_MUNLOCK 0x4B
+#define I2O_CMD_BLOCK_MMOUNT 0x41
+#define I2O_CMD_BLOCK_MEJECT 0x43
+
+#define I2O_PRIVATE_MSG 0xFF
+
+/*
+ * Init Outbound Q status
+ */
+
+#define I2O_CMD_OUTBOUND_INIT_IN_PROGRESS 0x01
+#define I2O_CMD_OUTBOUND_INIT_REJECTED 0x02
+#define I2O_CMD_OUTBOUND_INIT_FAILED 0x03
+#define I2O_CMD_OUTBOUND_INIT_COMPLETE 0x04
+
+/*
+ * I2O Get Status State values
+ */
+
+#define ADAPTER_STATE_INITIALIZING 0x01
+#define ADAPTER_STATE_RESET 0x02
+#define ADAPTER_STATE_HOLD 0x04
+#define ADAPTER_STATE_READY 0x05
+#define ADAPTER_STATE_OPERATIONAL 0x08
+#define ADAPTER_STATE_FAILED 0x10
+#define ADAPTER_STATE_FAULTED 0x11
+
+/* I2O API function return values */
+
+#define I2O_RTN_NO_ERROR 0
+#define I2O_RTN_NOT_INIT 1
+#define I2O_RTN_FREE_Q_EMPTY 2
+#define I2O_RTN_TCB_ERROR 3
+#define I2O_RTN_TRANSACTION_ERROR 4
+#define I2O_RTN_ADAPTER_ALREADY_INIT 5
+#define I2O_RTN_MALLOC_ERROR 6
+#define I2O_RTN_ADPTR_NOT_REGISTERED 7
+#define I2O_RTN_MSG_REPLY_TIMEOUT 8
+#define I2O_RTN_NO_STATUS 9
+#define I2O_RTN_NO_FIRM_VER 10
+#define I2O_RTN_NO_LINK_SPEED 11
+
+/* Reply message status defines for all messages */
+
+#define I2O_REPLY_STATUS_SUCCESS 0x00
+#define I2O_REPLY_STATUS_ABORT_DIRTY 0x01
+#define I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER 0x02
+#define I2O_REPLY_STATUS_ABORT_PARTIAL_TRANSFER 0x03
+#define I2O_REPLY_STATUS_ERROR_DIRTY 0x04
+#define I2O_REPLY_STATUS_ERROR_NO_DATA_TRANSFER 0x05
+#define I2O_REPLY_STATUS_ERROR_PARTIAL_TRANSFER 0x06
+#define I2O_REPLY_STATUS_PROCESS_ABORT_DIRTY 0x08
+#define I2O_REPLY_STATUS_PROCESS_ABORT_NO_DATA_TRANSFER 0x09
+#define I2O_REPLY_STATUS_PROCESS_ABORT_PARTIAL_TRANSFER 0x0A
+#define I2O_REPLY_STATUS_TRANSACTION_ERROR 0x0B
+#define I2O_REPLY_STATUS_PROGRESS_REPORT 0x80
+
+/* Status codes and Error Information for Parameter functions */
+
+#define I2O_PARAMS_STATUS_SUCCESS 0x00
+#define I2O_PARAMS_STATUS_BAD_KEY_ABORT 0x01
+#define I2O_PARAMS_STATUS_BAD_KEY_CONTINUE 0x02
+#define I2O_PARAMS_STATUS_BUFFER_FULL 0x03
+#define I2O_PARAMS_STATUS_BUFFER_TOO_SMALL 0x04
+#define I2O_PARAMS_STATUS_FIELD_UNREADABLE 0x05
+#define I2O_PARAMS_STATUS_FIELD_UNWRITEABLE 0x06
+#define I2O_PARAMS_STATUS_INSUFFICIENT_FIELDS 0x07
+#define I2O_PARAMS_STATUS_INVALID_GROUP_ID 0x08
+#define I2O_PARAMS_STATUS_INVALID_OPERATION 0x09
+#define I2O_PARAMS_STATUS_NO_KEY_FIELD 0x0A
+#define I2O_PARAMS_STATUS_NO_SUCH_FIELD 0x0B
+#define I2O_PARAMS_STATUS_NON_DYNAMIC_GROUP 0x0C
+#define I2O_PARAMS_STATUS_OPERATION_ERROR 0x0D
+#define I2O_PARAMS_STATUS_SCALAR_ERROR 0x0E
+#define I2O_PARAMS_STATUS_TABLE_ERROR 0x0F
+#define I2O_PARAMS_STATUS_WRONG_GROUP_TYPE 0x10
+
+/* DetailedStatusCode defines for Executive, DDM, Util and Transaction error
+ * messages: Table 3-2 Detailed Status Codes.*/
+
+#define I2O_DSC_SUCCESS 0x0000
+#define I2O_DSC_BAD_KEY 0x0002
+#define I2O_DSC_TCL_ERROR 0x0003
+#define I2O_DSC_REPLY_BUFFER_FULL 0x0004
+#define I2O_DSC_NO_SUCH_PAGE 0x0005
+#define I2O_DSC_INSUFFICIENT_RESOURCE_SOFT 0x0006
+#define I2O_DSC_INSUFFICIENT_RESOURCE_HARD 0x0007
+#define I2O_DSC_CHAIN_BUFFER_TOO_LARGE 0x0009
+#define I2O_DSC_UNSUPPORTED_FUNCTION 0x000A
+#define I2O_DSC_DEVICE_LOCKED 0x000B
+#define I2O_DSC_DEVICE_RESET 0x000C
+#define I2O_DSC_INAPPROPRIATE_FUNCTION 0x000D
+#define I2O_DSC_INVALID_INITIATOR_ADDRESS 0x000E
+#define I2O_DSC_INVALID_MESSAGE_FLAGS 0x000F
+#define I2O_DSC_INVALID_OFFSET 0x0010
+#define I2O_DSC_INVALID_PARAMETER 0x0011
+#define I2O_DSC_INVALID_REQUEST 0x0012
+#define I2O_DSC_INVALID_TARGET_ADDRESS 0x0013
+#define I2O_DSC_MESSAGE_TOO_LARGE 0x0014
+#define I2O_DSC_MESSAGE_TOO_SMALL 0x0015
+#define I2O_DSC_MISSING_PARAMETER 0x0016
+#define I2O_DSC_TIMEOUT 0x0017
+#define I2O_DSC_UNKNOWN_ERROR 0x0018
+#define I2O_DSC_UNKNOWN_FUNCTION 0x0019
+#define I2O_DSC_UNSUPPORTED_VERSION 0x001A
+#define I2O_DSC_DEVICE_BUSY 0x001B
+#define I2O_DSC_DEVICE_NOT_AVAILABLE 0x001C
+
+/* Message header defines for VersionOffset */
+#define I2OVER15 0x0001
+#define I2OVER20 0x0002
+/* Default is 1.5, FIXME: Need support for both 1.5 and 2.0 */
+#define I2OVERSION I2OVER15
+#define SGL_OFFSET_0 I2OVERSION
+#define SGL_OFFSET_4 (0x0040 | I2OVERSION)
+#define SGL_OFFSET_5 (0x0050 | I2OVERSION)
+#define SGL_OFFSET_6 (0x0060 | I2OVERSION)
+#define SGL_OFFSET_8 (0x0080 | I2OVERSION)
+#define SGL_OFFSET_10 (0x00A0 | I2OVERSION)
+
+#define TRL_OFFSET_5 (0x0050 | I2OVERSION)
+#define TRL_OFFSET_6 (0x0060 | I2OVERSION)
+
+ /* msg header defines for MsgFlags */
+#define MSG_STATIC 0x0100
+#define MSG_64BIT_CNTXT 0x0200
+#define MSG_MULTI_TRANS 0x1000
+#define MSG_FAIL 0x2000
+#define MSG_LAST 0x4000
+#define MSG_REPLY 0x8000
+
+ /* normal LAN request message MsgFlags and VersionOffset (0x1041) */
+#define LAN_MSG_REQST (MSG_MULTI_TRANS | SGL_OFFSET_4)
+
+ /* minimum size msg */
+#define THREE_WORD_MSG_SIZE 0x00030000
+#define FOUR_WORD_MSG_SIZE 0x00040000
+#define FIVE_WORD_MSG_SIZE 0x00050000
+#define SIX_WORD_MSG_SIZE 0x00060000
+#define SEVEN_WORD_MSG_SIZE 0x00070000
+#define EIGHT_WORD_MSG_SIZE 0x00080000
+#define NINE_WORD_MSG_SIZE 0x00090000
+#define TEN_WORD_MSG_SIZE 0x000A0000
+#define I2O_MESSAGE_SIZE(x) ((x)<<16)
+
+
+/* Special TID Assignments */
+
+#define ADAPTER_TID 0
+#define HOST_TID 1
+
+#define MSG_FRAME_SIZE 128
+#define NMBR_MSG_FRAMES 128
+
+#define MSG_POOL_SIZE 16384
+
+#define I2O_POST_WAIT_OK 1
+#define I2O_POST_WAIT_TIMEOUT -1
+
+#endif /* __KERNEL__ */
+
+#include <asm/ioctl.h>
+
+/*
+ * I2O Control IOCTLs and structures
+ */
+#define I2O_MAGIC_NUMBER 'i'
+#define I2OGETIOPS _IO(I2O_MAGIC_NUMBER,0)
+#define I2OHRTGET _IO(I2O_MAGIC_NUMBER,1)
+#define I2OLCTGET _IO(I2O_MAGIC_NUMBER,2)
+#define I2OPARMSET _IO(I2O_MAGIC_NUMBER,3)
+#define I2OPARMGET _IO(I2O_MAGIC_NUMBER,4)
+#define I2OSWDL _IO(I2O_MAGIC_NUMBER,5)
+#define I2OSWUL _IO(I2O_MAGIC_NUMBER,6)
+#define I2OSWDEL _IO(I2O_MAGIC_NUMBER,7)
+#define I2OHTML _IO(I2O_MAGIC_NUMBER,8)
+
+/* On hold until we figure this out
+#define I2OEVTREG _IO(I2O_MAGIC_NUMBER,9)
+#define I2OEVTCLR _IO(I2O_MAGIC_NUMBER,10)
+#define I2OEVTGET _IO(I2O_MAGIC_NUMBER,11)
+ */
+
+struct i2o_cmd_hrtlct
+{
+ unsigned int iop; /* IOP unit number */
+ void *resbuf; /* Buffer for result */
+ unsigned int *reslen; /* Buffer length in bytes */
+};
+
+
+struct i2o_cmd_psetget
+{
+ unsigned int iop; /* IOP unit number */
+ unsigned int tid; /* Target device TID */
+ void *opbuf; /* Operation List buffer */
+ unsigned int oplen; /* Operation List buffer length in bytes */
+ void *resbuf; /* Result List buffer */
+ unsigned int *reslen; /* Result List buffer length in bytes */
+};
+
+struct i2o_sw_xfer
+{
+ unsigned int iop; /* IOP unit number */
+ unsigned char dl_flags; /* DownLoadFlags field */
+ unsigned char sw_type; /* Software type */
+ unsigned int sw_id; /* Software ID */
+ void *buf; /* Pointer to software buffer */
+ unsigned int *swlen; /* Length of software data */
+ unsigned int *maxfrag; /* Maximum fragment count */
+ unsigned int *curfrag; /* Current fragment count */
+};
+
+struct i2o_html
+{
+ unsigned int iop; /* IOP unit number */
+ unsigned int tid; /* Target device ID */
+ unsigned int page; /* HTML page */
+ void *resbuf; /* Buffer for reply HTML page */
+ unsigned int *reslen; /* Length in bytes of reply buffer */
+ void *qbuf; /* Pointer to HTTP query string */
+ unsigned int qlen; /* Length in bytes of query string buffer */
+};
+
+#endif
diff --git a/include/linux/ide.h b/include/linux/ide.h
new file mode 100644
index 000000000..c0b5ed929
--- /dev/null
+++ b/include/linux/ide.h
@@ -0,0 +1,813 @@
+#ifndef _IDE_H
+#define _IDE_H
+/*
+ * linux/drivers/block/ide.h
+ *
+ * Copyright (C) 1994-1998 Linus Torvalds & authors
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/hdreg.h>
+#include <linux/hdsmart.h>
+#include <linux/blkdev.h>
+#include <linux/proc_fs.h>
+#include <asm/hdreg.h>
+
+/*
+ * This is the multiple IDE interface driver, as evolved from hd.c.
+ * It supports up to four IDE interfaces, on one or more IRQs (usually 14 & 15).
+ * There can be up to two drives per interface, as per the ATA-2 spec.
+ *
+ * Primary i/f: ide0: major=3; (hda) minor=0; (hdb) minor=64
+ * Secondary i/f: ide1: major=22; (hdc or hd1a) minor=0; (hdd or hd1b) minor=64
+ * Tertiary i/f: ide2: major=33; (hde) minor=0; (hdf) minor=64
+ * Quaternary i/f: ide3: major=34; (hdg) minor=0; (hdh) minor=64
+ */
+
+/******************************************************************************
+ * IDE driver configuration options (play with these as desired):
+ *
+ * REALLY_SLOW_IO can be defined in ide.c and ide-cd.c, if necessary
+ */
+#undef REALLY_FAST_IO /* define if ide ports are perfect */
+#define INITIAL_MULT_COUNT 0 /* off=0; on=2,4,8,16,32, etc.. */
+
+#ifndef SUPPORT_SLOW_DATA_PORTS /* 1 to support slow data ports */
+#define SUPPORT_SLOW_DATA_PORTS 1 /* 0 to reduce kernel size */
+#endif
+#ifndef SUPPORT_VLB_SYNC /* 1 to support weird 32-bit chips */
+#define SUPPORT_VLB_SYNC 1 /* 0 to reduce kernel size */
+#endif
+#ifndef DISK_RECOVERY_TIME /* off=0; on=access_delay_time */
+#define DISK_RECOVERY_TIME 0 /* for hardware that needs it */
+#endif
+#ifndef OK_TO_RESET_CONTROLLER /* 1 needed for good error recovery */
+#define OK_TO_RESET_CONTROLLER 1 /* 0 for use with AH2372A/B interface */
+#endif
+#ifndef FAKE_FDISK_FOR_EZDRIVE /* 1 to help linux fdisk with EZDRIVE */
+#define FAKE_FDISK_FOR_EZDRIVE 1 /* 0 to reduce kernel size */
+#endif
+#ifndef FANCY_STATUS_DUMPS /* 1 for human-readable drive errors */
+#define FANCY_STATUS_DUMPS 1 /* 0 to reduce kernel size */
+#endif
+
+#ifdef CONFIG_BLK_DEV_CMD640
+#if 0 /* change to 1 when debugging cmd640 problems */
+void cmd640_dump_regs (void);
+#define CMD640_DUMP_REGS cmd640_dump_regs() /* for debugging cmd640 chipset */
+#endif
+#endif /* CONFIG_BLK_DEV_CMD640 */
+
+/*
+ * IDE_DRIVE_CMD is used to implement many features of the hdparm utility
+ */
+#define IDE_DRIVE_CMD 99 /* (magic) undef to reduce kernel size*/
+
+/*
+ * "No user-serviceable parts" beyond this point :)
+ *****************************************************************************/
+
+typedef unsigned char byte; /* used everywhere */
+
+/*
+ * Probably not wise to fiddle with these
+ */
+#define ERROR_MAX 8 /* Max read/write errors per sector */
+#define ERROR_RESET 3 /* Reset controller every 4th retry */
+#define ERROR_RECAL 1 /* Recalibrate every 2nd retry */
+
+/*
+ * Ensure that various configuration flags have compatible settings
+ */
+#ifdef REALLY_SLOW_IO
+#undef REALLY_FAST_IO
+#endif
+
+#define HWIF(drive) ((ide_hwif_t *)((drive)->hwif))
+#define HWGROUP(drive) ((ide_hwgroup_t *)(HWIF(drive)->hwgroup))
+
+/*
+ * Definitions for accessing IDE controller registers
+ */
+#define IDE_NR_PORTS (10)
+
+#define IDE_DATA_OFFSET (0)
+#define IDE_ERROR_OFFSET (1)
+#define IDE_NSECTOR_OFFSET (2)
+#define IDE_SECTOR_OFFSET (3)
+#define IDE_LCYL_OFFSET (4)
+#define IDE_HCYL_OFFSET (5)
+#define IDE_SELECT_OFFSET (6)
+#define IDE_STATUS_OFFSET (7)
+#define IDE_CONTROL_OFFSET (8)
+#define IDE_IRQ_OFFSET (9)
+
+#define IDE_FEATURE_OFFSET IDE_ERROR_OFFSET
+#define IDE_COMMAND_OFFSET IDE_STATUS_OFFSET
+
+#define IDE_DATA_REG (HWIF(drive)->io_ports[IDE_DATA_OFFSET])
+#define IDE_ERROR_REG (HWIF(drive)->io_ports[IDE_ERROR_OFFSET])
+#define IDE_NSECTOR_REG (HWIF(drive)->io_ports[IDE_NSECTOR_OFFSET])
+#define IDE_SECTOR_REG (HWIF(drive)->io_ports[IDE_SECTOR_OFFSET])
+#define IDE_LCYL_REG (HWIF(drive)->io_ports[IDE_LCYL_OFFSET])
+#define IDE_HCYL_REG (HWIF(drive)->io_ports[IDE_HCYL_OFFSET])
+#define IDE_SELECT_REG (HWIF(drive)->io_ports[IDE_SELECT_OFFSET])
+#define IDE_STATUS_REG (HWIF(drive)->io_ports[IDE_STATUS_OFFSET])
+#define IDE_CONTROL_REG (HWIF(drive)->io_ports[IDE_CONTROL_OFFSET])
+#define IDE_IRQ_REG (HWIF(drive)->io_ports[IDE_IRQ_OFFSET])
+
+#define IDE_FEATURE_REG IDE_ERROR_REG
+#define IDE_COMMAND_REG IDE_STATUS_REG
+#define IDE_ALTSTATUS_REG IDE_CONTROL_REG
+#define IDE_IREASON_REG IDE_NSECTOR_REG
+#define IDE_BCOUNTL_REG IDE_LCYL_REG
+#define IDE_BCOUNTH_REG IDE_HCYL_REG
+
+#ifdef REALLY_FAST_IO
+#define OUT_BYTE(b,p) outb((b),(p))
+#define IN_BYTE(p) (byte)inb(p)
+#else
+#define OUT_BYTE(b,p) outb_p((b),(p))
+#define IN_BYTE(p) (byte)inb_p(p)
+#endif /* REALLY_FAST_IO */
+
+#define GET_ERR() IN_BYTE(IDE_ERROR_REG)
+#define GET_STAT() IN_BYTE(IDE_STATUS_REG)
+#define OK_STAT(stat,good,bad) (((stat)&((good)|(bad)))==(good))
+#define BAD_R_STAT (BUSY_STAT | ERR_STAT)
+#define BAD_W_STAT (BAD_R_STAT | WRERR_STAT)
+#define BAD_STAT (BAD_R_STAT | DRQ_STAT)
+#define DRIVE_READY (READY_STAT | SEEK_STAT)
+#define DATA_READY (DRQ_STAT)
+
+/*
+ * Some more useful definitions
+ */
+#define IDE_MAJOR_NAME "ide" /* the same for all i/f; see also genhd.c */
+#define MAJOR_NAME IDE_MAJOR_NAME
+#define PARTN_BITS 6 /* number of minor dev bits for partitions */
+#define PARTN_MASK ((1<<PARTN_BITS)-1) /* a useful bit mask */
+#define MAX_DRIVES 2 /* per interface; 2 assumed by lots of code */
+#define SECTOR_WORDS (512 / 4) /* number of 32bit words per sector */
+#define IDE_LARGE_SEEK(b1,b2,t) (((b1) > (b2) + (t)) || ((b2) > (b1) + (t)))
+#define IDE_MIN(a,b) ((a)<(b) ? (a):(b))
+#define IDE_MAX(a,b) ((a)>(b) ? (a):(b))
+
+/*
+ * Timeouts for various operations:
+ */
+#define WAIT_DRQ (5*HZ/100) /* 50msec - spec allows up to 20ms */
+#ifdef CONFIG_APM
+#define WAIT_READY (5*HZ) /* 5sec - some laptops are very slow */
+#else
+#define WAIT_READY (3*HZ/100) /* 30msec - should be instantaneous */
+#endif /* CONFIG_APM */
+#define WAIT_PIDENTIFY (10*HZ) /* 10sec - should be less than 3ms (?)
+ if all ATAPI CD is closed at boot */
+#define WAIT_WORSTCASE (30*HZ) /* 30sec - worst case when spinning up */
+#define WAIT_CMD (10*HZ) /* 10sec - maximum wait for an IRQ to happen */
+#define WAIT_MIN_SLEEP (2*HZ/100) /* 20msec - minimum sleep time */
+
+#define SELECT_DRIVE(hwif,drive) \
+{ \
+ if (hwif->selectproc) \
+ hwif->selectproc(drive); \
+ OUT_BYTE((drive)->select.all, hwif->io_ports[IDE_SELECT_OFFSET]); \
+}
+
+/*
+ * Check for an interrupt and acknowledge the interrupt status
+ */
+struct hwif_s;
+typedef int (ide_ack_intr_t)(struct hwif_s *);
+
+/*
+ * Structure to hold all information about the location of this port
+ */
+typedef struct hw_regs_s {
+ ide_ioreg_t io_ports[IDE_NR_PORTS]; /* task file registers */
+ int irq; /* our irq number */
+ ide_ack_intr_t *ack_intr; /* acknowledge interrupt */
+ void *priv; /* interface specific data */
+} hw_regs_t;
+
+/*
+ * Register new hardware with ide
+ */
+int ide_register_hw(hw_regs_t *hw, struct hwif_s **hwifp);
+
+/*
+ * Set up hw_regs_t structure before calling ide_register_hw (optional)
+ */
+void ide_setup_ports( hw_regs_t *hw,
+ ide_ioreg_t base,
+ int *offsets,
+ ide_ioreg_t ctrl,
+ ide_ioreg_t intr,
+ ide_ack_intr_t *ack_intr,
+ int irq);
+
+#include <asm/ide.h>
+
+/*
+ * Now for the data we need to maintain per-drive: ide_drive_t
+ */
+
+#define ide_scsi 0x21
+#define ide_disk 0x20
+#define ide_optical 0x7
+#define ide_cdrom 0x5
+#define ide_tape 0x1
+#define ide_floppy 0x0
+
+typedef union {
+ unsigned all : 8; /* all of the bits together */
+ struct {
+ unsigned set_geometry : 1; /* respecify drive geometry */
+ unsigned recalibrate : 1; /* seek to cyl 0 */
+ unsigned set_multmode : 1; /* set multmode count */
+ unsigned set_tune : 1; /* tune interface for drive */
+ unsigned reserved : 4; /* unused */
+ } b;
+ } special_t;
+
+typedef struct ide_drive_s {
+ struct request *queue; /* request queue */
+ struct ide_drive_s *next; /* circular list of hwgroup drives */
+ unsigned long sleep; /* sleep until this time */
+ unsigned long service_start; /* time we started last request */
+ unsigned long service_time; /* service time of last request */
+ special_t special; /* special action flags */
+ byte keep_settings; /* restore settings after drive reset */
+ byte using_dma; /* disk is using dma for read/write */
+ byte waiting_for_dma; /* dma currently in progress */
+ byte unmask; /* flag: okay to unmask other irqs */
+ byte slow; /* flag: slow data port */
+ byte bswap; /* flag: byte swap data */
+ byte dsc_overlap; /* flag: DSC overlap */
+ byte nice1; /* flag: give potential excess bandwidth */
+ unsigned present : 1; /* drive is physically present */
+ unsigned noprobe : 1; /* from: hdx=noprobe */
+ unsigned busy : 1; /* currently doing revalidate_disk() */
+ unsigned removable : 1; /* 1 if need to do check_media_change */
+ unsigned forced_geom : 1; /* 1 if hdx=c,h,s was given at boot */
+ unsigned no_unmask : 1; /* disallow setting unmask bit */
+ unsigned no_io_32bit : 1; /* disallow enabling 32bit I/O */
+ unsigned nobios : 1; /* flag: do not probe bios for drive */
+ unsigned revalidate : 1; /* request revalidation */
+ unsigned atapi_overlap : 1; /* flag: ATAPI overlap (not supported) */
+ unsigned nice0 : 1; /* flag: give obvious excess bandwidth */
+ unsigned nice2 : 1; /* flag: give a share in our own bandwidth */
+ unsigned doorlocking : 1; /* flag: for removable only: door lock/unlock works */
+ unsigned autotune : 2; /* 1=autotune, 2=noautotune, 0=default */
+#if FAKE_FDISK_FOR_EZDRIVE
+ unsigned remap_0_to_1 : 1; /* flag: partitioned with ezdrive */
+#endif /* FAKE_FDISK_FOR_EZDRIVE */
+ byte media; /* disk, cdrom, tape, floppy, ... */
+ select_t select; /* basic drive/head select reg value */
+ byte ctl; /* "normal" value for IDE_CONTROL_REG */
+ byte ready_stat; /* min status value for drive ready */
+ byte mult_count; /* current multiple sector setting */
+ byte mult_req; /* requested multiple sector setting */
+ byte tune_req; /* requested drive tuning setting */
+ byte io_32bit; /* 0=16-bit, 1=32-bit, 2/3=32bit+sync */
+ byte bad_wstat; /* used for ignoring WRERR_STAT */
+ byte nowerr; /* used for ignoring WRERR_STAT */
+ byte sect0; /* offset of first sector for DM6:DDO */
+ byte usage; /* current "open()" count for drive */
+ byte head; /* "real" number of heads */
+ byte sect; /* "real" sectors per track */
+ byte bios_head; /* BIOS/fdisk/LILO number of heads */
+ byte bios_sect; /* BIOS/fdisk/LILO sectors per track */
+ unsigned short bios_cyl; /* BIOS/fdisk/LILO number of cyls */
+ unsigned short cyl; /* "real" number of cyls */
+ unsigned int drive_data; /* for use by tuneproc/selectproc as needed */
+ void *hwif; /* actually (ide_hwif_t *) */
+ wait_queue_head_t wqueue; /* used to wait for drive in open() */
+ struct hd_driveid *id; /* drive model identification info */
+ struct hd_struct *part; /* drive partition table */
+ char name[4]; /* drive name, such as "hda" */
+ void *driver; /* (ide_driver_t *) */
+ void *driver_data; /* extra driver data */
+ struct proc_dir_entry *proc; /* /proc/ide/ directory entry */
+ void *settings; /* /proc/ide/ drive settings */
+ char driver_req[10]; /* requests specific driver */
+#if 1
+ struct thresholds_s *smart_thresholds;
+ struct values_s *smart_values;
+#else
+ thresholds_t smart_thresholds;
+ values_t smart_values;
+#endif
+ } ide_drive_t;
+
+/*
+ * An ide_dmaproc_t() initiates/aborts DMA read/write operations on a drive.
+ *
+ * The caller is assumed to have selected the drive and programmed the drive's
+ * sector address using CHS or LBA. All that remains is to prepare for DMA
+ * and then issue the actual read/write DMA/PIO command to the drive.
+ *
+ * Returns 0 if all went well.
+ * Returns 1 if DMA read/write could not be started, in which case the caller
+ * should either try again later, or revert to PIO for the current request.
+ */
+typedef enum { ide_dma_read, ide_dma_write, ide_dma_begin,
+ ide_dma_end, ide_dma_check, ide_dma_on,
+ ide_dma_off, ide_dma_off_quietly, ide_dma_test_irq,
+ ide_dma_bad_drive, ide_dma_good_drive
+ } ide_dma_action_t;
+
+typedef int (ide_dmaproc_t)(ide_dma_action_t, ide_drive_t *);
+
+
+/*
+ * An ide_tuneproc_t() is used to set the speed of an IDE interface
+ * to a particular PIO mode. The "byte" parameter is used
+ * to select the PIO mode by number (0,1,2,3,4,5), and a value of 255
+ * indicates that the interface driver should "auto-tune" the PIO mode
+ * according to the drive capabilities in drive->id;
+ *
+ * Not all interface types support tuning, and not all of those
+ * support all possible PIO settings. They may silently ignore
+ * or round values as they see fit.
+ */
+typedef void (ide_tuneproc_t)(ide_drive_t *, byte);
+
+/*
+ * This is used to provide support for strange interfaces
+ */
+typedef void (ide_selectproc_t) (ide_drive_t *);
+
+/*
+ * hwif_chipset_t is used to keep track of the specific hardware
+ * chipset used by each IDE interface, if known.
+ */
+typedef enum { ide_unknown, ide_generic, ide_pci,
+ ide_cmd640, ide_dtc2278, ide_ali14xx,
+ ide_qd6580, ide_umc8672, ide_ht6560b,
+ ide_pdc4030, ide_rz1000, ide_trm290,
+ ide_cmd646, ide_cy82c693, ide_4drives
+ } hwif_chipset_t;
+
+typedef struct ide_pci_devid_s {
+ unsigned short vid;
+ unsigned short did;
+} ide_pci_devid_t;
+
+#define IDE_PCI_DEVID_NULL ((ide_pci_devid_t){0,0})
+#define IDE_PCI_DEVID_EQ(a,b) (a.vid == b.vid && a.did == b.did)
+
+typedef struct hwif_s {
+ struct hwif_s *next; /* for linked-list in ide_hwgroup_t */
+ void *hwgroup; /* actually (ide_hwgroup_t *) */
+ ide_ioreg_t io_ports[IDE_NR_PORTS]; /* task file registers */
+ hw_regs_t hw; /* Hardware info */
+ ide_drive_t drives[MAX_DRIVES]; /* drive info */
+ struct gendisk *gd; /* gendisk structure */
+ ide_tuneproc_t *tuneproc; /* routine to tune PIO mode for drives */
+ ide_selectproc_t *selectproc; /* tweaks hardware to select drive */
+ ide_dmaproc_t *dmaproc; /* dma read/write/abort routine */
+ unsigned long *dmatable; /* dma physical region descriptor table */
+ struct hwif_s *mate; /* other hwif from same PCI chip */
+ unsigned long dma_base; /* base addr for dma ports */
+ unsigned dma_extra; /* extra addr for dma ports */
+ unsigned long config_data; /* for use by chipset-specific code */
+ unsigned long select_data; /* for use by chipset-specific code */
+ struct proc_dir_entry *proc; /* /proc/ide/ directory entry */
+ int irq; /* our irq number */
+ byte major; /* our major number */
+ char name[6]; /* name of interface, eg. "ide0" */
+ byte index; /* 0 for ide0; 1 for ide1; ... */
+ hwif_chipset_t chipset; /* sub-module for tuning.. */
+ unsigned noprobe : 1; /* don't probe for this interface */
+ unsigned present : 1; /* this interface exists */
+ unsigned serialized : 1; /* serialized operation with mate hwif */
+ unsigned sharing_irq: 1; /* 1 = sharing irq with another hwif */
+ unsigned reset : 1; /* reset after probe */
+ unsigned autodma : 1; /* automatically try to enable DMA at boot */
+ byte channel; /* for dual-port chips: 0=primary, 1=secondary */
+ struct pci_dev *pci_dev; /* for pci chipsets */
+ ide_pci_devid_t pci_devid; /* for pci chipsets: {VID,DID} */
+#if (DISK_RECOVERY_TIME > 0)
+ unsigned long last_time; /* time when previous rq was done */
+#endif
+ } ide_hwif_t;
+
+/*
+ * internal ide interrupt handler type
+ */
+typedef void (ide_handler_t)(ide_drive_t *);
+
+typedef struct hwgroup_s {
+ spinlock_t spinlock; /* protects "busy" and "handler" */
+ ide_handler_t *handler;/* irq handler, if active */
+ int busy; /* BOOL: protects all fields below */
+ ide_drive_t *drive; /* current drive */
+ ide_hwif_t *hwif; /* ptr to current hwif in linked-list */
+ struct request *rq; /* current request */
+ struct timer_list timer; /* failsafe timer */
+ struct request wrq; /* local copy of current write rq */
+ unsigned long poll_timeout; /* timeout value during long polls */
+ } ide_hwgroup_t;
+
+/*
+ * configurable drive settings
+ */
+
+#define TYPE_INT 0
+#define TYPE_INTA 1
+#define TYPE_BYTE 2
+#define TYPE_SHORT 3
+
+#define SETTING_READ (1 << 0)
+#define SETTING_WRITE (1 << 1)
+#define SETTING_RW (SETTING_READ | SETTING_WRITE)
+
+typedef int (ide_procset_t)(ide_drive_t *, int);
+typedef struct ide_settings_s {
+ char *name;
+ int rw;
+ int read_ioctl;
+ int write_ioctl;
+ int data_type;
+ int min;
+ int max;
+ int mul_factor;
+ int div_factor;
+ void *data;
+ ide_procset_t *set;
+ int auto_remove;
+ struct ide_settings_s *next;
+} ide_settings_t;
+
+void ide_add_setting(ide_drive_t *drive, const char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set);
+void ide_remove_setting(ide_drive_t *drive, char *name);
+ide_settings_t *ide_find_setting_by_name(ide_drive_t *drive, char *name);
+int ide_read_setting(ide_drive_t *t, ide_settings_t *setting);
+int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val);
+void ide_add_generic_settings(ide_drive_t *drive);
+
+/*
+ * /proc/ide interface
+ */
+typedef struct {
+ const char *name;
+ mode_t mode;
+ read_proc_t *read_proc;
+ write_proc_t *write_proc;
+} ide_proc_entry_t;
+
+#ifdef CONFIG_PROC_FS
+void proc_ide_create(void);
+void proc_ide_destroy(void);
+void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data);
+void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p);
+read_proc_t proc_ide_read_capacity;
+read_proc_t proc_ide_read_geometry;
+
+/*
+ * Standard exit stuff:
+ */
+#define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) \
+{ \
+ len -= off; \
+ if (len < count) { \
+ *eof = 1; \
+ if (len <= 0) \
+ return 0; \
+ } else \
+ len = count; \
+ *start = page + off; \
+ return len; \
+}
+#else
+#define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) return 0;
+#endif
+
+/*
+ * Subdrivers support.
+ */
+#define IDE_SUBDRIVER_VERSION 1
+
+typedef int (ide_cleanup_proc)(ide_drive_t *);
+typedef void (ide_do_request_proc)(ide_drive_t *, struct request *, unsigned long);
+typedef void (ide_end_request_proc)(byte, ide_hwgroup_t *);
+typedef int (ide_ioctl_proc)(ide_drive_t *, struct inode *, struct file *, unsigned int, unsigned long);
+typedef int (ide_open_proc)(struct inode *, struct file *, ide_drive_t *);
+typedef void (ide_release_proc)(struct inode *, struct file *, ide_drive_t *);
+typedef int (ide_check_media_change_proc)(ide_drive_t *);
+typedef void (ide_pre_reset_proc)(ide_drive_t *);
+typedef unsigned long (ide_capacity_proc)(ide_drive_t *);
+typedef void (ide_special_proc)(ide_drive_t *);
+typedef void (ide_setting_proc)(ide_drive_t *);
+
+typedef struct ide_driver_s {
+ const char *name;
+ const char *version;
+ byte media;
+ unsigned busy : 1;
+ unsigned supports_dma : 1;
+ unsigned supports_dsc_overlap : 1;
+ ide_cleanup_proc *cleanup;
+ ide_do_request_proc *do_request;
+ ide_end_request_proc *end_request;
+ ide_ioctl_proc *ioctl;
+ ide_open_proc *open;
+ ide_release_proc *release;
+ ide_check_media_change_proc *media_change;
+ ide_pre_reset_proc *pre_reset;
+ ide_capacity_proc *capacity;
+ ide_special_proc *special;
+ ide_proc_entry_t *proc;
+ } ide_driver_t;
+
+#define DRIVER(drive) ((ide_driver_t *)((drive)->driver))
+
+/*
+ * IDE modules.
+ */
+#define IDE_CHIPSET_MODULE 0 /* not supported yet */
+#define IDE_PROBE_MODULE 1
+#define IDE_DRIVER_MODULE 2
+
+typedef int (ide_module_init_proc)(void);
+
+typedef struct ide_module_s {
+ int type;
+ ide_module_init_proc *init;
+ void *info;
+ struct ide_module_s *next;
+} ide_module_t;
+
+/*
+ * ide_hwifs[] is the master data structure used to keep track
+ * of just about everything in ide.c. Whenever possible, routines
+ * should be using pointers to a drive (ide_drive_t *) or
+ * pointers to a hwif (ide_hwif_t *), rather than indexing this
+ * structure directly (the allocation/layout may change!).
+ *
+ */
+#ifndef _IDE_C
+extern ide_hwif_t ide_hwifs[]; /* master data repository */
+extern ide_module_t *ide_modules;
+#endif
+
+/*
+ * We need blk.h, but we replace its end_request by our own version.
+ */
+#define IDE_DRIVER /* Toggle some magic bits in blk.h */
+#define LOCAL_END_REQUEST /* Don't generate end_request in blk.h */
+#include <linux/blk.h>
+
+void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup);
+
+/*
+ * This is used for (nearly) all data transfers from/to the IDE interface
+ */
+void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
+void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
+
+/*
+ * This is used for (nearly) all ATAPI data transfers from/to the IDE interface
+ */
+void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount);
+void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount);
+
+/*
+ * This is used on exit from the driver, to designate the next irq handler
+ * and also to start the safety timer.
+ */
+void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout);
+
+/*
+ * Error reporting, in human readable form (luxurious, but a memory hog).
+ */
+byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat);
+
+/*
+ * ide_error() takes action based on the error returned by the controller.
+ * The calling function must return afterwards, to restart the request.
+ */
+void ide_error (ide_drive_t *drive, const char *msg, byte stat);
+
+/*
+ * Issue a simple drive command
+ * The drive must be selected beforehand.
+ */
+void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler);
+
+/*
+ * ide_fixstring() cleans up and (optionally) byte-swaps a text string,
+ * removing leading/trailing blanks and compressing internal blanks.
+ * It is primarily used to tidy up the model name/number fields as
+ * returned by the WIN_[P]IDENTIFY commands.
+ */
+void ide_fixstring (byte *s, const int bytecount, const int byteswap);
+
+/*
+ * This routine busy-waits for the drive status to be not "busy".
+ * It then checks the status for all of the "good" bits and none
+ * of the "bad" bits, and if all is okay it returns 0. All other
+ * cases return 1 after invoking ide_error() -- caller should return.
+ *
+ */
+int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout);
+
+/*
+ * This routine is called from the partition-table code in genhd.c
+ * to "convert" a drive to a logical geometry with fewer than 1024 cyls.
+ *
+ * The second parameter, "xparm", determines exactly how the translation
+ * will be handled:
+ * 0 = convert to CHS with fewer than 1024 cyls
+ * using the same method as Ontrack DiskManager.
+ * 1 = same as "0", plus offset everything by 63 sectors.
+ * -1 = similar to "0", plus redirect sector 0 to sector 1.
+ * >1 = convert to a CHS geometry with "xparm" heads.
+ *
+ * Returns 0 if the translation was not possible, if the device was not
+ * an IDE disk drive, or if a geometry was "forced" on the commandline.
+ * Returns 1 if the geometry translation was successful.
+ */
+int ide_xlate_1024 (kdev_t, int, const char *);
+
+/*
+ * Start a reset operation for an IDE interface.
+ * The caller should return immediately after invoking this.
+ */
+void ide_do_reset (ide_drive_t *);
+
+/*
+ * This function is intended to be used prior to invoking ide_do_drive_cmd().
+ */
+void ide_init_drive_cmd (struct request *rq);
+
+/*
+ * "action" parameter type for ide_do_drive_cmd() below.
+ */
+typedef enum
+ {ide_wait, /* insert rq at end of list, and wait for it */
+ ide_next, /* insert rq immediately after current request */
+ ide_preempt, /* insert rq in front of current request */
+ ide_end} /* insert rq at end of list, but don't wait for it */
+ ide_action_t;
+
+/*
+ * This function issues a special IDE device request
+ * onto the request queue.
+ *
+ * If action is ide_wait, then the rq is queued at the end of the
+ * request queue, and the function sleeps until it has been processed.
+ * This is for use when invoked from an ioctl handler.
+ *
+ * If action is ide_preempt, then the rq is queued at the head of
+ * the request queue, displacing the currently-being-processed
+ * request and this function returns immediately without waiting
+ * for the new rq to be completed. This is VERY DANGEROUS, and is
+ * intended for careful use by the ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_next, then the rq is queued immediately after
+ * the currently-being-processed-request (if any), and the function
+ * returns without waiting for the new rq to be completed. As above,
+ * This is VERY DANGEROUS, and is intended for careful use by the
+ * ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_end, then the rq is queued at the end of the
+ * request queue, and the function returns immediately without waiting
+ * for the new rq to be completed. This is again intended for careful
+ * use by the ATAPI tape/cdrom driver code.
+ */
+int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action);
+
+/*
+ * Clean up after success/failure of an explicit drive cmd.
+ * stat/err are used only when (HWGROUP(drive)->rq->cmd == IDE_DRIVE_CMD).
+ */
+void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err);
+
+/*
+ * Issue ATA command and wait for completion.
+ */
+int ide_wait_cmd (ide_drive_t *drive, int cmd, int nsect, int feature, int sectors, byte *buf);
+
+void ide_delay_50ms (void);
+
+/*
+ * ide_system_bus_speed() returns what we think is the system VESA/PCI
+ * bus speed (in MHz). This is used for calculating interface PIO timings.
+ * The default is 40 for known PCI systems, 50 otherwise.
+ * The "idebus=xx" parameter can be used to override this value.
+ */
+int ide_system_bus_speed (void);
+
+/*
+ * ide_multwrite() transfers a block of up to mcount sectors of data
+ * to a drive as part of a disk multwrite operation.
+ */
+void ide_multwrite (ide_drive_t *drive, unsigned int mcount);
+
+/*
+ * ide_stall_queue() can be used by a drive to give excess bandwidth back
+ * to the hwgroup by sleeping for timeout jiffies.
+ */
+void ide_stall_queue (ide_drive_t *drive, unsigned long timeout);
+
+/*
+ * ide_get_queue() returns the queue which corresponds to a given device.
+ */
+struct request **ide_get_queue (kdev_t dev);
+
+/*
+ * CompactFlash cards and their brethern pretend to be removable hard disks,
+ * but they never have a slave unit, and they don't have doorlock mechanisms.
+ * This test catches them, and is invoked elsewhere when setting appropriate config bits.
+ */
+int drive_is_flashcard (ide_drive_t *drive);
+
+int ide_spin_wait_hwgroup(ide_drive_t *drive, unsigned long *flags);
+void ide_timer_expiry (unsigned long data);
+void ide_intr (int irq, void *dev_id, struct pt_regs *regs);
+void ide_geninit (struct gendisk *gd);
+void do_ide0_request (void);
+#if MAX_HWIFS > 1
+void do_ide1_request (void);
+#endif
+#if MAX_HWIFS > 2
+void do_ide2_request (void);
+#endif
+#if MAX_HWIFS > 3
+void do_ide3_request (void);
+#endif
+#if MAX_HWIFS > 4
+void do_ide4_request (void);
+#endif
+#if MAX_HWIFS > 5
+void do_ide5_request (void);
+#endif
+#if MAX_HWIFS > 6
+void do_ide6_request (void);
+#endif
+#if MAX_HWIFS > 7
+void do_ide7_request (void);
+#endif
+void ide_init_subdrivers (void);
+
+#ifndef _IDE_C
+extern struct file_operations ide_fops[];
+#endif
+
+#ifdef _IDE_C
+#ifdef CONFIG_BLK_DEV_IDE
+int ideprobe_init (void);
+#endif /* CONFIG_BLK_DEV_IDE */
+#ifdef CONFIG_BLK_DEV_IDEDISK
+int idedisk_init (void);
+#endif /* CONFIG_BLK_DEV_IDEDISK */
+#ifdef CONFIG_BLK_DEV_IDECD
+int ide_cdrom_init (void);
+#endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+int idetape_init (void);
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+int idefloppy_init (void);
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+int idescsi_init (void);
+#endif /* CONFIG_BLK_DEV_IDESCSI */
+#endif /* _IDE_C */
+
+int ide_register_module (ide_module_t *module);
+void ide_unregister_module (ide_module_t *module);
+ide_drive_t *ide_scan_devices (byte media, const char *name, ide_driver_t *driver, int n);
+int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int version);
+int ide_unregister_subdriver (ide_drive_t *drive);
+int ide_replace_subdriver(ide_drive_t *drive, const char *driver);
+
+#ifdef CONFIG_BLK_DEV_IDEPCI
+#define ON_BOARD 1
+#define NEVER_BOARD 0
+#ifdef CONFIG_BLK_DEV_OFFBOARD
+# define OFF_BOARD ON_BOARD
+#else /* CONFIG_BLK_DEV_OFFBOARD */
+# define OFF_BOARD NEVER_BOARD
+#endif /* CONFIG_BLK_DEV_OFFBOARD */
+unsigned long ide_find_free_region (unsigned short size) __init;
+void ide_scan_pcibus (void) __init;
+#endif
+#ifdef CONFIG_BLK_DEV_IDEDMA
+#define BAD_DMA_DRIVE 0
+#define GOOD_DMA_DRIVE 1
+int ide_build_dmatable (ide_drive_t *drive, ide_dma_action_t func);
+void ide_dma_intr (ide_drive_t *drive);
+int check_drive_lists (ide_drive_t *drive, int good_bad);
+int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive);
+int ide_release_dma (ide_hwif_t *hwif);
+void ide_setup_dma (ide_hwif_t *hwif, unsigned long dmabase, unsigned int num_ports) __init;
+unsigned long ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name) __init;
+#endif
+
+#endif /* _IDE_H */
diff --git a/include/linux/isdn_budget.h b/include/linux/isdn_budget.h
new file mode 100644
index 000000000..2eacceacf
--- /dev/null
+++ b/include/linux/isdn_budget.h
@@ -0,0 +1,62 @@
+/* isdn_budget.h
+ *
+ * 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.
+ *
+ */
+
+/*
+30.06.97:cal:angelegt
+04.11.97:cal:budget.period: int --> time_t
+*/
+
+#ifndef __isdn_budget_h__
+#define __isdn_budget_h__
+
+#include <linux/types.h>
+
+#define ISDN_BUDGET_DIAL 0
+#define ISDN_BUDGET_CHARGE 1
+#define ISDN_BUDGET_ONLINE 2
+#define ISDN_BUDGET_NUM_BUDGET 3
+
+#define ISDN_BUDGET_INIT 0
+#define ISDN_BUDGET_CHECK_DIAL 1
+#define ISDN_BUDGET_CHECK_CHARGE 2
+#define ISDN_BUDGET_CHECK_ONLINE 3
+#define ISDN_BUDGET_START_ONLINE 10
+
+#define ISDN_BUDGET_SET_BUDGET 0
+#define ISDN_BUDGET_GET_BUDGET 1
+
+typedef struct {
+ char name [9]; /* Interface */
+ int command, /* subcommand */
+ budget, /* budget-nr. */
+ amount, /* set/get budget-amount */
+ used; /* set/get used amount */
+ time_t period, /* set/get length of period */
+ period_started; /* set/get startpoint of period */
+} isdn_ioctl_budget;
+
+#ifdef __KERNEL__
+extern int isdn_net_budget(int, struct device *);
+extern int isdn_budget_ioctl(isdn_ioctl_budget *);
+#endif /* __KERNEL__ */
+
+#endif /* __isdn_budget_h__ */
diff --git a/include/linux/isdn_lzscomp.h b/include/linux/isdn_lzscomp.h
new file mode 100644
index 000000000..8f64bfa67
--- /dev/null
+++ b/include/linux/isdn_lzscomp.h
@@ -0,0 +1,26 @@
+/*
+ * $Id: isdn_lzscomp.h,v 1.1 1998/07/08 16:52:33 hipp Exp $
+ *
+ * Header for isdn_lzscomp.c
+ * Concentrated here to not mess up half a dozen kernel headers with code
+ * snippets
+ *
+ */
+
+#define CI_LZS_COMPRESS 17
+#define CILEN_LZS_COMPRESS 5
+
+#define LZS_CMODE_NONE 0
+#define LZS_CMODE_LCB 1
+#define LZS_CMODE_CRC 2
+#define LZS_CMODE_SEQNO 3 /* MUST be implemented (default) */
+#define LZS_CMODE_EXT 4 /* Seems to be what Win0.95 uses */
+
+#define LZS_COMP_MAX_HISTS 1 /* Don't waste peers ressources */
+#define LZS_COMP_DEF_HISTS 1 /* Most likely to negotiate */
+#define LZS_DECOMP_MAX_HISTS 32 /* More is really nonsense */
+#define LZS_DECOMP_DEF_HISTS 8 /* If we get it, this may be optimal */
+
+#define LZS_HIST_BYTE1(word) (word>>8) /* Just for better reading */
+#define LZS_HIST_BYTE2(word) (word&0xff) /* of this big endian stuff */
+#define LZS_HIST_WORD(b1,b2) ((b1<<8)|b2) /* (network byte order rulez) */
diff --git a/include/linux/isdn_timru.h b/include/linux/isdn_timru.h
new file mode 100644
index 000000000..361dbbea1
--- /dev/null
+++ b/include/linux/isdn_timru.h
@@ -0,0 +1,119 @@
+/* isdn_timru.h
+ *
+ * Linux ISDN subsystem, timeout-rules 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.
+ *
+ */
+
+/*
+02.06.97:cal:ISDN_TIMRU_PACKET_NONE def., ISDN_TIMRU_PACKET_* inkr.
+*/
+
+#ifndef __isdn_timru_h__
+#define __isdn_timru_h__
+
+#define ISDN_TIMRU_PACKET_NONE 0
+#define ISDN_TIMRU_PACKET_SKB 1
+#define ISDN_TIMRU_PACKET_PPP 2
+#define ISDN_TIMRU_PACKET_PPP_NO_HEADER 3
+
+#define ISDN_TIMRU_BRINGUP 0
+#define ISDN_TIMRU_KEEPUP_IN 1
+#define ISDN_TIMRU_KEEPUP_OUT 2
+#define ISDN_TIMRU_BRINGDOWN 3
+#define ISDN_TIMRU_NUM_CHECK 4
+
+#define ISDN_TIMRU_PROTFAM_WILDCARD 0
+#define ISDN_TIMRU_PROTFAM_IP 1
+#define ISDN_TIMRU_PROTFAM_PPP 2
+#define ISDN_TIMRU_PROTFAM_IPX 3
+#define ISDN_TIMRU_NUM_PROTFAM 4
+
+#define ISDN_TIMRU_IP_WILDCARD 0
+#define ISDN_TIMRU_IP_ICMP 1
+#define ISDN_TIMRU_IP_TCP 2
+#define ISDN_TIMRU_IP_UDP 3
+
+#define ISDN_TIMRU_PPP_WILDCARD 0
+#define ISDN_TIMRU_PPP_IPCP 1
+#define ISDN_TIMRU_PPP_IPXCP 2
+#define ISDN_TIMRU_PPP_CCP 3
+#define ISDN_TIMRU_PPP_LCP 4
+#define ISDN_TIMRU_PPP_PAP 5
+#define ISDN_TIMRU_PPP_LQR 6
+#define ISDN_TIMRU_PPP_CHAP 7
+
+typedef struct {
+ struct in_addr saddr, /* Source Address */
+ smask, /* Source Subnetmask */
+ daddr, /* Dest. Address */
+ dmask; /* Dest. Subnetmask */
+ ushort protocol; /* TCP, UDP, ... */
+ union {
+ struct {
+ __u16 s_from, /* Source Port */
+ s_to,
+ d_from,
+ d_to;
+ } port;
+ struct {
+ __u8 from, /* ICMP-Type */
+ to;
+ } type;
+ } pt;
+} isdn_timeout_rule_ip;
+
+
+typedef struct {
+ ushort protocol; /* IPCP, LCP, ... */
+} isdn_timeout_rule_ppp;
+
+
+typedef struct isdn_timeout_rule_s {
+ struct isdn_timeout_rule_s *next, /* Pointer to next rule */
+ *prev; /* Pointer to previous rule */
+ ushort type, /* BRINGUP, KEEPUP_*, ... */
+ neg;
+ int timeout; /* Timeout value */
+ ushort protfam; /* IP, IPX, PPP, ... */
+ union {
+ isdn_timeout_rule_ip ip; /* IP-Rule */
+ isdn_timeout_rule_ppp ppp; /* PPP-Rule */
+ } rule; /* Prot.-specific rule */
+} isdn_timeout_rule;
+
+
+typedef struct {
+ char name [9]; /* Interface */
+ int where, /* 0/1: add to start/end of list, -1: handle default */
+ type,
+ protfam,
+ index,
+ defval;
+ isdn_timeout_rule rule; /* Rule */
+} isdn_ioctl_timeout_rule;
+
+#ifdef __KERNEL__
+extern int isdn_net_recalc_timeout(int, int, struct device *, void *, ulong);
+extern int isdn_timru_alloc_timeout_rules(struct device *);
+extern int isdn_timru_ioctl_add_rule(isdn_ioctl_timeout_rule *);
+extern int isdn_timru_ioctl_del_rule(isdn_ioctl_timeout_rule *);
+extern int isdn_timru_ioctl_get_rule(isdn_ioctl_timeout_rule *);
+#endif /* __KERNEL__ */
+
+#endif /* __isdn_timru_h__ */
diff --git a/include/net/decnet_call.h b/include/net/decnet_call.h
new file mode 100644
index 000000000..90a30561e
--- /dev/null
+++ b/include/net/decnet_call.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void decnet_proto_init(struct net_proto *pro);
diff --git a/include/net/dn.h b/include/net/dn.h
new file mode 100644
index 000000000..b2ad40426
--- /dev/null
+++ b/include/net/dn.h
@@ -0,0 +1,206 @@
+#ifndef _NET_DN_H
+#define _NET_DN_H
+
+#include <linux/dn.h>
+#include <asm/byteorder.h>
+
+typedef unsigned short dn_address;
+
+#define dn_ntohs(x) le16_to_cpu(x)
+#define dn_htons(x) cpu_to_le16(x)
+
+struct dn_scp /* Session Control Port */
+{
+ unsigned char state;
+#define DN_O 1 /* Open */
+#define DN_CR 2 /* Connect Receive */
+#define DN_DR 3 /* Disconnect Reject */
+#define DN_DRC 4 /* Discon. Rej. Complete*/
+#define DN_CC 5 /* Connect Confirm */
+#define DN_CI 6 /* Connect Initiate */
+#define DN_NR 7 /* No resources */
+#define DN_NC 8 /* No communication */
+#define DN_CD 9 /* Connect Delivery */
+#define DN_RJ 10 /* Rejected */
+#define DN_RUN 11 /* Running */
+#define DN_DI 12 /* Disconnect Initiate */
+#define DN_DIC 13 /* Disconnect Complete */
+#define DN_DN 14 /* Disconnect Notificat */
+#define DN_CL 15 /* Closed */
+#define DN_CN 16 /* Closed Notification */
+
+ unsigned short addrloc;
+ unsigned short addrrem;
+ unsigned short numdat;
+ unsigned short numoth;
+ unsigned short numoth_rcv;
+ unsigned short numdat_rcv;
+ unsigned short ackxmt_dat;
+ unsigned short ackxmt_oth;
+ unsigned short ackrcv_dat;
+ unsigned short ackrcv_oth;
+ unsigned char flowrem_sw;
+ unsigned char flowloc_sw;
+#define DN_SEND 2
+#define DN_DONTSEND 1
+#define DN_NOCHANGE 0
+ unsigned char accept_mode;
+ unsigned short mss;
+
+ struct optdata_dn conndata_in;
+ struct optdata_dn conndata_out;
+ struct optdata_dn discdata_in;
+ struct optdata_dn discdata_out;
+ struct accessdata_dn accessdata;
+
+ struct sockaddr_dn addr; /* Local address */
+ struct sockaddr_dn peer; /* Remote address */
+
+ /*
+ * In this case the RTT estimation is not specified in the
+ * docs, nor is any back off algorithm. Here we follow well
+ * known tcp algorithms with a few small variations.
+ *
+ * snd_window: Max number of packets we send before we wait for
+ * an ack to come back. This will become part of a
+ * more complicated scheme when we support flow
+ * control.
+ *
+ * nsp_srtt: Round-Trip-Time (x8) in jiffies. This is a rolling
+ * average.
+ * nsp_rttvar: Round-Trip-Time-Varience (x4) in jiffies. This is the
+ * varience of the smoothed average (but calculated in
+ * a simpler way than for normal statistical varience
+ * calculations).
+ *
+ * nsp_rxtshift: Backoff counter. Value is zero normally, each time
+ * a packet is lost is increases by one until an ack
+ * is received. Its used to index an array of backoff
+ * multipliers.
+ */
+#define NSP_MIN_WINDOW 1
+#define NSP_MAX_WINDOW 512
+ unsigned long snd_window;
+#define NSP_INITIAL_SRTT (HZ)
+ unsigned long nsp_srtt;
+#define NSP_INITIAL_RTTVAR (HZ*3)
+ unsigned long nsp_rttvar;
+#define NSP_MAXRXTSHIFT 12
+ unsigned long nsp_rxtshift;
+
+ /*
+ * Output queues, one for data, one for otherdata/linkservice
+ */
+ struct sk_buff_head data_xmit_queue;
+ struct sk_buff_head other_xmit_queue;
+
+ /*
+ * Input queue for other data
+ */
+ struct sk_buff_head other_receive_queue;
+ int other_report;
+
+ /*
+ * Stuff to do with the slow timer
+ */
+ unsigned long stamp; /* time of last transmit */
+ unsigned long persist;
+ int (*persist_fxn)(struct sock *sk);
+ unsigned long keepalive;
+ void (*keepalive_fxn)(struct sock *sk);
+
+ /*
+ * This stuff is for the fast timer for delayed acks
+ */
+ struct timer_list delack_timer;
+ int delack_pending;
+ void (*delack_fxn)(struct sock *sk);
+};
+
+/*
+ * src,dst : Source and Destination DECnet addresses
+ * neigh: Address from which we've just got this skb.
+ * hops : Number of hops through the network
+ * dst_port, src_port : NSP port numbers
+ * services, info : Useful data extracted from conninit messages
+ * rt_flags : Routing flags byte
+ * nsp_flags : NSP layer flags byte
+ * segsize : Size of segment
+ * segnum : Number, for data, otherdata and linkservice
+ * xmit_count : Number of times we've transmitted this skb
+ * stamp : Time stamp of first transmission, used in RTT calculations
+ * iif: Input interface number
+ *
+ * As a general policy, this structure keeps all addresses in network
+ * byte order, and all else in host byte order. Thus dst, src, dst_port
+ * src_port and neigh are in network order. All else is in host order.
+ *
+ */
+struct dn_skb_cb {
+ unsigned short dst;
+ unsigned short src;
+ unsigned short neigh;
+ unsigned short hops;
+ unsigned short dst_port;
+ unsigned short src_port;
+ unsigned char services;
+ unsigned char info;
+ unsigned char rt_flags;
+ unsigned char nsp_flags;
+ unsigned short segsize;
+ unsigned short segnum;
+ unsigned short xmit_count;
+ unsigned long stamp;
+ int iif;
+};
+
+static __inline__ dn_address dn_eth2dn(unsigned char *ethaddr)
+{
+ return ethaddr[4] | (ethaddr[5] << 8);
+}
+
+static __inline__ dn_address dn_saddr2dn(struct sockaddr_dn *saddr)
+{
+ return *(dn_address *)saddr->sdn_nodeaddr;
+}
+
+static __inline__ void dn_dn2eth(unsigned char *ethaddr, dn_address addr)
+{
+ ethaddr[0] = 0xAA;
+ ethaddr[1] = 0x00;
+ ethaddr[2] = 0x04;
+ ethaddr[3] = 0x00;
+ ethaddr[4] = (unsigned char)(addr & 0xff);
+ ethaddr[5] = (unsigned char)(addr >> 8);
+}
+
+#define DN_MENUVER_ACC 0x01
+#define DN_MENUVER_USR 0x02
+#define DN_MENUVER_PRX 0x04
+#define DN_MENUVER_UIC 0x08
+
+extern struct sock *dn_sklist_find_listener(struct sockaddr_dn *addr);
+extern struct sock *dn_find_by_skb(struct sk_buff *skb);
+extern unsigned short dn_alloc_port(void);
+#define DN_ASCBUF_LEN 7
+extern char *dn_addr2asc(dn_address, char *);
+extern void dn_destroy_sock(struct sock *sk);
+
+extern int dn_sockaddr2username(struct sockaddr_dn *addr, unsigned char *buf, unsigned char type);
+extern int dn_username2sockaddr(unsigned char *data, int len, struct sockaddr_dn *addr, unsigned char *type);
+
+extern void dn_start_slow_timer(struct sock *sk);
+extern void dn_stop_slow_timer(struct sock *sk);
+extern void dn_start_fast_timer(struct sock *sk);
+extern void dn_stop_fast_timer(struct sock *sk);
+
+extern dn_address decnet_address;
+extern unsigned char decnet_ether_address[6];
+extern int decnet_node_type;
+extern int decnet_debug_level;
+extern int decnet_time_wait;
+extern int decnet_dn_count;
+extern int decnet_di_count;
+extern int decnet_dr_count;
+
+#endif /* _NET_DN_H */
diff --git a/include/net/dn_dev.h b/include/net/dn_dev.h
new file mode 100644
index 000000000..cb9eda018
--- /dev/null
+++ b/include/net/dn_dev.h
@@ -0,0 +1,194 @@
+#ifndef _NET_DN_DEV_H
+#define _NET_DN_DEV_H
+
+
+struct dn_dev;
+
+struct dn_ifaddr {
+ struct dn_ifaddr *ifa_next;
+ struct dn_dev *ifa_dev;
+ dn_address ifa_local;
+ unsigned char ifa_flags;
+ unsigned char ifa_scope;
+ char ifa_label[IFNAMSIZ];
+};
+
+#define DN_DEV_S_RU 0 /* Run - working normally */
+#define DN_DEV_S_CR 1 /* Circuit Rejected */
+#define DN_DEV_S_DS 2 /* Data Link Start */
+#define DN_DEV_S_RI 3 /* Routing Layer Initialize */
+#define DN_DEV_S_RV 4 /* Routing Layer Verify */
+#define DN_DEV_S_RC 5 /* Routing Layer Complete */
+#define DN_DEV_S_OF 6 /* Off */
+#define DN_DEV_S_HA 7 /* Halt */
+
+
+/*
+ * The dn_dev_parms structure contains the set of parameters
+ * for each device (hence inclusion in the dn_dev structure)
+ * and an array is used to store the default types of supported
+ * device (in dn_dev.c).
+ *
+ * The type field matches the ARPHRD_ constants and is used in
+ * searching the list for supported devices when new devices
+ * come up.
+ *
+ * The mode field is used to find out if a device is broadcast,
+ * multipoint, or pointopoint. Please note that DECnet thinks
+ * different ways about devices to the rest of the kernel
+ * so the normal IFF_xxx flags are invalid here. For devices
+ * which can be any combination of the previously mentioned
+ * attributes, you can set this on a per device basis by
+ * installing an up() routine.
+ *
+ * The device state field, defines the initial state in which the
+ * device will come up. In the dn_dev structure, it is the actual
+ * state.
+ *
+ * The cost field is used in the routing algorithm.
+ *
+ * Timers:
+ * t1 - Routing timer, send routing messages when it expires
+ * t2 - Rate limit timer, min time between routing and hello messages
+ * t3 - Hello timer, send hello messages when it expires
+ *
+ * Callbacks:
+ * up() - Called to initialize device, return value can veto use of
+ * device with DECnet.
+ * down() - Called to turn device off when it goes down
+ * timer1() - Called when timer 1 goes off
+ * timer3() - Called when timer 3 goes off
+ * setsrc() - Called for each incomming frame to set previous hop info
+ * neigh_setup() - Called to do device specific setup of neighbours
+ *
+ * sysctl - Hook for sysctl things
+ *
+ */
+struct dn_dev_parms {
+ int type; /* ARPHRD_xxx */
+ int mode; /* Broadcast, Unicast, Mulitpoint */
+#define DN_DEV_BCAST 1
+#define DN_DEV_UCAST 2
+#define DN_DEV_MPOINT 4
+ int state; /* Initial state */
+ int cost; /* Default cost of device */
+ unsigned short blksize; /* Block Size */
+ unsigned long t1; /* Default value of t1 */
+ unsigned long t2; /* Default value of t2 */
+ unsigned long t3; /* Default value of t3 */
+ int priority; /* Priority to be a router */
+ char *name; /* Name for sysctl */
+ int ctl_name; /* Index for sysctl */
+ int (*up)(struct device *);
+ void (*down)(struct device *);
+ void (*timer1)(struct device *);
+ void (*timer3)(struct device *);
+ int (*setsrc)(struct sk_buff *skb);
+ int (*neigh_setup)(struct neighbour *);
+ void *sysctl;
+};
+
+
+struct dn_dev {
+ struct dn_ifaddr *ifa_list;
+ struct device *dev;
+ struct dn_dev_parms parms;
+ char use_long;
+ struct timer_list timer;
+ unsigned long t3, t1;
+ struct neigh_parms *neigh_parms;
+ unsigned char addr[ETH_ALEN];
+ struct neighbour *router; /* Default router on circuit */
+ struct neighbour *peer; /* Peer on pointopoint links */
+ unsigned long uptime; /* Time device went up in jiffies */
+};
+
+struct dn_short_packet
+{
+ unsigned char msgflg __attribute__((packed));
+ unsigned short dstnode __attribute__((packed));
+ unsigned short srcnode __attribute__((packed));
+ unsigned char forward __attribute__((packed));
+};
+
+struct dn_long_packet
+{
+ unsigned char msgflg __attribute__((packed));
+ unsigned char d_area __attribute__((packed));
+ unsigned char d_subarea __attribute__((packed));
+ unsigned char d_id[6] __attribute__((packed));
+ unsigned char s_area __attribute__((packed));
+ unsigned char s_subarea __attribute__((packed));
+ unsigned char s_id[6] __attribute__((packed));
+ unsigned char nl2 __attribute__((packed));
+ unsigned char visit_ct __attribute__((packed));
+ unsigned char s_class __attribute__((packed));
+ unsigned char pt __attribute__((packed));
+};
+
+/*------------------------- DRP - Routing messages ---------------------*/
+
+ struct endnode_hello_message
+ {
+ unsigned char msgflg __attribute__((packed));
+ unsigned char tiver[3] __attribute__((packed));
+ unsigned char id[6] __attribute__((packed));
+ unsigned char iinfo __attribute__((packed));
+ unsigned short blksize __attribute__((packed));
+ unsigned char area __attribute__((packed));
+ unsigned char seed[8] __attribute__((packed));
+ unsigned char neighbor[6] __attribute__((packed));
+ unsigned short timer __attribute__((packed));
+ unsigned char mpd __attribute__((packed));
+ unsigned char datalen __attribute__((packed));
+ unsigned char data[2] __attribute__((packed));
+ };
+ struct rtnode_hello_message
+ {
+ unsigned char msgflg __attribute__((packed));
+ unsigned char tiver[3] __attribute__((packed));
+ unsigned char id[6] __attribute__((packed));
+ unsigned char iinfo __attribute__((packed));
+ unsigned short blksize __attribute__((packed));
+ unsigned char priority __attribute__((packed));
+ unsigned char area __attribute__((packed));
+ unsigned short timer __attribute__((packed));
+ unsigned char mpd __attribute__((packed));
+ };
+
+
+extern void dn_dev_init(void);
+extern void dn_dev_cleanup(void);
+
+extern int dn_dev_ioctl(unsigned int cmd, void *arg);
+
+extern void dn_dev_devices_off(void);
+extern void dn_dev_devices_on(void);
+
+extern void dn_dev_init_pkt(struct sk_buff *skb);
+extern void dn_dev_veri_pkt(struct sk_buff *skb);
+extern void dn_dev_hello(struct sk_buff *skb);
+
+extern void dn_dev_up(struct device *);
+extern void dn_dev_down(struct device *);
+
+extern struct device *decnet_default_device;
+
+static __inline__ int dn_dev_islocal(struct device *dev, dn_address addr)
+{
+ struct dn_dev *dn_db = dev->dn_ptr;
+ struct dn_ifaddr *ifa;
+
+ if (dn_db == NULL) {
+ printk(KERN_DEBUG "dn_dev_islocal: Called for non DECnet device\n");
+ return 0;
+ }
+
+ for(ifa = dn_db->ifa_list; ifa; ifa = ifa->ifa_next)
+ if ((addr ^ ifa->ifa_local) == 0)
+ return 1;
+
+ return 0;
+}
+
+#endif /* _NET_DN_DEV_H */
diff --git a/include/net/dn_fib.h b/include/net/dn_fib.h
new file mode 100644
index 000000000..22d227278
--- /dev/null
+++ b/include/net/dn_fib.h
@@ -0,0 +1,83 @@
+#ifndef _NET_DN_FIB_H
+#define _NET_DN_FIB_H
+
+#ifdef CONFIG_DECNET_ROUTER
+
+
+struct dn_fib_res {
+ dn_address res_addr;
+ dn_address res_mask;
+ int res_ifindex;
+ int res_proto;
+ int res_cost;
+ int res_type;
+ struct dn_fib_node *res_fn;
+ struct dn_fib_action *res_fa;
+};
+
+struct dn_fib_action {
+ struct dn_fib_action *fa_next;
+ dn_address fa_key;
+ dn_address fa_mask;
+ int fa_ifindex;
+ int fa_proto;
+ int fa_cost;
+ int fa_type;
+ union {
+ struct neighbour *fau_neigh; /* Normal route */
+ int fau_error; /* Reject */
+ int fau_table; /* Throw */
+ } fa_u;
+#define fa_neigh fa_u.fau_neigh
+#define fa_error fa_u.fau_error
+#define fa_table fa_u.fau_table
+};
+
+struct dn_fib_node {
+ struct dn_fib_node *fn_up;
+ dn_address fn_cmpmask;
+ dn_address fn_key;
+ int fn_shift;
+ struct dn_fib_action *fn_action;
+ struct dn_fib_node *fn_children[2];
+};
+
+#define DN_FIB_NEXT(fibnode, key) ((fibnode)->fn_children[((key) ^ (fibnode)->fn_cmpmask) >> (fibnode)->fn_shift])
+
+struct dn_fib_walker_t;
+
+struct dn_fib_table {
+ int n;
+ unsigned long count;
+ struct dn_fib_node *root;
+
+ int (*insert)(struct dn_fib_table *t, struct dn_fib_action *fa);
+ int (*delete)(struct dn_fib_table *t, struct dn_fib_action *fa);
+ int (*lookup)(struct dn_fib_table *t, struct dn_fib_res *res);
+ int (*walk)(struct dn_fib_walker_t *fwt);
+#ifdef CONFIG_RTNETLINK
+ int (*dump)(struct dn_fib_table *t, struct sk_buff *skb, struct netlink_callback *cb);
+#endif /* CONFIG_RTNETLINK */
+};
+
+struct dn_fib_walker_t {
+ struct dn_fib_table *table;
+ void *arg;
+ int (*fxn)(struct dn_fib_walker_t *fwt, struct dn_fib_node *n);
+};
+
+extern void dn_fib_init(void);
+extern void dn_fib_cleanup(void);
+
+extern int dn_fib_rt_message(struct sk_buff *skb);
+extern int dn_fib_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg);
+
+#ifdef CONFIG_RTNETLINK
+extern int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
+extern int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
+extern int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb);
+extern int dn_fib_rtm_getroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg);
+#endif /* CONFIG_RTNETLINK */
+#endif /* CONFIG_DECNET_ROUTER */
+
+#endif /* _NET_DN_FIB_H */
diff --git a/include/net/dn_neigh.h b/include/net/dn_neigh.h
new file mode 100644
index 000000000..b4e7ed1d8
--- /dev/null
+++ b/include/net/dn_neigh.h
@@ -0,0 +1,29 @@
+#ifndef _NET_DN_NEIGH_H
+#define _NET_DN_NEIGH_H
+
+/*
+ * The position of the first two fields of
+ * this structure are critical - SJW
+ */
+struct dn_neigh {
+ struct neighbour n;
+ unsigned char addr[ETH_ALEN];
+ unsigned long flags;
+#define DN_NDFLAG_R1 0x0001 /* Router L1 */
+#define DN_NDFLAG_R2 0x0002 /* Router L2 */
+#define DN_NDFLAG_P3 0x0004 /* Phase III Node */
+ unsigned long blksize;
+ unsigned char priority;
+};
+
+extern void dn_neigh_init(void);
+extern void dn_neigh_cleanup(void);
+extern struct neighbour *dn_neigh_lookup(struct neigh_table *tbl, void *ptr);
+extern void dn_neigh_router_hello(struct sk_buff *skb);
+extern void dn_neigh_endnode_hello(struct sk_buff *skb);
+extern void dn_neigh_pointopoint_hello(struct sk_buff *skb);
+extern int dn_neigh_elist(struct device *dev, unsigned char *ptr, int n);
+
+extern struct neigh_table dn_neigh_table;
+
+#endif /* _NET_DN_NEIGH_H */
diff --git a/include/net/dn_nsp.h b/include/net/dn_nsp.h
new file mode 100644
index 000000000..d8cef8c06
--- /dev/null
+++ b/include/net/dn_nsp.h
@@ -0,0 +1,194 @@
+#ifndef _NET_DN_NSP_H
+#define _NET_DN_NSP_H
+/******************************************************************************
+ (c) 1995-1998 E.M. Serrat emserrat@geocities.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
+ 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.
+*******************************************************************************/
+/* dn_nsp.c functions prototyping */
+
+extern void dn_nsp_send_data_ack(struct sock *sk);
+extern void dn_nsp_send_oth_ack(struct sock *sk);
+extern void dn_nsp_delayed_ack(struct sock *sk);
+extern void dn_send_conn_ack(struct sock *sk);
+extern void dn_send_conn_conf(struct sock *sk);
+extern void dn_send_disc(struct sock *sk, unsigned char type, unsigned short reason);
+extern void dn_nsp_send_lnk(struct sock *sk, unsigned short flags);
+extern void dn_nsp_send_conninit(struct sock *sk, unsigned char flags);
+
+extern void dn_nsp_output(struct sock *sk);
+extern int dn_nsp_check_xmit_queue(struct sock *sk, struct sk_buff *skb, struct sk_buff_head *q, unsigned short acknum);
+extern void dn_nsp_queue_xmit(struct sock *sk, struct sk_buff *skb, int oob);
+extern unsigned long dn_nsp_persist(struct sock *sk);
+extern int dn_nsp_xmit_timeout(struct sock *sk);
+
+extern int dn_nsp_rx(struct sk_buff *);
+extern int dn_nsp_backlog_rcv(struct sock *sk, struct sk_buff *skb);
+
+extern struct sk_buff *dn_alloc_skb(struct sock *sk, int size, int pri);
+extern struct sk_buff *dn_alloc_send_skb(struct sock *sk, int *size, int noblock, int *err);
+
+#define NSP_REASON_NR 1
+#define NSP_REASON_DC 42
+#define NSP_REASON_NL 41
+
+#define NSP_DISCINIT 0x38
+#define NSP_DISCCONF 0x48
+
+/*------------------------- NSP - messages ------------------------------*/
+/* Data Messages */
+/*---------------*/
+
+/* Data Messages (data segment/interrupt/link service) */
+
+ struct nsp_data_seg_msg
+ {
+ unsigned char msgflg __attribute__((packed));
+ unsigned short dstaddr __attribute__((packed));
+ unsigned short srcaddr __attribute__((packed));
+ };
+
+ struct nsp_data_opt_msg
+ {
+ unsigned short acknum __attribute__((packed));
+ unsigned short segnum __attribute__((packed));
+ unsigned short lsflgs __attribute__((packed));
+ };
+
+ struct nsp_data_opt_msg1
+ {
+ unsigned short acknum __attribute__((packed));
+ unsigned short segnum __attribute__((packed));
+ };
+
+/* Acknowledgment Messages */
+/*-------------------------*/
+
+/* Acknowledgment Messages (data/other data) */
+
+ struct nsp_data_ack_msg
+ {
+ unsigned char msgflg __attribute__((packed));
+ unsigned short dstaddr __attribute__((packed));
+ unsigned short srcaddr __attribute__((packed));
+ unsigned short acknum __attribute__((packed));
+ };
+
+/* Connect Acknowledgment Message */
+
+ struct nsp_conn_ack_msg
+ {
+ unsigned char msgflg __attribute__((packed));
+ unsigned short dstaddr __attribute__((packed));
+ };
+
+/* Control Messages */
+/*------------------*/
+
+/* Connect Initiate/Retransmit Initiate/Connect Confirm */
+
+ struct nsp_conn_init_msg
+ {
+ unsigned char msgflg __attribute__((packed));
+#define NSP_CI 0x18 /* Connect Initiate */
+#define NSP_RCI 0x68 /* Retrans. Conn Init */
+ unsigned short dstaddr __attribute__((packed));
+ unsigned short srcaddr __attribute__((packed));
+ unsigned char services __attribute__((packed));
+#define NSP_FC_NONE 0x00 /* Flow Control None */
+#define NSP_FC_SRC 0x04 /* Seg Req. Count */
+#define NSP_FC_SCMC 0x08 /* Sess. Control Mess */
+ unsigned char info __attribute__((packed));
+ unsigned short segsize __attribute__((packed));
+ };
+
+/* Disconnect Initiate/Disconnect Confirm */
+
+ struct nsp_disconn_init_msg
+ {
+ unsigned char msgflg __attribute__((packed));
+ unsigned short dstaddr __attribute__((packed));
+ unsigned short srcaddr __attribute__((packed));
+ unsigned short reason __attribute__((packed));
+ };
+
+
+/*------------------------- SCP - messages ------------------------------*/
+
+ struct srcobj_fmt
+ {
+ char format __attribute__((packed));
+ unsigned char task __attribute__((packed));
+ unsigned short grpcode __attribute__((packed));
+ unsigned short usrcode __attribute__((packed));
+ char dlen __attribute__((packed));
+ };
+
+/*
+ * A collection of functions for manipulating the sequence
+ * numbers used in NSP. Similar in operation to the functions
+ * of the same name in TCP.
+ */
+static __inline__ int before(unsigned short seq1, unsigned short seq2)
+{
+ seq1 &= 0x0fff;
+ seq2 &= 0x0fff;
+
+ return (int)((seq1 - seq2) & 0x0fff) > 2048;
+}
+
+
+static __inline__ int after(unsigned short seq1, unsigned short seq2)
+{
+ seq1 &= 0x0fff;
+ seq2 &= 0x0fff;
+
+ return (int)((seq2 - seq1) & 0x0fff) > 2048;
+}
+
+static __inline__ int equal(unsigned short seq1, unsigned short seq2)
+{
+ return ((seq1 ^ seq2) & 0x0fff) == 0;
+}
+
+static __inline__ int before_or_equal(unsigned short seq1, unsigned short seq2)
+{
+ return (before(seq1, seq2) || equal(seq1, seq2));
+}
+
+static __inline__ void seq_add(unsigned short *seq, unsigned short off)
+{
+ *seq += off;
+ *seq &= 0x0fff;
+}
+
+static __inline__ int seq_next(unsigned short seq1, unsigned short seq2)
+{
+ return (((seq2&0x0fff) - (seq1&0x0fff)) == 1);
+}
+
+/*
+ * Can we delay the ack ?
+ */
+static __inline__ int sendack(unsigned short seq)
+{
+ return (int)((seq & 0x1000) ? 0 : 1);
+}
+
+/*
+ * Is socket congested ?
+ */
+static __inline__ int dn_congested(struct sock *sk)
+{
+ return atomic_read(&sk->rmem_alloc) > (sk->rcvbuf >> 1);
+}
+
+#endif /* _NET_DN_NSP_H */
diff --git a/include/net/dn_raw.h b/include/net/dn_raw.h
new file mode 100644
index 000000000..a4502d29c
--- /dev/null
+++ b/include/net/dn_raw.h
@@ -0,0 +1,17 @@
+#ifndef _NET_DN_RAW_H
+#define _NET_DN_RAW_H
+
+#ifdef CONFIG_DECNET_RAW
+
+extern struct proto_ops dn_raw_proto_ops;
+
+extern void dn_raw_rx_nsp(struct sk_buff *skb);
+extern void dn_raw_rx_routing(struct sk_buff *skb);
+
+#ifdef CONFIG_DECNET_MOP
+extern void dn_raw_rx_mop(struct sk_buff *skb);
+#endif /* CONFIG_DECNET_MOP */
+
+#endif /* CONFIG_DECNET_RAW */
+
+#endif /* _NET_DN_RAW_H */
diff --git a/include/net/dn_route.h b/include/net/dn_route.h
new file mode 100644
index 000000000..ecd1f313b
--- /dev/null
+++ b/include/net/dn_route.h
@@ -0,0 +1,74 @@
+#ifndef _NET_DN_ROUTE_H
+#define _NET_DN_ROUTE_H
+
+/******************************************************************************
+ (c) 1995-1998 E.M. Serrat emserrat@geocities.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
+ 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.
+*******************************************************************************/
+/* dn_route.c functions prototyping */
+extern void dn_send_skb(struct sk_buff *);
+extern struct sk_buff *dn_alloc_skb(struct sock *sk, int size, int pri);
+extern int dn_route_output(struct sock *sk);
+
+/* Masks for flags field */
+#define DN_RT_F_PID 0x07 /* Mask for packet type */
+#define DN_RT_F_PF 0x80 /* Padding Follows */
+#define DN_RT_F_VER 0x40 /* Version =0 discard packet if ==1 */
+#define DN_RT_F_IE 0x20 /* Intra Ethernet, Reserved in short pkt */
+#define DN_RT_F_RTS 0x10 /* Packet is being returned to sender */
+#define DN_RT_F_RQR 0x08 /* Return packet to sender upon non-delivery */
+
+/* Mask for types of routing packets */
+#define DN_RT_PKT_MSK 0x06
+/* Types of routing packets */
+#define DN_RT_PKT_SHORT 0x02 /* Short routing packet */
+#define DN_RT_PKT_LONG 0x06 /* Long routing packet */
+
+/* Mask for control/routing selection */
+#define DN_RT_PKT_CNTL 0x01 /* Set to 1 if a control packet */
+/* Types of control packets */
+#define DN_RT_CNTL_MSK 0x0f /* Mask for control packets */
+#define DN_RT_PKT_INIT 0x01 /* Initialisation packet */
+#define DN_RT_PKT_VERI 0x03 /* Verification Message */
+#define DN_RT_PKT_HELO 0x05 /* Hello and Test Message */
+#define DN_RT_PKT_L1RT 0x07 /* Level 1 Routing Message */
+#define DN_RT_PKT_L2RT 0x09 /* Level 2 Routing Message */
+#define DN_RT_PKT_ERTH 0x0b /* Ethernet Router Hello */
+#define DN_RT_PKT_EEDH 0x0d /* Ethernet EndNode Hello */
+
+/* Values for info field in hello message */
+#define DN_RT_INFO_TYPE 0x03 /* Type mask */
+#define DN_RT_INFO_L1RT 0x02 /* L1 Router */
+#define DN_RT_INFO_L2RT 0x01 /* L2 Router */
+#define DN_RT_INFO_ENDN 0x03 /* EndNode */
+#define DN_RT_INFO_VERI 0x04 /* Verification Reqd. */
+#define DN_RT_INFO_RJCT 0x08 /* Reject Flag, Reserved */
+#define DN_RT_INFO_VFLD 0x10 /* Verification Failed, Reserved */
+#define DN_RT_INFO_NOML 0x20 /* No Multicast traffic accepted */
+#define DN_RT_INFO_BLKR 0x40 /* Blocking Requested */
+
+
+struct dn_route {
+ union {
+ struct dst_entry dst;
+ struct dn_route *rt_next;
+ } u;
+ unsigned short rt_saddr;
+ unsigned short rt_daddr;
+ int rt_iif;
+ int rt_oif;
+};
+
+extern void dn_route_init(void);
+extern void dn_route_cleanup(void);
+
+#endif /* _NET_DN_ROUTE_H */
diff --git a/include/net/irda/smc-ircc.h b/include/net/irda/smc-ircc.h
new file mode 100644
index 000000000..2c5cbf4fd
--- /dev/null
+++ b/include/net/irda/smc-ircc.h
@@ -0,0 +1,160 @@
+/*********************************************************************
+ *
+ * Filename: smc.h
+ * Version:
+ * Description:
+ * Status: Experimental.
+ * Author: Thomas Davis (tadavis@jps.net)
+ *
+ * Copyright (c) 1998, 1999 Thomas Davis (tadavis@jps.net>
+ * 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.
+ *
+ * Definitions for the SMC IrCC controller.
+ *
+ ********************************************************************/
+
+#ifndef SMC_IRCC_H
+#define SMC_IRCC_H
+
+#define UART_MASTER 0x07
+#define UART_MASTER_POWERDOWN 1<<7
+#define UART_MASTER_RESET 1<<6
+#define UART_MASTER_INT_EN 1<<5
+#define UART_MASTER_ERROR_RESET 1<<4
+
+/* Register block 0 */
+
+#define UART_IIR 0x01
+#define UART_IER 0x02
+#define UART_LSR 0x03
+#define UART_LCR_A 0x04
+#define UART_LCR_B 0x05
+#define UART_BSR 0x06
+
+#define UART_IIR_ACTIVE_FRAME 1<<7
+#define UART_IIR_EOM 1<<6
+#define UART_IIR_RAW_MODE 1<<5
+#define UART_IIR_FIFO 1<<4
+
+#define UART_IER_ACTIVE_FRAME 1<<7
+#define UART_IER_EOM 1<<6
+#define UART_IER_RAW_MODE 1<<5
+#define UART_IER_FIFO 1<<4
+
+#define UART_LSR_UNDERRUN 1<<7
+#define UART_LSR_OVERRUN 1<<6
+#define UART_LSR_FRAME_ERROR 1<<5
+#define UART_LSR_SIZE_ERROR 1<<4
+#define UART_LSR_CRC_ERROR 1<<3
+#define UART_LSR_FRAME_ABORT 1<<2
+
+#define UART_LCR_A_FIFO_RESET 1<<7
+#define UART_LCR_A_FAST 1<<6
+#define UART_LCR_A_GP_DATA 1<<5
+#define UART_LCR_A_RAW_TX 1<<4
+#define UART_LCR_A_RAW_RX 1<<3
+#define UART_LCR_A_ABORT 1<<2
+#define UART_LCR_A_DATA_DONE 1<<1
+
+#define UART_LCR_B_SCE_DISABLED 0x00<<6
+#define UART_LCR_B_SCE_TRANSMIT 0x01<<6
+#define UART_LCR_B_SCE_RECEIVE 0x02<<6
+#define UART_LCR_B_SCE_UNDEFINED 0x03<<6
+#define UART_LCR_B_SIP_ENABLE 1<<5
+#define UART_LCR_B_BRICK_WALL 1<<4
+
+#define UART_BSR_NOT_EMPTY 1<<7
+#define UART_BSR_FIFO_FULL 1<<6
+#define UART_BSR_TIMEOUT 1<<5
+
+/* Register block 1 */
+
+#define UART_SCE_CFGA 0x00
+#define UART_SCE_CFGB 0x01
+#define UART_FIFO_THRESHOLD 0x02
+
+#define UART_CFGA_AUX_IR 0x01<<7
+#define UART_CFGA_HALF_DUPLEX 0x01<<2
+#define UART_CFGA_TX_POLARITY 0x01<<1
+#define UART_CFGA_RX_POLARITY 0x01
+
+#define UART_CFGA_COM 0x00<<3
+#define UART_CFGA_IRDA_SIR_A 0x01<<3
+#define UART_CFGA_ASK_SIR 0x02<<3
+#define UART_CFGA_IRDA_SIR_B 0x03<<3
+#define UART_CFGA_IRDA_HDLC 0x04<<3
+#define UART_CFGA_IRDA_4PPM 0x05<<3
+#define UART_CFGA_CONSUMER 0x06<<3
+#define UART_CFGA_RAW_IR 0x07<<3
+#define UART_CFGA_OTHER 0x08<<3
+
+#define UART_IR_HDLC 0x04
+#define UART_IR_4PPM 0x01
+#define UART_IR_CONSUMER 0x02
+
+#define UART_CFGB_LOOPBACK 0x01<<5
+#define UART_CFGB_LPBCK_TX_CRC 0x01<<4
+#define UART_CFGB_NOWAIT 0x01<<3
+#define UART_CFGB_STRING_MOVE 0x01<<2
+#define UART_CFGB_DMA_BURST 0x01<<1
+#define UART_CFGB_DMA_ENABLE 0x01
+
+#define UART_CFGB_COM 0x00<<6
+#define UART_CFGB_IR 0x01<<6
+#define UART_CFGB_AUX 0x02<<6
+#define UART_CFGB_INACTIVE 0x03<<6
+
+/* Register block 2 - Consumer IR - not used */
+
+/* Register block 3 - Identification Registers! */
+
+#define UART_ID_HIGH 0x00 /* 0x10 */
+#define UART_ID_LOW 0x01 /* 0xB8 */
+#define UART_CHIP_ID 0x02 /* 0xF1 */
+#define UART_VERSION 0x03 /* 0x01 */
+#define UART_INTERFACE 0x04 /* low 4 = DMA, high 4 = IRQ */
+
+/* Register block 4 - IrDA */
+#define UART_CONTROL 0x00
+#define UART_BOF_COUNT_LO 0x01
+#define UART_BRICKWALL_CNT_LO 0x02
+#define UART_BRICKWALL_TX_CNT_HI 0x03
+#define UART_TX_SIZE_LO 0x04
+#define UART_RX_SIZE_HI 0x05
+#define UART_RX_SIZE_LO 0x06
+
+#define UART_1152 0x01<<7
+#define UART_CRC 0x01<<6
+
+/* For storing entries in the status FIFO */
+struct st_fifo_entry {
+ int status;
+ int len;
+};
+
+struct st_fifo {
+ struct st_fifo_entry entries[10];
+ int head;
+ int tail;
+ int len;
+};
+
+/* Private data for each instance */
+struct ircc_cb {
+ struct st_fifo st_fifo;
+
+ int tx_buff_offsets[10]; /* Offsets between frames in tx_buff */
+ int tx_len; /* Number of frames in tx_buff */
+
+ struct irda_device idev;
+};
+
+#endif
diff --git a/include/net/irda/toshoboe.h b/include/net/irda/toshoboe.h
new file mode 100644
index 000000000..b2f5b953b
--- /dev/null
+++ b/include/net/irda/toshoboe.h
@@ -0,0 +1,165 @@
+/*********************************************************************
+ *
+ * Filename: toshoboe.h
+ * Version: 0.1
+ * Description: Driver for the Toshiba OBOE (or type-O)
+ * 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
+ *
+ ********************************************************************/
+
+/*
+ * $Log: toshoboe.h,v $
+ * Revision 1.2 1999/05/09 01:43:08 root
+ * *** empty log message ***
+ *
+ * Revision 1.1 1999/05/09 01:25:58 root
+ * Initial revision
+ *
+ */
+
+#ifndef TOSHOBOE_H
+#define TOSHOBOE_H
+
+/* Registers */
+/*Receive and transmit task registers (read only) */
+#define OBOE_RCVT (0x00+(self->base))
+#define OBOE_XMTT (0x01+(self->base))
+#define OBOE_XMTT_OFFSET 0x40
+
+/*Page pointers to the TaskFile structure */
+#define OBOE_TFP2 (0x02+(self->base))
+#define OBOE_TFP0 (0x04+(self->base))
+#define OBOE_TFP1 (0x05+(self->base))
+
+/*Dunno */
+#define OBOE_REG_3 (0x03+(self->base))
+
+/*Number of tasks to use in Xmit and Recv queues */
+#define OBOE_NTR (0x07+(self->base))
+#define OBOE_NTR_XMIT4 0x00
+#define OBOE_NTR_XMIT8 0x10
+#define OBOE_NTR_XMIT16 0x30
+#define OBOE_NTR_XMIT32 0x70
+#define OBOE_NTR_XMIT64 0xf0
+#define OBOE_NTR_RECV4 0x00
+#define OBOE_NTR_RECV8 0x01
+#define OBOE_NTR_RECV6 0x03
+#define OBOE_NTR_RECV32 0x07
+#define OBOE_NTR_RECV64 0x0f
+
+/* Dunno */
+#define OBOE_REG_9 (0x09+(self->base))
+
+/* Interrupt Status Register */
+#define OBOE_ISR (0x0c+(self->base))
+#define OBOE_ISR_TXDONE 0x80
+#define OBOE_ISR_RXDONE 0x40
+#define OBOE_ISR_20 0x20
+#define OBOE_ISR_10 0x10
+#define OBOE_ISR_8 0x08 /*This is collision or parity or something */
+#define OBOE_ISR_4 0x08
+#define OBOE_ISR_2 0x08
+#define OBOE_ISR_1 0x08
+
+/*Dunno */
+#define OBOE_REG_D (0x0d+(self->base))
+
+/*Register Lock Register */
+#define OBOE_LOCK ((self->base)+0x0e)
+
+
+
+/*Speed control registers */
+#define OBOE_PMDL (0x10+(self->base))
+#define OBOE_PMDL_SIR 0x18
+#define OBOE_PMDL_MIR 0xa0
+#define OBOE_PMDL_FIR 0x40
+
+#define OBOE_SMDL (0x18+(self->base))
+#define OBOE_SMDL_SIR 0x20
+#define OBOE_SMDL_MIR 0x01
+#define OBOE_SMDL_FIR 0x0f
+
+#define OBOE_UDIV (0x19+(self->base))
+
+/*Dunno */
+#define OBOE_REG_11 (0x11+(self->base))
+
+/*Chip Reset Register */
+#define OBOE_RST (0x15+(self->base))
+#define OBOE_RST_WRAP 0x8
+
+/*Dunno */
+#define OBOE_REG_1A (0x1a+(self->base))
+#define OBOE_REG_1B (0x1b+(self->base))
+
+/* The PCI ID of the OBOE chip */
+#ifndef PCI_DEVICE_ID_FIR701
+#define PCI_DEVICE_ID_FIR701 0x0701
+#endif
+
+typedef unsigned int dword;
+typedef unsigned short int word;
+typedef unsigned char byte;
+typedef dword Paddr;
+
+struct OboeTask
+ {
+ __u16 len;
+ __u8 unused;
+ __u8 control;
+ __u32 buffer;
+ };
+
+#define OBOE_NTASKS 64
+
+struct OboeTaskFile
+ {
+ struct OboeTask recv[OBOE_NTASKS];
+ struct OboeTask xmit[OBOE_NTASKS];
+ };
+
+#define OBOE_TASK_BUF_LEN (sizeof(struct OboeTaskFile) << 1)
+
+/*These set the number of slots in use */
+#define TX_SLOTS 4
+#define RX_SLOTS 4
+
+/* You need also to change this, toshiba uses 4,8 and 4,4 */
+/* It makes no difference if you are only going to use ONETASK mode */
+/* remember each buffer use XX_BUF_SZ more _PHYSICAL_ memory */
+#define OBOE_NTR_VAL (OBOE_NTR_XMIT4 | OBOE_NTR_RECV4)
+
+struct toshoboe_cb
+ {
+ struct irda_device idev; /*IRDA device */
+ struct pci_dev *pdev; /*PCI device */
+ int base; /*IO base */
+ int txpending; /*how many tx's are pending */
+ int txs, rxs; /*Which slots are we at */
+ void *taskfilebuf; /*The unaligned taskfile buffer */
+ struct OboeTaskFile *taskfile; /*The taskfile */
+ void *xmit_bufs[TX_SLOTS]; /*The buffers */
+ void *recv_bufs[RX_SLOTS];
+ };
+
+
+#endif
+
+
diff --git a/include/video/fbcon-vga-planes.h b/include/video/fbcon-vga-planes.h
new file mode 100644
index 000000000..31578d41c
--- /dev/null
+++ b/include/video/fbcon-vga-planes.h
@@ -0,0 +1,37 @@
+/*
+ * FBcon low-level driver for VGA 4-plane modes
+ */
+
+#ifndef _VIDEO_FBCON_VGA_PLANES_H
+#define _VIDEO_FBCON_VGA_PLANES_H
+
+#include <linux/config.h>
+
+#ifdef MODULE
+#if defined(CONFIG_FBCON_VGA_PLANES) || defined(CONFIG_FBCON_VGA_PLANES_MODULE)
+#define FBCON_HAS_VGA_PLANES
+#endif
+#else
+#if defined(CONFIG_FBCON_VGA_PLANES)
+#define FBCON_HAS_VGA_PLANES
+#endif
+#endif
+
+extern struct display_switch fbcon_vga_planes;
+extern struct display_switch fbcon_ega_planes;
+extern void fbcon_vga_planes_setup(struct display *p);
+extern void fbcon_vga_planes_bmove(struct display *p, int sy, int sx, int dy, int dx,
+ int height, int width);
+extern void fbcon_vga_planes_clear(struct vc_data *conp, struct display *p, int sy,
+ int sx, int height, int width);
+extern void fbcon_vga_planes_putc(struct vc_data *conp, struct display *p, int c,
+ int yy, int xx);
+extern void fbcon_ega_planes_putc(struct vc_data *conp, struct display *p, int c,
+ int yy, int xx);
+extern void fbcon_vga_planes_putcs(struct vc_data *conp, struct display *p,
+ const unsigned short *s, int count, int yy, int xx);
+extern void fbcon_ega_planes_putcs(struct vc_data *conp, struct display *p,
+ const unsigned short *s, int count, int yy, int xx);
+extern void fbcon_vga_planes_revc(struct display *p, int xx, int yy);
+
+#endif /* _VIDEO_FBCON_VGA_PLANES_H */
diff --git a/net/decnet/Config.in b/net/decnet/Config.in
new file mode 100644
index 000000000..ac12d2aca
--- /dev/null
+++ b/net/decnet/Config.in
@@ -0,0 +1,13 @@
+#
+# DECnet configuration
+#
+bool 'DECnet: SIOCGIFCONF support' CONFIG_DECNET_SIOCGIFCONF
+bool 'DECnet: router support (VERY VERY EXPERIMENTAL)' CONFIG_DECNET_ROUTER
+bool 'DECnet: raw socket support' CONFIG_DECNET_RAW
+#bool 'DECnet: MOP support' CONFIG_DECNET_MOP
+#if [ "$CONFIG_FIREWALL" = "y" ]; then
+# bool 'DECnet: firewall support' CONFIG_DECNET_FW
+# if [ "$CONFIG_DECNET_FW" = "y" ]; then
+# bool 'DECnet: firewall netlink support' CONFIG_DECNET_FIREWALL_NETLINK
+# fi
+#fi
diff --git a/net/decnet/Makefile b/net/decnet/Makefile
new file mode 100644
index 000000000..d99da95a1
--- /dev/null
+++ b/net/decnet/Makefile
@@ -0,0 +1,30 @@
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := decnet.o
+O_OBJS := af_decnet.o dn_nsp_in.o dn_nsp_out.o dn_route.o dn_dev.o dn_neigh.o dn_timer.o
+M_OBJS := $(O_TARGET)
+
+ifeq ($(CONFIG_DECNET_ROUTER),y)
+O_OBJS += dn_fib.o
+endif
+
+ifeq ($(CONFIG_DECNET_RAW),y)
+O_OBJS += dn_raw.o
+endif
+
+#ifeq ($(CONFIG_DECNET_MOP),y)
+#O_OBJS += dn_mop.o
+#endif
+
+ifeq ($(CONFIG_DECNET_FW),y)
+O_OBJS += dn_fw.o
+endif
+
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_decnet.o
+endif
+
+include $(TOPDIR)/Rules.make
+
+tar:
+ tar -cvf /dev/f1 .
diff --git a/net/decnet/TODO b/net/decnet/TODO
new file mode 100644
index 000000000..48ea5e212
--- /dev/null
+++ b/net/decnet/TODO
@@ -0,0 +1,59 @@
+Steve's quick list of things that need finishing off:
+[they are in no particular order and range from the trivial to the long winded]
+
+ o Proper timeouts on each neighbour (in routing mode) rather than
+ just the 60 second On-Ethernet cache value.
+
+ o MOP support (probably as part of Raw sockets) [hooks in]
+
+ o Routing stuff in dn_fib.c
+
+ o Misc. get/set_sockopt() functions [done for the time being, more later]
+
+ o Support for X.25 linklayer
+
+ o Support for DDCMP link layer
+
+ o The DDCMP device itself
+
+ o PPP support (rfc1762)
+
+ o sendmsg() in the raw socket layer
+
+ o Better filtering of traffic in raw sockets
+
+ o Fix /proc for raw sockets
+
+ o Lots of testing with real applications
+
+ o Verify errors etc. against POSIX 1003.1g (draft)
+
+ o Using send/recvmsg() to get at connect/disconnect data (POSIX 1003.1g)
+ [maybe this should be done at socket level... the control data in the
+ send/recvmsg() calls should simply be a vector of set/getsockopt()
+ calls]
+
+ o recvmsg() to optionally report remote address.
+
+ o check MSG_TRUNC, MSG_CTRUNC are set where they should be.
+
+ o Work out if I really need support for rtnetlink "link" messages and if
+ so how they should be handled.
+
+ o More rtnetlink "route" message support & testing of this code
+
+ o Routing ioctl() support
+
+ o Start to hack together user level software and add more DECnet support
+ in ifconfig for example. Also a DECnet equivalent to Alexey's ip config
+ tool is required. Hopefully I can steal some code from that.
+
+ o Sort out MSG_EOR in sendmsg.... should it be used, or is each transmission
+ a seperate message ? What about when you get interrupted by a signal ?
+
+ o Fix conninit_rx to check out each CI before queuing it
+
+ o Work out which errors we can return from conninit_rx, and how to do it
+
+ o Check out receiving of errors in the light of what conninit_rx can return
+
diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c
new file mode 100644
index 000000000..9d355f752
--- /dev/null
+++ b/net/decnet/af_decnet.c
@@ -0,0 +1,2192 @@
+
+/*
+ * DECnet An implementation of the DECnet protocol suite for the LINUX
+ * operating system. DECnet is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * DECnet Socket Layer Interface
+ *
+ * Authors: Eduardo Marcelo Serrat <emserrat@geocities.com>
+ * Patrick Caulfield <patrick@pandh.demon.co.uk>
+ *
+ * Changes:
+ * Steve Whitehouse: Copied from Eduardo Serrat and Patrick Caulfield's
+ * version of the code. Original copyright preserved
+ * below.
+ * Steve Whitehouse: Some bug fixes, cleaning up some code to make it
+ * compatible with my routing layer.
+ * Steve Whitehouse: Merging changes from Eduardo Serrat and Patrick
+ * Caulfield.
+ * Steve Whitehouse: Further bug fixes, checking module code still works
+ * with new routing layer.
+ * Steve Whitehouse: Additional set/get_sockopt() calls.
+ * Steve Whitehouse: Fixed TIOCINQ ioctl to be same as Eduardo's new
+ * code.
+ * Steve Whitehouse: recvmsg() changed to try and behave in a POSIX like
+ * way. Didn't manage it entirely, but its better.
+ * Steve Whitehouse: ditto for sendmsg().
+ * Steve Whitehouse: A selection of bug fixes to various things.
+ * Steve Whitehouse: Added TIOCOUTQ ioctl.
+ * Steve Whitehouse: Fixes to username2sockaddr & sockaddr2username.
+ * Steve Whitehouse: Fixes to connect() error returns.
+ * Patrick Caulfield: Fixes to delayed acceptance logic.
+ */
+
+
+/******************************************************************************
+ (c) 1995-1998 E.M. Serrat emserrat@geocities.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
+ 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.
+
+HISTORY:
+
+Version Kernel Date Author/Comments
+------- ------ ---- ---------------
+Version 0.0.1 2.0.30 01-dic-97 Eduardo Marcelo Serrat
+ (emserrat@geocities.com)
+
+ First Development of DECnet Socket La-
+ yer for Linux. Only supports outgoing
+ connections.
+
+Version 0.0.2 2.1.105 20-jun-98 Patrick J. Caulfield
+ (patrick@pandh.demon.co.uk)
+
+ Port to new kernel development version.
+
+Version 0.0.3 2.1.106 25-jun-98 Eduardo Marcelo Serrat
+ (emserrat@geocities.com)
+ _
+ Added support for incoming connections
+ so we can start developing server apps
+ on Linux.
+ -
+ Module Support
+Version 0.0.4 2.1.109 21-jul-98 Eduardo Marcelo Serrat
+ (emserrat@geocities.com)
+ _
+ Added support for X11R6.4. Now we can
+ use DECnet transport for X on Linux!!!
+ -
+Version 0.0.5 2.1.110 01-aug-98 Eduardo Marcelo Serrat
+ (emserrat@geocities.com)
+ Removed bugs on flow control
+ Removed bugs on incoming accessdata
+ order
+ -
+Version 0.0.6 2.1.110 07-aug-98 Eduardo Marcelo Serrat
+ dn_recvmsg fixes
+
+ Patrick J. Caulfield
+ dn_bind fixes
+*******************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/inet.h>
+#include <linux/route.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <net/neighbour.h>
+#include <net/dst.h>
+#include <net/dn.h>
+#include <net/dn_nsp.h>
+#include <net/dn_dev.h>
+#include <net/dn_route.h>
+#include <net/dn_fib.h>
+#include <net/dn_raw.h>
+#include <net/dn_neigh.h>
+
+#define MAX(a,b) ((a)>(b)?(a):(b))
+
+static void dn_keepalive(struct sock *sk);
+
+/*
+ * decnet_address is kept in network order, decnet_ether_address is kept
+ * as a string of bytes.
+ */
+dn_address decnet_address = 0;
+unsigned char decnet_ether_address[ETH_ALEN] = { 0xAA, 0x00, 0x04, 0x00, 0x00, 0x00 };
+int decnet_node_type = DN_RT_INFO_ENDN;
+
+static struct proto_ops dn_proto_ops;
+static struct sock *dn_sklist = NULL;
+static struct sock *dn_wild_sk = NULL;
+
+static int _dn_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen, int flags);
+static int _dn_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen, int flags);
+
+int dn_sockaddr2username(struct sockaddr_dn *sdn, unsigned char *buf, unsigned char type)
+{
+ int len = 2;
+
+ *buf++ = type;
+
+ switch(type) {
+ case 0:
+ *buf++ = sdn->sdn_objnum;
+ break;
+ case 1:
+ *buf++ = 0;
+ *buf++ = sdn->sdn_objnamel;
+ memcpy(buf, sdn->sdn_objname, sdn->sdn_objnamel);
+ len = 3 + sdn->sdn_objnamel;
+ break;
+ case 2:
+ memset(buf, 0, 5);
+ buf += 5;
+ *buf++ = sdn->sdn_objnamel;
+ memcpy(buf, sdn->sdn_objname, sdn->sdn_objnamel);
+ len = 7 + sdn->sdn_objnamel;
+ break;
+ }
+
+ return len;
+}
+
+/*
+ * On reception of usernames, we handle types 1 and 0 for destination
+ * addresses only. Types 2 and 4 are used for source addresses, but the
+ * UIC, GIC are ignored and they are both treated the same way. Type 3
+ * is never used as I've no idea what its purpose might be or what its
+ * format is.
+ */
+int dn_username2sockaddr(unsigned char *data, int len, struct sockaddr_dn *sdn, unsigned char *fmt)
+{
+ unsigned char type;
+ int size = len;
+ int namel = 12;
+
+ sdn->sdn_objnum = 0;
+ sdn->sdn_objnamel = 0;
+ memset(sdn->sdn_objname, 0, DN_MAXOBJL);
+
+ if (len < 2)
+ return -1;
+
+ len -= 2;
+ *fmt = *data++;
+ type = *data++;
+
+ switch(*fmt) {
+ case 0:
+ sdn->sdn_objnum = type;
+ return 2;
+ case 1:
+ namel = 16;
+ break;
+ case 2:
+ len -= 4;
+ data += 4;
+ break;
+ case 4:
+ len -= 8;
+ data += 8;
+ break;
+ default:
+ return -1;
+ }
+
+ len -= 1;
+
+ if (len < 0)
+ return -1;
+
+ sdn->sdn_objnamel = *data++;
+ len -= sdn->sdn_objnamel;
+
+ if ((len < 0) || (sdn->sdn_objnamel > namel))
+ return -1;
+
+ memcpy(sdn->sdn_objname, data, sdn->sdn_objnamel);
+
+ return size - len;
+}
+
+struct sock *dn_sklist_find_listener(struct sockaddr_dn *addr)
+{
+ struct sock *sk;
+
+ for(sk = dn_sklist; sk != NULL; sk = sk->next) {
+ struct dn_scp *scp = &sk->protinfo.dn;
+ if (sk->state != TCP_LISTEN)
+ continue;
+ if (scp->addr.sdn_objnum) {
+ if (scp->addr.sdn_objnum != addr->sdn_objnum)
+ continue;
+ } else {
+ if (addr->sdn_objnum)
+ continue;
+ if (scp->addr.sdn_objnamel != addr->sdn_objnamel)
+ continue;
+ if (memcmp(scp->addr.sdn_objname, addr->sdn_objname, addr->sdn_objnamel) != 0)
+ continue;
+ }
+ return sk;
+ }
+
+ return (dn_wild_sk && (dn_wild_sk->state == TCP_LISTEN)) ? dn_wild_sk : NULL;
+}
+
+struct sock *dn_sklist_find(unsigned short port)
+{
+ struct sock *s;
+
+ for (s = dn_sklist; s != NULL; s = s->next) {
+ if (s->protinfo.dn.addrloc == port) {
+ return s;
+ }
+ }
+
+ return NULL;
+}
+
+static struct sock *dn_sklist_find_by_objnum(unsigned char objnum)
+{
+ struct sock *s;
+
+ for (s = dn_sklist; s != NULL; s = s->next) {
+ if ((s->protinfo.dn.addr.sdn_objnum == objnum) &&
+ (s->state == TCP_LISTEN)) {
+ return s;
+ }
+ }
+ return NULL;
+}
+
+static struct sock *dn_sklist_find_by_name(char *name)
+{
+ struct sock *s;
+
+ for (s = dn_sklist; s != NULL; s = s->next) {
+ if (s->protinfo.dn.addr.sdn_objnum)
+ continue;
+ if ((memcmp(s->protinfo.dn.addr.sdn_objname,name,
+ s->protinfo.dn.addr.sdn_objnamel) == 0)
+ && (s->state == TCP_LISTEN)) {
+ return s;
+ }
+ }
+ return NULL;
+}
+
+
+struct sock *dn_find_by_skb(struct sk_buff *skb)
+{
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ struct sock *sk;
+ struct dn_scp *scp;
+
+ for(sk = dn_sklist; sk != NULL; sk = sk->next) {
+ scp = &sk->protinfo.dn;
+ if (cb->src != dn_saddr2dn(&scp->peer))
+ continue;
+ if (cb->dst_port != scp->addrloc)
+ continue;
+ if (scp->addrrem && (cb->src_port != scp->addrrem))
+ continue;
+ break;
+ }
+
+ return sk;
+}
+
+
+unsigned short dn_alloc_port(void)
+{
+ struct sock *sk;
+ static unsigned short dn_port = 0x2000;
+ short port;
+
+ start_bh_atomic();
+
+ do {
+ port = dn_port++;
+ sk = dn_sklist_find(port);
+ } while((sk != NULL) || (port == 0));
+
+ end_bh_atomic();
+
+ return dn_htons(port);
+};
+
+
+static void dn_destruct(struct sock *sk)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+
+ skb_queue_purge(&scp->data_xmit_queue);
+ skb_queue_purge(&scp->other_xmit_queue);
+ skb_queue_purge(&scp->other_receive_queue);
+
+ dst_release(xchg(&sk->dst_cache, NULL));
+
+ MOD_DEC_USE_COUNT;
+}
+
+struct sock *dn_alloc_sock(struct socket *sock, int flags)
+{
+ struct sock *sk;
+ struct dn_scp *scp;
+
+ if ((sk = sk_alloc(PF_DECnet, flags, 1)) == NULL)
+ goto no_sock;
+
+ if (sock) {
+#ifdef CONFIG_DECNET_RAW
+ if (sock->type == SOCK_RAW)
+ sock->ops = &dn_raw_proto_ops;
+ else
+#endif /* CONFIG_DECNET_RAW */
+ sock->ops = &dn_proto_ops;
+ }
+ sock_init_data(sock,sk);
+ scp = &sk->protinfo.dn;
+
+ sk->backlog_rcv = dn_nsp_backlog_rcv;
+ sk->destruct = dn_destruct;
+ sk->no_check = 1;
+ sk->family = PF_DECnet;
+ sk->protocol = 0;
+
+ /* Initialization of DECnet Session Control Port */
+ scp->state = DN_O; /* Open */
+ scp->numdat = 1; /* Next data seg to tx */
+ scp->numoth = 1; /* Next oth data to tx */
+ scp->ackxmt_dat = 0; /* Last data seg ack'ed */
+ scp->ackxmt_oth = 0; /* Last oth data ack'ed */
+ scp->ackrcv_dat = 0; /* Highest data ack recv*/
+ scp->ackrcv_oth = 0; /* Last oth data ack rec*/
+ scp->flowrem_sw = DN_SEND;
+ scp->flowloc_sw = DN_SEND;
+ scp->accept_mode = ACC_IMMED;
+ scp->addr.sdn_family = AF_DECnet;
+ scp->peer.sdn_family = AF_DECnet;
+ scp->accessdata.acc_accl = 5;
+ memcpy(scp->accessdata.acc_acc, "LINUX", 5);
+ scp->mss = 1460;
+
+ scp->snd_window = NSP_MIN_WINDOW;
+ scp->nsp_srtt = NSP_INITIAL_SRTT;
+ scp->nsp_rttvar = NSP_INITIAL_RTTVAR;
+ scp->nsp_rxtshift = 0;
+
+ skb_queue_head_init(&scp->data_xmit_queue);
+ skb_queue_head_init(&scp->other_xmit_queue);
+ skb_queue_head_init(&scp->other_receive_queue);
+
+ scp->persist = 0;
+ scp->persist_fxn = NULL;
+ scp->keepalive = 10 * HZ;
+ scp->keepalive_fxn = dn_keepalive;
+
+ init_timer(&scp->delack_timer);
+ scp->delack_pending = 0;
+ scp->delack_fxn = dn_nsp_delayed_ack;
+
+ dn_start_slow_timer(sk);
+
+ MOD_INC_USE_COUNT;
+
+ return sk;
+no_sock:
+ return NULL;
+}
+
+/*
+ * Keepalive timer.
+ * FIXME: Should respond to SO_KEEPALIVE etc.
+ */
+static void dn_keepalive(struct sock *sk)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+
+ /*
+ * By checking the other_data transmit queue is empty
+ * we are double checking that we are not sending too
+ * many of these keepalive frames.
+ */
+ if (skb_queue_len(&scp->other_xmit_queue) == 0)
+ dn_nsp_send_lnk(sk, DN_NOCHANGE);
+}
+
+
+/*
+ * Timer for shutdown/destroyed sockets.
+ * When socket is dead & no packets have been sent for a
+ * certain amount of time, they are removed by this
+ * routine. Also takes care of sending out DI & DC
+ * frames at correct times. This is called by both
+ * socket level and interrupt driven code.
+ */
+static int dn_destroy_timer(struct sock *sk)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+
+ scp->persist = dn_nsp_persist(sk);
+
+ switch(scp->state) {
+ case DN_DI:
+ /* printk(KERN_DEBUG "dn_destroy_timer: DI\n"); */
+ dn_send_disc(sk, NSP_DISCINIT, 0);
+ if (scp->nsp_rxtshift >= decnet_di_count)
+ scp->state = DN_CN;
+ return 0;
+
+ case DN_DR:
+ /* printk(KERN_DEBUG "dn_destroy_timer: DR\n"); */
+ dn_send_disc(sk, NSP_DISCINIT, 0);
+ if (scp->nsp_rxtshift >= decnet_dr_count)
+ scp->state = DN_DRC;
+ return 0;
+
+ case DN_DN:
+ if (scp->nsp_rxtshift < decnet_dn_count) {
+ /* printk(KERN_DEBUG "dn_destroy_timer: DN\n"); */
+ dn_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC);
+ return 0;
+ }
+ }
+
+ scp->persist = (HZ * decnet_time_wait);
+
+/* printk(KERN_DEBUG "dn_destroy_timer: testing dead\n"); */
+
+ if (sk->socket)
+ return 0;
+
+ dn_stop_fast_timer(sk); /* unlikely, but possible that this is runninng */
+ if ((jiffies - scp->stamp) >= (HZ * decnet_time_wait)) {
+ sklist_destroy_socket(&dn_sklist, sk);
+ return 1;
+ }
+
+ /*printk(KERN_DEBUG "dn_destroy_timer: dead 'n' waiting...\n"); */
+
+ return 0;
+}
+
+void dn_destroy_sock(struct sock *sk)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+
+ if (sk->dead)
+ return;
+
+ sk->dead = 1;
+ scp->nsp_rxtshift = 0; /* reset back off */
+
+ if (sk->socket) {
+ if (sk->socket->state != SS_UNCONNECTED)
+ sk->socket->state = SS_DISCONNECTING;
+ }
+
+ sk->state = TCP_CLOSE;
+
+ switch(scp->state) {
+ case DN_DN:
+ dn_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC);
+ scp->persist_fxn = dn_destroy_timer;
+ scp->persist = dn_nsp_persist(sk);
+ break;
+ case DN_CD:
+ case DN_CR:
+ scp->state = DN_DR;
+ goto disc_reject;
+ case DN_RUN:
+ scp->state = DN_DI;
+ case DN_DI:
+ case DN_DR:
+disc_reject:
+ dn_send_disc(sk, NSP_DISCINIT, 0);
+ case DN_NC:
+ case DN_NR:
+ case DN_RJ:
+ case DN_DIC:
+ case DN_CN:
+ case DN_DRC:
+ case DN_CI:
+ scp->persist_fxn = dn_destroy_timer;
+ scp->persist = dn_nsp_persist(sk);
+ break;
+ default:
+ printk(KERN_DEBUG "DECnet: dn_destroy_sock passed socket in invalid state\n");
+ case DN_O:
+ start_bh_atomic();
+ dn_stop_fast_timer(sk);
+ dn_stop_slow_timer(sk);
+
+ if (sk == dn_wild_sk) {
+ dn_wild_sk = NULL;
+ sklist_destroy_socket(NULL, sk);
+ } else {
+ sklist_destroy_socket(&dn_sklist, sk);
+ }
+
+ end_bh_atomic();
+ break;
+ }
+}
+
+char *dn_addr2asc(dn_address addr, char *buf)
+{
+ unsigned short node, area;
+
+ node = addr & 0x03ff;
+ area = addr >> 10;
+ sprintf(buf, "%hd.%hd", area, node);
+
+ return buf;
+}
+
+
+static char *dn_state2asc(unsigned char state)
+{
+ switch(state) {
+ case DN_O:
+ return "OPEN";
+ case DN_CR:
+ return " CR";
+ case DN_DR:
+ return " DR";
+ case DN_DRC:
+ return " DRC";
+ case DN_CC:
+ return " CC";
+ case DN_CI:
+ return " CI";
+ case DN_NR:
+ return " NR";
+ case DN_NC:
+ return " NC";
+ case DN_CD:
+ return " CD";
+ case DN_RJ:
+ return " RJ";
+ case DN_RUN:
+ return " RUN";
+ case DN_DI:
+ return " DI";
+ case DN_DIC:
+ return " DIC";
+ case DN_DN:
+ return " DN";
+ case DN_CL:
+ return " CL";
+ case DN_CN:
+ return " CN";
+ }
+
+ return "????";
+}
+
+static int dn_get_info(char *buffer, char **start, off_t offset,
+ int length, int dummy)
+{
+ struct sock *sk;
+ int len = 0;
+ off_t pos = 0;
+ off_t begin = 0;
+ char buf[DN_ASCBUF_LEN];
+
+ len += sprintf(buffer+len,"%-8s%-7s%-7s%-7s%-5s%-13s%-13s\n",
+ "Remote","Source","Remote","Object","Link ",
+ " Data Packets ","Link Packets");
+ len += sprintf(buffer+len,"%-8s%-7s%-7s%-7s%-5s%-13s%-13s\n\n",
+ "Node ","Port ","Port ","Number","State",
+ " Out In "," Out In");
+ start_bh_atomic();
+ for (sk = dn_sklist; sk != NULL; sk = sk->next) {
+ len += sprintf(buffer+len,
+ "%6s %04X %04X %6d %4s %6d %6d %6d %6d\n",
+
+ dn_addr2asc(dn_ntohs(dn_saddr2dn(&sk->protinfo.dn.peer)), buf),
+ sk->protinfo.dn.addrloc,sk->protinfo.dn.addrrem,
+ sk->protinfo.dn.addr.sdn_objnum,
+ dn_state2asc(sk->protinfo.dn.state),
+ sk->protinfo.dn.numdat, sk->protinfo.dn.numdat_rcv,
+ sk->protinfo.dn.numoth, sk->protinfo.dn.numoth_rcv);
+
+ pos = begin + len;
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset+length)
+ break;
+ }
+ end_bh_atomic();
+
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
+
+ if (len > length)
+ len = length;
+
+ return len;
+}
+
+static int dn_create(struct socket *sock, int protocol)
+{
+ struct sock *sk;
+
+ switch(sock->type) {
+ case SOCK_SEQPACKET:
+ if (protocol != DNPROTO_NSP)
+ return -EPROTONOSUPPORT;
+ break;
+ case SOCK_STREAM:
+ break;
+#ifdef CONFIG_DECNET_RAW
+ case SOCK_RAW:
+ if ((protocol != DNPROTO_NSP) &&
+#ifdef CONFIG_DECNET_MOP
+ (protocol != DNPROTO_MOP) &&
+#endif /* CONFIG_DECNET_MOP */
+ (protocol != DNPROTO_ROU))
+ return -EPROTONOSUPPORT;
+ break;
+#endif /* CONFIG_DECNET_RAW */
+ default:
+ return -ESOCKTNOSUPPORT;
+ }
+
+
+ if ((sk = dn_alloc_sock(sock, GFP_KERNEL)) == NULL)
+ return -ENOBUFS;
+
+ sk->protocol = protocol;
+
+ return 0;
+}
+
+
+static int
+dn_release(struct socket *sock, struct socket *peer)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk) {
+ lock_sock(sk);
+ sock->sk = NULL;
+ sk->socket = NULL;
+ dn_destroy_sock(sk);
+ release_sock(sk);
+ }
+
+ return 0;
+}
+
+static int dn_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+ struct sock *sk = sock->sk;
+ struct sockaddr_dn *saddr = (struct sockaddr_dn *)uaddr;
+
+ if (sk->zapped == 0)
+ return -EINVAL;
+
+ if (addr_len != sizeof(struct sockaddr_dn))
+ return -EINVAL;
+
+ if (saddr->sdn_family != AF_DECnet)
+ return -EINVAL;
+
+ if (saddr->sdn_objnum && !suser())
+ return -EPERM;
+
+ if (!saddr->sdn_objname && (saddr->sdn_objnamel > DN_MAXOBJL))
+ return -EINVAL;
+
+ if (saddr->sdn_flags & ~SDF_WILD)
+ return -EINVAL;
+
+ if ((saddr->sdn_flags & SDF_WILD) && !suser())
+ return -EPERM;
+
+ start_bh_atomic();
+
+ if (saddr->sdn_flags & SDF_WILD) {
+ if (dn_wild_sk) {
+ end_bh_atomic();
+ return -EADDRINUSE;
+ }
+ dn_wild_sk = sk;
+ sk->zapped = 0;
+ memcpy(&sk->protinfo.dn.addr, saddr, addr_len);
+ end_bh_atomic();
+ return 0;
+ }
+
+ if (saddr->sdn_objnum && dn_sklist_find_by_objnum(saddr->sdn_objnum)) {
+ end_bh_atomic();
+ return -EADDRINUSE;
+ }
+
+ if (!saddr->sdn_objnum) {
+ if (dn_sklist_find_by_name(saddr->sdn_objname)) {
+ end_bh_atomic();
+ return -EADDRINUSE;
+ }
+ }
+
+ memcpy(&sk->protinfo.dn.addr, saddr, addr_len);
+ sk->zapped = 0;
+ sklist_insert_socket(&dn_sklist, sk);
+ end_bh_atomic();
+
+ return 0;
+}
+
+
+static int dn_auto_bind(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ struct dn_scp *scp = &sk->protinfo.dn;
+
+ sk->zapped = 0;
+
+ scp->addr.sdn_flags = 0;
+ scp->addr.sdn_objnum = 0;
+
+ /*
+ * This stuff is to keep compatibility with Eduardo's
+ * patch. I hope I can dispense with it shortly...
+ */
+ if ((scp->accessdata.acc_accl != 0) &&
+ (scp->accessdata.acc_accl <= 12)) {
+
+ scp->addr.sdn_objnamel = scp->accessdata.acc_accl;
+ memcpy(scp->addr.sdn_objname, scp->accessdata.acc_acc, scp->addr.sdn_objnamel);
+
+ scp->accessdata.acc_accl = 0;
+ memset(scp->accessdata.acc_acc, 0, 40);
+ }
+
+ scp->addr.sdn_add.a_len = 2;
+ *(dn_address *)scp->addr.sdn_add.a_addr = decnet_address;
+
+ sklist_insert_socket(&dn_sklist, sk);
+
+ return 0;
+}
+
+
+static int dn_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
+{
+ struct sockaddr_dn *addr = (struct sockaddr_dn *)uaddr;
+ struct sock *sk = sock->sk;
+ int err = -EISCONN;
+
+ lock_sock(sk);
+
+ if (sock->state == SS_CONNECTED)
+ goto out;
+
+ if (sock->state == SS_CONNECTING) {
+ err = 0;
+ if (sk->state == TCP_ESTABLISHED)
+ goto out;
+
+ err = -ECONNREFUSED;
+ if (sk->state == TCP_CLOSE)
+ goto out;
+ }
+
+ err = -EINVAL;
+ if (sk->protinfo.dn.state != DN_O)
+ goto out;
+
+ if (addr_len != sizeof(struct sockaddr_dn))
+ goto out;
+
+ if (addr->sdn_family != AF_DECnet)
+ goto out;
+
+ if (addr->sdn_flags & SDF_WILD)
+ goto out;
+
+ err = -EADDRNOTAVAIL;
+ if (sk->zapped && (err = dn_auto_bind(sock)))
+ goto out;
+
+ memcpy(&sk->protinfo.dn.peer, addr, addr_len);
+
+ err = -EHOSTUNREACH;
+ if (dn_route_output(sk) < 0)
+ goto out;
+
+ sk->state = TCP_SYN_SENT;
+ sock->state = SS_CONNECTING;
+ sk->protinfo.dn.state = DN_CI;
+
+ dn_nsp_send_conninit(sk, NSP_CI);
+
+ err = -EINPROGRESS;
+ if ((sk->state == TCP_SYN_SENT) && (flags & O_NONBLOCK))
+ goto out;
+
+ while(sk->state == TCP_SYN_SENT) {
+
+ err = -ERESTARTSYS;
+ if (signal_pending(current))
+ goto out;
+
+ if ((err = sock_error(sk)) != 0) {
+ sock->state = SS_UNCONNECTED;
+ goto out;
+ }
+
+ SOCK_SLEEP_PRE(sk);
+
+ if (sk->state == TCP_SYN_SENT)
+ schedule();
+
+ SOCK_SLEEP_POST(sk);
+ }
+
+ if (sk->state != TCP_ESTABLISHED) {
+ sock->state = SS_UNCONNECTED;
+ err = sock_error(sk);
+ goto out;
+ }
+
+ err = 0;
+ sock->state = SS_CONNECTED;
+out:
+ release_sock(sk);
+
+ return err;
+}
+
+static int dn_access_copy(struct sk_buff *skb, struct accessdata_dn *acc)
+{
+ unsigned char *ptr = skb->data;
+
+ acc->acc_userl = *ptr++;
+ memcpy(&acc->acc_user, ptr, acc->acc_userl);
+ ptr += acc->acc_userl;
+
+ acc->acc_passl = *ptr++;
+ memcpy(&acc->acc_pass, ptr, acc->acc_passl);
+ ptr += acc->acc_passl;
+
+ acc->acc_accl = *ptr++;
+ memcpy(&acc->acc_acc, ptr, acc->acc_accl);
+
+ skb_pull(skb, acc->acc_accl + acc->acc_passl + acc->acc_userl + 3);
+
+ return 0;
+}
+
+static int dn_user_copy(struct sk_buff *skb, struct optdata_dn *opt)
+{
+ unsigned char *ptr = skb->data;
+
+ opt->opt_optl = *ptr++;
+ opt->opt_status = 0;
+ memcpy(opt->opt_data, ptr, opt->opt_optl);
+ skb_pull(skb, opt->opt_optl + 1);
+
+ return 0;
+}
+
+
+/*
+ * This is here for use in the sockopt() call as well as
+ * in accept(). Must be called with a locked socket.
+ */
+static int dn_wait_accept(struct socket *sock, int flags)
+{
+ struct sock *sk = sock->sk;
+
+ /* printk(KERN_DEBUG "dn_wait_accept: in\n"); */
+
+ while(sk->state == TCP_LISTEN) {
+ if (flags & O_NONBLOCK) {
+ return -EAGAIN;
+ }
+
+ SOCK_SLEEP_PRE(sk)
+
+ if (sk->state == TCP_LISTEN)
+ schedule();
+
+ SOCK_SLEEP_POST(sk)
+
+ if (signal_pending(current)) {
+ /* printk(KERN_DEBUG "dn_wait_accept: signal\n"); */
+ return -ERESTARTSYS; /* But of course you don't! */
+ }
+ }
+
+ if ((sk->protinfo.dn.state != DN_RUN) && (sk->protinfo.dn.state != DN_DRC)) {
+ sock->state = SS_UNCONNECTED;
+ return sock_error(sk);
+ }
+
+ sock->state = SS_CONNECTED;
+ /* printk(KERN_DEBUG "dn_wait_accept: out\n"); */
+
+ return 0;
+}
+
+
+static int dn_accept(struct socket *sock, struct socket *newsock,int flags)
+{
+ struct sock *sk=sock->sk, *newsk;
+ struct sk_buff *skb = NULL;
+ struct dn_skb_cb *cb;
+ unsigned char menuver;
+ int err = 0;
+ unsigned char type;
+
+ lock_sock(sk);
+
+ if (sk->state != TCP_LISTEN) {
+ release_sock(sk);
+ return -EINVAL;
+ }
+
+ if (sk->protinfo.dn.state != DN_O) {
+ release_sock(sk);
+ return -EINVAL;
+ }
+
+ if (newsock->sk != NULL) {
+ newsock->sk->socket = NULL;
+ dn_destroy_sock(newsock->sk);
+ newsock->sk = NULL;
+ }
+
+ do
+ {
+ /* printk(KERN_DEBUG "dn_accept: loop top\n"); */
+ if ((skb = skb_dequeue(&sk->receive_queue)) == NULL)
+ {
+ if (flags & O_NONBLOCK)
+ {
+ release_sock(sk);
+ return -EAGAIN;
+ }
+
+ SOCK_SLEEP_PRE(sk);
+
+ if (!skb_peek(&sk->receive_queue))
+ schedule();
+
+ SOCK_SLEEP_POST(sk);
+
+ if (signal_pending(current))
+ {
+ release_sock(sk);
+ return -ERESTARTSYS;
+ }
+ }
+ } while (skb == NULL);
+
+ cb = (struct dn_skb_cb *)skb->cb;
+
+ if ((newsk = dn_alloc_sock(newsock, GFP_KERNEL)) == NULL) {
+ release_sock(sk);
+ kfree_skb(skb);
+ return -ENOBUFS;
+ }
+ sk->ack_backlog--;
+ release_sock(sk);
+
+ dst_release(xchg(&newsk->dst_cache, skb->dst));
+ skb->dst = NULL;
+
+
+ newsk->protinfo.dn.state = DN_CR;
+ newsk->protinfo.dn.addrloc = dn_alloc_port();
+ newsk->protinfo.dn.addrrem = cb->src_port;
+ newsk->protinfo.dn.mss = cb->segsize;
+ newsk->protinfo.dn.accept_mode = sk->protinfo.dn.accept_mode;
+
+ if (newsk->protinfo.dn.mss < 230)
+ newsk->protinfo.dn.mss = 230;
+
+ newsk->state = TCP_LISTEN;
+ newsk->zapped = 0;
+
+ memcpy(&newsk->protinfo.dn.addr, &sk->protinfo.dn.addr, sizeof(struct sockaddr_dn));
+
+ skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &newsk->protinfo.dn.addr, &type));
+ skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &newsk->protinfo.dn.peer, &type));
+ *(dn_address *)newsk->protinfo.dn.peer.sdn_add.a_addr = cb->src;
+
+ menuver = *skb->data;
+ skb_pull(skb, 1);
+
+ if (menuver & DN_MENUVER_ACC)
+ dn_access_copy(skb, &newsk->protinfo.dn.accessdata);
+
+ if (menuver & DN_MENUVER_USR)
+ dn_user_copy(skb, &newsk->protinfo.dn.conndata_in);
+
+ if (menuver & DN_MENUVER_PRX)
+ newsk->protinfo.dn.peer.sdn_flags |= SDF_PROXY;
+
+ if (menuver & DN_MENUVER_UIC)
+ newsk->protinfo.dn.peer.sdn_flags |= SDF_UICPROXY;
+
+ kfree_skb(skb);
+
+ memcpy(&newsk->protinfo.dn.conndata_out, &sk->protinfo.dn.conndata_out,
+ sizeof(struct optdata_dn));
+ memcpy(&newsk->protinfo.dn.discdata_out, &sk->protinfo.dn.discdata_out,
+ sizeof(struct optdata_dn));
+
+ lock_sock(newsk);
+ sklist_insert_socket(&dn_sklist, newsk);
+
+ dn_send_conn_ack(newsk);
+
+ if (newsk->protinfo.dn.accept_mode == ACC_IMMED) {
+ newsk->protinfo.dn.state = DN_CC;
+ dn_send_conn_conf(newsk);
+ err = dn_wait_accept(newsock, flags);
+ }
+
+ release_sock(newsk);
+ return err;
+}
+
+
+static int dn_getname(struct socket *sock, struct sockaddr *uaddr,int *uaddr_len,int peer)
+{
+ struct sockaddr_dn *sa = (struct sockaddr_dn *)uaddr;
+ struct sock *sk = sock->sk;
+ struct dn_scp *scp = &sk->protinfo.dn;
+
+ *uaddr_len = sizeof(struct sockaddr_dn);
+
+ lock_sock(sk);
+
+ if (peer) {
+ if (sock->state != SS_CONNECTED && sk->protinfo.dn.accept_mode == ACC_IMMED)
+ return -ENOTCONN;
+
+ memcpy(sa, &scp->peer, sizeof(struct sockaddr_dn));
+ } else {
+ memcpy(sa, &scp->addr, sizeof(struct sockaddr_dn));
+ }
+
+ release_sock(sk);
+
+ return 0;
+}
+
+
+static unsigned int dn_poll(struct file *file, struct socket *sock, poll_table *wait)
+{
+ struct sock *sk = sock->sk;
+ struct dn_scp *scp = &sk->protinfo.dn;
+ int mask = datagram_poll(file, sock, wait);
+
+ if (skb_queue_len(&scp->other_receive_queue))
+ mask |= POLLRDBAND;
+
+ return mask;
+}
+
+static int dn_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct sock *sk = sock->sk;
+ int err = -EOPNOTSUPP;
+ unsigned long amount = 0;
+ struct sk_buff *skb;
+
+#if 0
+ struct dn_naddr dnaddr;
+#endif
+ switch(cmd)
+ {
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ return dn_dev_ioctl(cmd, (void *)arg);
+
+#ifdef CONFIG_DECNET_ROUTER
+ case SIOCADDRT:
+ case SIOCDELRT:
+ return dn_fib_ioctl(sock, cmd, arg);
+#endif /* CONFIG_DECNET_ROUTER */
+
+#if 0
+ case SIOCSIFADDR:
+ if (!suser()) return -EPERM;
+
+ if ((err = copy_from_user(devname, ioarg->devname, 5)) != 0)
+ break;
+ if ((err = copy_from_user(addr, ioarg->exec_addr, 6)) != 0)
+ break;
+ if ((dev = dev_get(devname)) == NULL) {
+ err = -ENODEV;
+ break;
+ }
+ if (dev->dn_ptr == NULL) {
+ err = -ENODEV;
+ break;
+ }
+
+ dn_dev_devices_off();
+
+ decnet_default_device = dev;
+ memcpy(decnet_ether_address, addr, ETH_ALEN);
+ decnet_address = dn_htons(dn_eth2dn(decnet_ether_address));
+
+ dn_dev_devices_on();
+
+ break;
+
+ case SIOCGIFADDR:
+ if (decnet_default_device)
+ strcpy(devname, decnet_default_device->name);
+ else
+ memset(devname, 0, 6);
+
+ if ((err = copy_to_user(ioarg->devname, devname, 5)) != 0)
+ break;
+
+ if ((err = copy_to_user(ioarg->exec_addr, decnet_ether_address, 6)) != 0)
+ break;
+
+ break;
+#endif
+
+#if 0
+ case SIOCSNETADDR:
+ if (!suser()) {
+ err = -EPERM;
+ break;
+ }
+
+ if ((err = copy_from_user(&dnaddr, (void *)arg, sizeof(struct dn_naddr))) != 0)
+ break;
+
+ if (dnaddr.a_len != ETH_ALEN) {
+ err = -EINVAL;
+ break;
+ }
+
+ dn_dev_devices_off();
+
+ memcpy(decnet_ether_address, dnaddr.a_addr, ETH_ALEN);
+ decnet_address = dn_htons(dn_eth2dn(decnet_ether_address));
+
+ dn_dev_devices_on();
+ break;
+
+ case SIOCGNETADDR:
+ dnaddr.a_len = ETH_ALEN;
+ memcpy(dnaddr.a_addr, decnet_ether_address, ETH_ALEN);
+
+ if ((err = copy_to_user((void *)arg, &dnaddr, sizeof(struct dn_naddr))) != 0)
+ break;
+
+ break;
+#endif
+ case OSIOCSNETADDR:
+ if (!suser()) {
+ err = -EPERM;
+ break;
+ }
+
+ dn_dev_devices_off();
+
+ decnet_address = (unsigned short)arg;
+ dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
+
+ dn_dev_devices_on();
+ err = 0;
+ break;
+
+ case OSIOCGNETADDR:
+ if ((err = put_user(decnet_address, (unsigned short *)arg)) != 0)
+ break;
+ break;
+ case SIOCGIFCONF:
+ case SIOCGIFFLAGS:
+ case SIOCGIFBRDADDR:
+ return dev_ioctl(cmd,(void *)arg);
+
+ case TIOCOUTQ:
+ amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
+ if (amount < 0)
+ amount = 0;
+ err = put_user(amount, (int *)arg);
+ break;
+
+ case TIOCINQ:
+ lock_sock(sk);
+ if ((skb = skb_peek(&sk->receive_queue)) != NULL)
+ amount = skb->len;
+ release_sock(sk);
+ err = put_user(amount, (int *)arg);
+ break;
+ }
+
+ return err;
+}
+
+static int dn_listen(struct socket *sock, int backlog)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk->zapped)
+ return -EINVAL;
+
+ if ((sk->protinfo.dn.state != DN_O) || (sk->state == TCP_LISTEN))
+ return -EINVAL;
+
+ if (backlog > SOMAXCONN)
+ backlog = SOMAXCONN;
+
+ sk->max_ack_backlog = backlog;
+ sk->ack_backlog = 0;
+ sk->state = TCP_LISTEN;
+
+ return 0;
+}
+
+
+static int dn_shutdown(struct socket *sock, int how)
+{
+ struct sock *sk = sock->sk;
+ struct dn_scp *scp = &sk->protinfo.dn;
+ int err = -ENOTCONN;
+
+ lock_sock(sk);
+
+ if (sock->state == SS_UNCONNECTED)
+ goto out;
+
+ err = 0;
+ if (sock->state == SS_DISCONNECTING)
+ goto out;
+
+ err = -EINVAL;
+ if (scp->state == DN_O)
+ goto out;
+
+ if (how != SHUTDOWN_MASK)
+ goto out;
+
+
+ sk->shutdown = how;
+ dn_destroy_sock(sk);
+ err = 0;
+
+out:
+ release_sock(sk);
+
+ return err;
+}
+
+static int dn_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
+{
+ return _dn_setsockopt(sock, level, optname, optval, optlen, 0);
+}
+
+static int _dn_setsockopt(struct socket *sock, int level,int optname, char *optval, int optlen, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct dn_scp *scp = &sk->protinfo.dn;
+ struct optdata_dn opt;
+ struct accessdata_dn acc;
+#ifdef CONFIG_DECNET_FW
+ char tmp_fw[MAX(sizeof(struct dn_fwtest),sizeof(struct dn_fwnew))];
+#endif
+ int err;
+
+ if (optlen && !optval)
+ return -EINVAL;
+
+ switch(optname) {
+ case DSO_CONDATA:
+ if (sock->state == SS_CONNECTED)
+ return -EISCONN;
+ if ((scp->state != DN_O) && (scp->state != DN_CR))
+ return -EINVAL;
+
+ if (optlen != sizeof(struct optdata_dn))
+ return -EINVAL;
+
+ if (copy_from_user(&opt, optval, optlen))
+ return -EFAULT;
+
+ if (opt.opt_optl > 16)
+ return -EINVAL;
+
+ memcpy(&scp->conndata_out, &opt, sizeof(struct optdata_dn));
+ break;
+
+ case DSO_DISDATA:
+ if (sock->state != SS_CONNECTED && sk->protinfo.dn.accept_mode == ACC_IMMED)
+ return -ENOTCONN;
+
+ if (optlen != sizeof(struct optdata_dn))
+ return -EINVAL;
+
+ if (copy_from_user(&opt, optval, sizeof(struct optdata_dn)))
+ return -EFAULT;
+
+ if (opt.opt_optl > 16)
+ return -EINVAL;
+
+ memcpy(&scp->discdata_out, &opt, sizeof(struct optdata_dn));
+ break;
+
+ case DSO_CONACCESS:
+ if (sock->state == SS_CONNECTED)
+ return -EISCONN;
+ if (scp->state != DN_O)
+ return -EINVAL;
+
+ if (optlen != sizeof(struct accessdata_dn))
+ return -EINVAL;
+
+ if (copy_from_user(&acc, optval, sizeof(struct accessdata_dn)))
+ return -EFAULT;
+
+ if ((acc.acc_accl > DN_MAXACCL) ||
+ (acc.acc_passl > DN_MAXACCL) ||
+ (acc.acc_userl > DN_MAXACCL))
+ return -EINVAL;
+
+ memcpy(&scp->accessdata, &acc, sizeof(struct accessdata_dn));
+ break;
+
+ case DSO_ACCEPTMODE:
+ if (sock->state == SS_CONNECTED)
+ return -EISCONN;
+ if (scp->state != DN_O)
+ return -EINVAL;
+
+ if (optlen != sizeof(int))
+ return -EINVAL;
+
+ {
+ int mode;
+
+ if (get_user(mode, optval))
+ return -EFAULT;
+ if ((mode != ACC_IMMED) && (mode != ACC_DEFER))
+ return -EINVAL;
+
+ scp->accept_mode = (unsigned char)mode;
+ }
+ break;
+
+ case DSO_CONACCEPT:
+ lock_sock(sk);
+
+ if (scp->state != DN_CR)
+ return -EINVAL;
+
+ scp->state = DN_CC;
+ dn_send_conn_conf(sk);
+ err = dn_wait_accept(sock, sock->file->f_flags);
+ release_sock(sk);
+ return err;
+
+ case DSO_CONREJECT:
+ lock_sock(sk);
+
+ if (scp->state != DN_CR)
+ return -EINVAL;
+
+ scp->state = DN_DR;
+ sk->shutdown = SHUTDOWN_MASK;
+ dn_send_disc(sk, 0x38, 0);
+ release_sock(sk);
+ break;
+
+#ifdef CONFIG_DECNET_FW
+ case DN_FW_MASQ_TIMEOUTS:
+ case DN_FW_APPEND:
+ case DN_FW_REPLACE:
+ case DN_FW_DELETE:
+ case DN_FW_DELETE_NUM:
+ case DN_FW_INSERT:
+ case DN_FW_FLUSH:
+ case DN_FW_ZERO:
+ case DN_FW_CHECK:
+ case DN_FW_CREATECHAIN:
+ case DN_FW_DELETECHAIN:
+ case DN_FW_POLICY:
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+ if ((optlen > sizeof(tmp_fw)) || (optlen < 1))
+ return -EINVAL;
+ if (copy_from_user(&tmp_fw, optval, optlen))
+ return -EFAULT;
+ err = dn_fw_ctl(optname, &tmp_fw, optlen);
+ return -err; /* -0 is 0 after all */
+#endif
+ default:
+ case DSO_LINKINFO:
+ case DSO_STREAM:
+ case DSO_SEQPACKET:
+
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int dn_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
+{
+ return _dn_getsockopt(sock, level, optname, optval, optlen, 0);
+}
+
+static int
+_dn_getsockopt(struct socket *sock, int level,int optname, char *optval,int *optlen, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct dn_scp *scp = &sk->protinfo.dn;
+ struct linkinfo_dn link;
+ int mode = scp->accept_mode;
+
+ switch(optname) {
+ case DSO_CONDATA:
+ if (*optlen != sizeof(struct optdata_dn))
+ return -EINVAL;
+
+ if (copy_to_user(optval, &scp->conndata_in, sizeof(struct optdata_dn)))
+ return -EFAULT;
+ break;
+
+ case DSO_DISDATA:
+ if (*optlen != sizeof(struct optdata_dn))
+ return -EINVAL;
+
+ if (copy_to_user(optval, &scp->discdata_in, sizeof(struct optdata_dn)))
+ return -EFAULT;
+
+ break;
+
+ case DSO_CONACCESS:
+ if (*optlen != sizeof(struct accessdata_dn))
+ return -EINVAL;
+
+ if (copy_to_user(optval, &scp->accessdata, sizeof(struct accessdata_dn)))
+ return -EFAULT;
+ break;
+
+ case DSO_ACCEPTMODE:
+ if (put_user(mode, optval))
+ return -EFAULT;
+ break;
+
+ case DSO_LINKINFO:
+ if (*optlen != sizeof(struct linkinfo_dn))
+ return -EINVAL;
+
+ switch(sock->state) {
+ case SS_CONNECTING:
+ link.idn_linkstate = LL_CONNECTING;
+ break;
+ case SS_DISCONNECTING:
+ link.idn_linkstate = LL_DISCONNECTING;
+ break;
+ case SS_CONNECTED:
+ link.idn_linkstate = LL_RUNNING;
+ break;
+ default:
+ link.idn_linkstate = LL_INACTIVE;
+ }
+
+ link.idn_segsize = scp->mss;
+
+ if (copy_to_user(optval, &link, sizeof(struct linkinfo_dn)))
+ return -EFAULT;
+ break;
+
+ case DSO_STREAM:
+ case DSO_SEQPACKET:
+ case DSO_CONACCEPT:
+ case DSO_CONREJECT:
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Used by send/recvmsg to wait until the socket is connected
+ * before passing data.
+ */
+static int dn_wait_run(struct sock *sk, int flags)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ int err = 0;
+
+ /* printk(KERN_DEBUG "dn_wait_run %d\n", scp->state); */
+
+ switch(scp->state) {
+ case DN_RUN:
+ return 0;
+
+ case DN_CR:
+ scp->state = DN_CC;
+ dn_send_conn_conf(sk);
+ return dn_wait_accept(sk->socket, (flags & MSG_DONTWAIT) ? O_NONBLOCK : 0);
+ case DN_CI:
+ case DN_CC:
+ break;
+ default:
+ return -ENOTCONN;
+ goto out;
+ }
+
+ if (flags & MSG_DONTWAIT)
+ return -EWOULDBLOCK;
+
+ do {
+ if ((err = sock_error(sk)) != 0)
+ goto out;
+
+ if (signal_pending(current)) {
+ err = -ERESTARTSYS;
+ goto out;
+ }
+
+ SOCK_SLEEP_PRE(sk)
+
+ if (scp->state != DN_RUN)
+ schedule();
+
+ SOCK_SLEEP_POST(sk)
+
+ } while(scp->state != DN_RUN);
+
+out:
+
+ return 0;
+}
+
+
+static int dn_data_ready(struct sock *sk, struct sk_buff_head *q, int flags, int target)
+{
+ struct sk_buff *skb = q->next;
+ int len = 0;
+
+ if (flags & MSG_OOB)
+ return skb_queue_len(q) ? 1 : 0;
+
+ while(skb != (struct sk_buff *)q) {
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ len += skb->len;
+
+ if (cb->nsp_flags & 0x40) {
+ /* SOCK_SEQPACKET reads to EOM */
+ if (sk->type == SOCK_SEQPACKET)
+ return 1;
+ /* so does SOCK_STREAM unless WAITALL is specified */
+ if (!(flags & MSG_WAITALL))
+ return 1;
+ }
+
+ /* minimum data length for read exceeded */
+ if (len >= target)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int dn_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+ int flags, struct scm_cookie *scm)
+{
+ struct sock *sk = sock->sk;
+ struct dn_scp *scp = &sk->protinfo.dn;
+ struct sk_buff_head *queue = &sk->receive_queue;
+ int target = size > 1 ? 1 : 0;
+ int copied = 0;
+ int rv = 0;
+ struct sk_buff *skb = NULL, **pskb;
+ struct dn_skb_cb *cb = NULL;
+
+ lock_sock(sk);
+
+ if (sk->zapped) {
+ rv = -EADDRNOTAVAIL;
+ goto out;
+ }
+
+ if ((rv = dn_wait_run(sk, flags)) != 0)
+ goto out;
+
+ if (sk->shutdown & RCV_SHUTDOWN) {
+ send_sig(SIGPIPE, current, 0);
+ rv = -EPIPE;
+ goto out;
+ }
+
+ if (flags & ~(MSG_PEEK|MSG_OOB|MSG_WAITALL|MSG_DONTWAIT)) {
+ rv = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (flags & MSG_OOB)
+ queue = &scp->other_receive_queue;
+
+ if (flags & MSG_WAITALL)
+ target = size;
+
+
+ /*
+ * See if there is data ready to read, sleep if there isn't
+ */
+ for(;;) {
+ if (sk->err)
+ goto out;
+
+ if (skb_queue_len(&scp->other_receive_queue)) {
+ if (!(flags & MSG_OOB)) {
+ msg->msg_flags |= MSG_OOB;
+ if (!scp->other_report) {
+ scp->other_report = 1;
+ goto out;
+ }
+ }
+ }
+
+ if (scp->state != DN_RUN)
+ goto out;
+
+ if (signal_pending(current)) {
+ rv = -ERESTARTSYS;
+ goto out;
+ }
+
+ if (dn_data_ready(sk, queue, flags, target))
+ break;
+
+ if (flags & MSG_DONTWAIT) {
+ rv = -EWOULDBLOCK;
+ goto out;
+ }
+
+ sock->flags |= SO_WAITDATA;
+ SOCK_SLEEP_PRE(sk)
+
+ if (!dn_data_ready(sk, queue, flags, target))
+ schedule();
+
+ SOCK_SLEEP_POST(sk)
+ sock->flags &= ~SO_WAITDATA;
+ }
+
+ pskb = &((struct sk_buff *)queue)->next;
+ while((skb = *pskb) != (struct sk_buff *)queue) {
+ int chunk = skb->len;
+ cb = (struct dn_skb_cb *)skb->cb;
+
+ if ((chunk + copied) > size)
+ chunk = size - copied;
+
+ if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
+ rv = -EFAULT;
+ break;
+ }
+ copied += chunk;
+
+ if (!(flags & MSG_PEEK))
+ skb->len -= chunk;
+
+ if (skb->len == 0) {
+ skb_unlink(skb);
+ kfree_skb(skb);
+ if ((scp->flowloc_sw == DN_DONTSEND) && !dn_congested(sk)) {
+ scp->flowloc_sw = DN_SEND;
+ dn_nsp_send_lnk(sk, DN_SEND);
+ }
+ }
+
+ pskb = &skb->next;
+
+ if (cb->nsp_flags & 0x40) {
+ if (sk->type == SOCK_SEQPACKET) {
+ msg->msg_flags |= MSG_EOR;
+ break;
+ }
+ }
+
+ if (!(flags & MSG_WAITALL))
+ break;
+
+ if (flags & MSG_OOB)
+ break;
+
+ if (copied >= target)
+ break;
+ }
+
+ rv = copied;
+out:
+ if (rv == 0)
+ rv = (flags & MSG_PEEK) ? -sk->err : sock_error(sk);
+
+ release_sock(sk);
+
+ return rv;
+}
+
+
+static int dn_sendmsg(struct socket *sock, struct msghdr *msg, int size,
+ struct scm_cookie *scm)
+{
+ struct sock *sk = sock->sk;
+ struct dn_scp *scp = &sk->protinfo.dn;
+ int mss = scp->mss;
+ int mtu = 230 - 11; /* maximum value thats always safe */
+ struct sk_buff_head *queue = &scp->data_xmit_queue;
+ int flags = msg->msg_flags;
+ unsigned short numseg = 0;
+ int err = 0;
+ int sent = 0;
+ int addr_len = msg->msg_namelen;
+ struct sockaddr_dn *addr = (struct sockaddr_dn *)msg->msg_name;
+ struct sk_buff *skb = NULL;
+ struct dn_skb_cb *cb;
+ unsigned char msgflg;
+ unsigned char *ptr;
+ unsigned short ack;
+ int len;
+
+ if (flags & ~(MSG_TRYHARD|MSG_OOB|MSG_DONTWAIT))
+ return -EOPNOTSUPP;
+
+ if (addr_len && (addr_len != sizeof(struct sockaddr_dn)))
+ return -EINVAL;
+
+ if (sk->zapped && dn_auto_bind(sock)) {
+ err = -EADDRNOTAVAIL;
+ goto out;
+ }
+
+ if (scp->state == DN_O) {
+ if (!addr_len || !addr) {
+ err = -ENOTCONN;
+ goto out;
+ }
+
+ if ((err = dn_connect(sock, (struct sockaddr *)addr, addr_len, (flags & MSG_DONTWAIT) ? O_NONBLOCK : 0)) < 0)
+ goto out;
+ }
+
+ lock_sock(sk);
+
+ if ((err = dn_wait_run(sk, flags)) < 0)
+ goto out;
+
+ if (sk->shutdown & SEND_SHUTDOWN) {
+ send_sig(SIGPIPE, current, 0);
+ err = -EPIPE;
+ goto out;
+ }
+
+ if ((flags & MSG_TRYHARD) && sk->dst_cache)
+ dst_negative_advice(&sk->dst_cache);
+
+ if (sk->dst_cache && sk->dst_cache->neighbour) {
+ struct dn_neigh *dn = (struct dn_neigh *)sk->dst_cache->neighbour;
+ if (dn->blksize > 230)
+ mtu = dn->blksize - 11;
+ }
+
+ /*
+ * The only difference between SEQPACKET & STREAM sockets under DECnet
+ * AFAIK is that SEQPACKET sockets set the MSG_EOR flag for the last
+ * session control message segment.
+ */
+
+ if (flags & MSG_OOB) {
+ mss = 16;
+ queue = &scp->other_xmit_queue;
+ if (size > mss) {
+ err = -EMSGSIZE;
+ goto out;
+ }
+ }
+
+ if (mss < mtu)
+ mtu = mss;
+
+ scp->persist_fxn = dn_nsp_xmit_timeout;
+
+ while(sent < size) {
+ if ((err = sock_error(sk) != 0))
+ goto out;
+
+ if (signal_pending(current)) {
+ err = -ERESTARTSYS;
+ goto out;
+ }
+
+ /*
+ * Calculate size that we wish to send.
+ */
+ len = size - sent;
+
+ if (len > mtu)
+ len = mtu;
+
+ /*
+ * Wait for queue size to go down below the window
+ * size.
+ */
+ if (skb_queue_len(queue) >= scp->snd_window) {
+ if (flags & MSG_DONTWAIT) {
+ err = -EWOULDBLOCK;
+ goto out;
+ }
+
+ SOCK_SLEEP_PRE(sk)
+
+ if (skb_queue_len(queue) >= scp->snd_window)
+ schedule();
+
+ SOCK_SLEEP_POST(sk)
+
+ continue;
+ }
+
+ /*
+ * Get a suitably sized skb.
+ */
+ skb = dn_alloc_send_skb(sk, &len, flags & MSG_DONTWAIT, &err);
+
+ if (err)
+ break;
+
+ if (!skb)
+ continue;
+
+ cb = (struct dn_skb_cb *)skb->cb;
+
+ ptr = skb_put(skb, 9);
+
+ if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ if (flags & MSG_OOB) {
+ cb->segnum = scp->numoth++;
+ scp->numoth &= 0x0fff;
+ msgflg = 0x30;
+ ack = scp->ackxmt_oth | 0x8000;
+ } else {
+ cb->segnum = scp->numdat++;
+ scp->numdat &= 0x0fff;
+ if (sock->type == SOCK_STREAM)
+ msgflg = 0x60;
+ else
+ msgflg = 0x00;
+ if (numseg == 0)
+ msgflg |= 0x20;
+ if ((sent + len) == size)
+ msgflg |= 0x40;
+ ack = scp->ackxmt_dat | 0x8000;
+ }
+
+ *ptr++ = msgflg;
+ *(__u16 *)ptr = scp->addrrem;
+ ptr += 2;
+ *(__u16 *)ptr = scp->addrloc;
+ ptr += 2;
+ *(__u16 *)ptr = dn_htons(ack);
+ ptr += 2;
+ *(__u16 *)ptr = dn_htons(cb->segnum);
+
+ sent += len;
+ dn_nsp_queue_xmit(sk, skb, flags & MSG_OOB);
+ numseg++;
+ skb = NULL;
+
+ scp->persist = dn_nsp_persist(sk);
+
+ }
+out:
+
+ if (skb)
+ kfree_skb(skb);
+
+ release_sock(sk);
+
+ return sent ? sent : err;
+}
+
+static int dn_device_event(struct notifier_block *this, unsigned long event,
+ void *ptr)
+{
+ struct device *dev = (struct device *)ptr;
+
+ switch(event) {
+ case NETDEV_UP:
+ dn_dev_up(dev);
+ break;
+ case NETDEV_DOWN:
+ dn_dev_down(dev);
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block dn_dev_notifier = {
+ dn_device_event,
+ 0
+};
+
+extern int dn_route_rcv(struct sk_buff *, struct device *, struct packet_type *);
+
+static struct packet_type dn_dix_packet_type =
+{
+ __constant_htons(ETH_P_DNA_RT),
+ NULL, /* All devices */
+ dn_route_rcv,
+ NULL,
+ NULL,
+};
+
+#ifdef CONFIG_PROC_FS
+struct proc_dir_entry decnet_linkinfo = {
+ PROC_NET_DN_SKT, 6, "decnet", S_IFREG | S_IRUGO,
+ 1, 0, 0, 0, &proc_net_inode_operations, dn_get_info
+};
+
+#ifdef CONFIG_DECNET_RAW
+
+extern int dn_raw_get_info(char *, char **, off_t, int, int);
+
+struct proc_dir_entry decnet_rawinfo = {
+ PROC_NET_DN_RAW, 10, "decnet_raw", S_IFREG | S_IRUGO,
+ 1, 0, 0, 0, &proc_net_inode_operations, dn_raw_get_info
+};
+
+#endif /* CONFIG_DECNET_RAW */
+#endif /* CONFIG_PROC_FS */
+static struct net_proto_family dn_family_ops = {
+ AF_DECnet,
+ dn_create
+};
+
+static struct proto_ops dn_proto_ops = {
+ AF_DECnet,
+
+ sock_no_dup,
+ dn_release,
+ dn_bind,
+ dn_connect,
+ sock_no_socketpair,
+ dn_accept,
+ dn_getname,
+ dn_poll,
+ dn_ioctl,
+ dn_listen,
+ dn_shutdown,
+ dn_setsockopt,
+ dn_getsockopt,
+ sock_no_fcntl,
+ dn_sendmsg,
+ dn_recvmsg
+};
+
+#ifdef CONFIG_SYSCTL
+void dn_register_sysctl(void);
+void dn_unregister_sysctl(void);
+#endif
+
+void __init decnet_proto_init(struct net_proto *pro)
+{
+ sock_register(&dn_family_ops);
+ dev_add_pack(&dn_dix_packet_type);
+ register_netdevice_notifier(&dn_dev_notifier);
+
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&decnet_linkinfo);
+#ifdef CONFIG_DECNET_RAW
+ proc_net_register(&decnet_rawinfo);
+#endif
+#endif
+ dn_dev_init();
+ dn_neigh_init();
+ dn_route_init();
+
+#ifdef CONFIG_DECNET_FW
+ dn_fw_init();
+#endif /* CONFIG_DECNET_FW */
+
+#ifdef CONFIG_DECNET_ROUTER
+ dn_fib_init();
+#endif /* CONFIG_DECNET_ROUTER */
+
+#ifdef CONFIG_SYSCTL
+ dn_register_sysctl();
+#endif /* CONFIG_SYSCTL */
+ printk(KERN_INFO "DECnet for Linux: V.2.2.5s (C) 1995-1999 Linux DECnet Project Team\n");
+
+}
+
+void __init decnet_setup(char *str, int *ints)
+{
+
+ if ((ints[0] == 2) || (ints[0] == 3)) {
+
+ if (ints[1] < 0)
+ ints[1] = 0;
+ if (ints[1] > 63)
+ ints[1] = 63;
+
+ if (ints[2] < 0)
+ ints[2] = 0;
+ if (ints[2] > 1023)
+ ints[2] = 1023;
+
+ decnet_address = dn_htons(ints[1] << 10 | ints[2]);
+ dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
+
+ if (ints[0] == 3) {
+ switch(ints[3]) {
+#ifdef CONFIG_DECNET_ROUTER
+ case 1:
+ decnet_node_type = DN_RT_INFO_L1RT;
+ break;
+ case 2:
+ decnet_node_type = DN_RT_INFO_L2RT;
+ break;
+#endif /* CONFIG_DECNET_ROUTER */
+ default:
+ decnet_node_type = DN_RT_INFO_ENDN;
+ }
+ }
+ } else {
+ printk(KERN_ERR "DECnet: Invalid command line options\n");
+ }
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+MODULE_DESCRIPTION("The Linux DECnet Network Protocol");
+MODULE_AUTHOR("Linux DECnet Project Team");
+
+static int addr[2] = {0, 0};
+#ifdef CONFIG_DECNET_ROUTER
+static int type = 0;
+#endif
+
+MODULE_PARM(addr, "2i");
+MODULE_PARM_DESC(addr, "The DECnet address of this machine: area,node");
+#ifdef CONFIG_DECNET_ROUTER
+MODULE_PARM(type, "i");
+MODULE_PARM_DESC(type, "The type of this DECnet node: 0=EndNode, 1,2=Router");
+#endif
+
+int init_module(void)
+{
+ if (addr[0] > 63 || addr[0] < 0) {
+ printk(KERN_ERR "DECnet: Area must be between 0 and 63");
+ return 1;
+ }
+
+ if (addr[1] > 1023 || addr[1] < 0) {
+ printk(KERN_ERR "DECnet: Node must be between 0 and 1023");
+ return 1;
+ }
+
+ decnet_address = dn_htons((addr[0] << 10) | addr[1]);
+ dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
+
+#ifdef CONFIG_DECNET_ROUTER
+ switch(type) {
+ case 0:
+ decnet_node_type = DN_RT_INFO_ENDN;
+ break;
+ case 1:
+ decnet_node_type = DN_RT_INFO_L1RT;
+ break;
+ case 2:
+ decnet_node_type = DN_RT_INFO_L2RT;
+ break;
+ default:
+ printk(KERN_ERR "DECnet: Node type must be between 0 and 2 inclusive\n");
+ return 1;
+ }
+#else
+ decnet_node_type = DN_RT_INFO_ENDN;
+#endif
+
+ decnet_proto_init(NULL);
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+#ifdef CONFIG_SYSCTL
+ dn_unregister_sysctl();
+#endif /* CONFIG_SYSCTL */
+
+ unregister_netdevice_notifier(&dn_dev_notifier);
+
+ dn_route_cleanup();
+ dn_neigh_cleanup();
+ dn_dev_cleanup();
+
+#ifdef CONFIG_DECNET_FW
+ /* dn_fw_cleanup(); */
+#endif /* CONFIG_DECNET_FW */
+
+#ifdef CONFIG_DECNET_ROUTER
+ dn_fib_cleanup();
+#endif /* CONFIG_DECNET_ROUTER */
+
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(PROC_NET_DN_SKT);
+#ifdef CONFIG_DECNET_RAW
+ proc_net_unregister(PROC_NET_DN_RAW);
+#endif
+#endif
+
+ dev_remove_pack(&dn_dix_packet_type);
+ sock_unregister(AF_DECnet);
+}
+
+#endif
diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c
new file mode 100644
index 000000000..e46fdbcc0
--- /dev/null
+++ b/net/decnet/dn_dev.c
@@ -0,0 +1,1386 @@
+/*
+ * DECnet An implementation of the DECnet protocol suite for the LINUX
+ * operating system. DECnet is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * DECnet Device Layer
+ *
+ * Authors: Steve Whitehouse <SteveW@ACM.org>
+ * Eduardo Marcelo Serrat <emserrat@geocities.com>
+ *
+ * Changes:
+ * Steve Whitehouse : Devices now see incoming frames so they
+ * can mark on who it came from.
+ * Steve Whitehouse : Fixed bug in creating neighbours. Each neighbour
+ * can now have a device specific setup func.
+ * Steve Whitehouse : Added /proc/sys/net/decnet/conf/<dev>/
+ * Steve Whitehouse : Fixed bug which sometimes killed timer
+ * Steve Whitehouse : Multiple ifaddr support
+ * Steve Whitehouse : SIOCGIFCONF is now a compile time option
+ */
+
+#include <linux/config.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/sysctl.h>
+#include <asm/uaccess.h>
+#include <net/neighbour.h>
+#include <net/dst.h>
+#include <net/dn.h>
+#include <net/dn_dev.h>
+#include <net/dn_route.h>
+#include <net/dn_neigh.h>
+#include <net/dn_fib.h>
+
+#define DN_IFREQ_SIZE (sizeof(struct ifreq) - sizeof(struct sockaddr) + sizeof(struct sockaddr_dn))
+
+static char dn_rt_all_end_mcast[ETH_ALEN] = {0xAB,0x00,0x00,0x04,0x00,0x00};
+static char dn_rt_all_rt_mcast[ETH_ALEN] = {0xAB,0x00,0x00,0x03,0x00,0x00};
+static char dn_hiord[ETH_ALEN] = {0xAA,0x00,0x04,0x00,0x00,0x00};
+static unsigned char dn_eco_version[3] = {0x02,0x00,0x00};
+
+extern struct neigh_table dn_neigh_table;
+
+struct device *decnet_default_device = NULL;
+
+static struct dn_dev *dn_dev_create(struct device *dev, int *err);
+static void dn_dev_delete(struct device *dev);
+#ifdef CONFIG_RTNETLINK
+static void rtmsg_ifa(int event, struct dn_ifaddr *ifa);
+#endif
+
+static int dn_eth_up(struct device *);
+static void dn_send_brd_hello(struct device *dev);
+static void dn_send_ptp_hello(struct device *dev);
+static int dn_dev_eth_setsrc(struct sk_buff *skb);
+static int dn_dev_lo_setsrc(struct sk_buff *skb);
+static int dn_dev_ptp_setsrc(struct sk_buff *skb);
+static int dn_dev_eth_neigh_setup(struct neighbour *n);
+
+static struct dn_dev_parms dn_dev_list[] = {
+{
+ ARPHRD_ETHER, /* Ethernet */
+ DN_DEV_BCAST,
+ DN_DEV_S_RU,
+ 1,
+ 1498,
+ 10,
+ 1,
+ 10,
+ 0,
+ "ethernet",
+ NET_DECNET_CONF_ETHER,
+ dn_eth_up,
+ NULL,
+ NULL,
+ dn_send_brd_hello,
+ dn_dev_eth_setsrc,
+ dn_dev_eth_neigh_setup,
+ NULL
+},
+{
+ ARPHRD_IPGRE, /* DECnet tunneled over GRE in IP */
+ DN_DEV_BCAST,
+ DN_DEV_S_RU,
+ 2,
+ 1400,
+ 10,
+ 1,
+ 10,
+ 0,
+ "ipgre",
+ NET_DECNET_CONF_GRE,
+ NULL,
+ NULL,
+ NULL,
+ dn_send_brd_hello,
+ NULL,
+ NULL,
+ NULL
+},
+#if 0
+{
+ ARPHRD_X25, /* Bog standard X.25 */
+ DN_DEV_UCAST,
+ DN_DEV_S_DS,
+ 5,
+ 230,
+ 10 * 60,
+ 1,
+ 120,
+ 0,
+ "x25",
+ NET_DECNET_CONF_X25,
+ NULL,
+ NULL,
+ NULL,
+ dn_send_ptp_hello,
+ dn_dev_ptp_setsrc,
+ NULL,
+ NULL
+},
+#endif
+#if 0
+{
+ ARPHRD_PPP, /* DECnet over PPP */
+ DN_DEV_BCAST,
+ DN_DEV_S_RU,
+ 5,
+ 230,
+ 10,
+ 1,
+ 10,
+ 0,
+ "ppp",
+ NET_DECNET_CONF_PPP,
+ NULL,
+ NULL,
+ NULL,
+ dn_send_brd_hello,
+ dn_dev_ptp_setsrc,
+ NULL,
+ NULL
+},
+#endif
+#if 0
+{
+ ARPHRD_DDCMP, /* DECnet over DDCMP */
+ DN_DEV_UCAST,
+ DN_DEV_S_DS,
+ 5,
+ 230,
+ 10 * 60,
+ 1,
+ 120,
+ 0,
+ "ddcmp",
+ NET_DECNET_CONF_DDCMP,
+ NULL,
+ NULL,
+ NULL,
+ dn_send_ptp_hello,
+ dn_dev_ptp_setsrc,
+ NULL,
+ NULL
+},
+#endif
+{
+ ARPHRD_LOOPBACK, /* Loopback interface - always last */
+ DN_DEV_BCAST,
+ DN_DEV_S_RU,
+ 0,
+ 1498,
+ 10,
+ 1,
+ 10,
+ 0,
+ "loopback",
+ NET_DECNET_CONF_LOOPBACK,
+ NULL,
+ NULL,
+ NULL,
+ dn_send_brd_hello,
+ dn_dev_lo_setsrc,
+ dn_dev_eth_neigh_setup,
+ NULL
+}
+};
+
+#define DN_DEV_LIST_SIZE (sizeof(dn_dev_list)/sizeof(struct dn_dev_parms))
+
+#define DN_DEV_PARMS_OFFSET(x) ((int) ((char *) &((struct dn_dev_parms *)0)->x))
+
+#ifdef CONFIG_SYSCTL
+
+static int min_t2[] = { 1 };
+static int max_t2[] = { 60 }; /* No max specified, but this seems sensible */
+static int min_t3[] = { 1 };
+static int max_t3[] = { 8191 }; /* Must fit in 16 bits when multiplied by BCT3MULT or T3MULT */
+#ifdef CONFIG_DECNET_ROUTER
+static int min_t1[] = { 1 };
+static int max_t1[] = { 8191 }; /* No max specified, so made it the same as t3 */
+static int min_cost[] = { 0 };
+static int max_cost[] = { 25 }; /* From DECnet spec */
+static int min_priority[] = { 0 };
+static int max_priority[] = { 127 }; /* From DECnet spec */
+#endif /* CONFIG_DECNET_ROUTER */
+
+static struct dn_dev_sysctl_table {
+ struct ctl_table_header *sysctl_header;
+#ifdef CONFIG_DECNET_ROUTER
+ ctl_table dn_dev_vars[6];
+#else
+ ctl_table dn_dev_vars[3];
+#endif
+ ctl_table dn_dev_dev[2];
+ ctl_table dn_dev_conf_dir[2];
+ ctl_table dn_dev_proto_dir[2];
+ ctl_table dn_dev_root_dir[2];
+} dn_dev_sysctl = {
+ NULL,
+ {
+#ifdef CONFIG_DECNET_ROUTER
+ {NET_DECNET_CONF_DEV_COST, "cost", (void *)DN_DEV_PARMS_OFFSET(cost),
+ sizeof(int), 0644, NULL,
+ proc_dointvec_minmax, sysctl_intvec,
+ NULL, &min_cost, &max_cost},
+ {NET_DECNET_CONF_DEV_PRIORITY, "priority", (void *)DN_DEV_PARMS_OFFSET(priority),
+ sizeof(int), 0644, NULL,
+ proc_dointvec_minmax, sysctl_intvec,
+ NULL, &min_priority, &max_priority},
+ {NET_DECNET_CONF_DEV_T1, "t1", (void *)DN_DEV_PARMS_OFFSET(t1),
+ sizeof(int), 0644, NULL,
+ proc_dointvec_minmax, sysctl_intvec,
+ NULL, &min_t1, &max_t1},
+#endif
+ {NET_DECNET_CONF_DEV_T2, "t2", (void *)DN_DEV_PARMS_OFFSET(t2),
+ sizeof(int), 0644, NULL,
+ proc_dointvec_minmax, sysctl_intvec,
+ NULL, &min_t2, &max_t2},
+ {NET_DECNET_CONF_DEV_T3, "t3", (void *)DN_DEV_PARMS_OFFSET(t3),
+ sizeof(int), 0644, NULL,
+ proc_dointvec_minmax, sysctl_intvec,
+ NULL, &min_t3, &max_t3},
+ {0}
+ },
+ {{0, "", NULL, 0, 0555, dn_dev_sysctl.dn_dev_vars}, {0}},
+ {{NET_DECNET_CONF, "conf", NULL, 0, 0555, dn_dev_sysctl.dn_dev_dev}, {0}},
+ {{NET_DECNET, "decnet", NULL, 0, 0555, dn_dev_sysctl.dn_dev_conf_dir}, {0}},
+ {{CTL_NET, "net", NULL, 0, 0555, dn_dev_sysctl.dn_dev_proto_dir}, {0}}
+};
+
+static void dn_dev_sysctl_register(struct device *dev, struct dn_dev_parms *parms)
+{
+ struct dn_dev_sysctl_table *t;
+ int i;
+
+ t = kmalloc(sizeof(*t), GFP_KERNEL);
+ if (t == NULL)
+ return;
+
+ memcpy(t, &dn_dev_sysctl, sizeof(*t));
+
+ for(i = 0; i < (sizeof(t->dn_dev_vars)/sizeof(t->dn_dev_vars[0]) - 1); i++) {
+ long offset = (long)t->dn_dev_vars[i].data;
+ t->dn_dev_vars[i].data = ((char *)parms) + offset;
+ t->dn_dev_vars[i].de = NULL;
+ }
+
+ if (dev) {
+ t->dn_dev_dev[0].procname = dev->name;
+ t->dn_dev_dev[0].ctl_name = dev->ifindex;
+ } else {
+ t->dn_dev_dev[0].procname = parms->name;
+ t->dn_dev_dev[0].ctl_name = parms->ctl_name;
+ }
+
+ t->dn_dev_dev[0].child = t->dn_dev_vars;
+ t->dn_dev_dev[0].de = NULL;
+ t->dn_dev_conf_dir[0].child = t->dn_dev_dev;
+ t->dn_dev_conf_dir[0].de = NULL;
+ t->dn_dev_proto_dir[0].child = t->dn_dev_conf_dir;
+ t->dn_dev_proto_dir[0].de = NULL;
+ t->dn_dev_root_dir[0].child = t->dn_dev_proto_dir;
+ t->dn_dev_root_dir[0].de = NULL;
+
+ t->sysctl_header = register_sysctl_table(t->dn_dev_root_dir, 0);
+ if (t->sysctl_header == NULL)
+ kfree(t);
+ else
+ parms->sysctl = t;
+}
+
+static void dn_dev_sysctl_unregister(struct dn_dev_parms *parms)
+{
+ if (parms->sysctl) {
+ struct dn_dev_sysctl_table *t = parms->sysctl;
+ parms->sysctl = NULL;
+ unregister_sysctl_table(t->sysctl_header);
+ kfree(t);
+ }
+}
+#endif
+
+static struct dn_ifaddr *dn_dev_alloc_ifa(void)
+{
+ struct dn_ifaddr *ifa;
+
+ ifa = kmalloc(sizeof(*ifa), GFP_KERNEL);
+
+ if (ifa) {
+ memset(ifa, 0, sizeof(*ifa));
+ }
+
+ return ifa;
+}
+
+static __inline__ void dn_dev_free_ifa(struct dn_ifaddr *ifa)
+{
+ kfree_s(ifa, sizeof(*ifa));
+}
+
+static void dn_dev_del_ifa(struct dn_dev *dn_db, struct dn_ifaddr **ifap, int destroy)
+{
+ struct dn_ifaddr *ifa1 = *ifap;
+
+ *ifap = ifa1->ifa_next;
+
+#ifdef CONFIG_RTNETLINK
+ rtmsg_ifa(RTM_DELADDR, ifa1);
+#endif /* CONFIG_RTNETLINK */
+
+ if (destroy) {
+ dn_dev_free_ifa(ifa1);
+
+ if (dn_db->ifa_list == NULL)
+ dn_dev_delete(dn_db->dev);
+ }
+}
+
+static int dn_dev_insert_ifa(struct dn_dev *dn_db, struct dn_ifaddr *ifa)
+{
+ /*
+ * FIXME: Duplicate check here.
+ */
+
+ ifa->ifa_next = dn_db->ifa_list;
+ dn_db->ifa_list = ifa;
+
+#ifdef CONFIG_RTNETLINK
+ rtmsg_ifa(RTM_NEWADDR, ifa);
+#endif /* CONFIG_RTNETLINK */
+
+ return 0;
+}
+
+static int dn_dev_set_ifa(struct device *dev, struct dn_ifaddr *ifa)
+{
+ struct dn_dev *dn_db = dev->dn_ptr;
+
+ if (dn_db == NULL) {
+ int err;
+ dn_db = dn_dev_create(dev, &err);
+ if (dn_db == NULL)
+ return err;
+ }
+
+ ifa->ifa_dev = dn_db;
+
+ if (dev->flags & IFF_LOOPBACK)
+ ifa->ifa_scope = RT_SCOPE_HOST;
+
+ return dn_dev_insert_ifa(dn_db, ifa);
+}
+
+static struct dn_dev *dn_dev_by_index(int ifindex)
+{
+ struct device *dev;
+ dev = dev_get_by_index(ifindex);
+ if (dev)
+ return dev->dn_ptr;
+
+ return NULL;
+}
+
+
+int dn_dev_ioctl(unsigned int cmd, void *arg)
+{
+ char buffer[DN_IFREQ_SIZE];
+ struct ifreq *ifr = (struct ifreq *)buffer;
+ struct sockaddr_dn *sdn = (struct sockaddr_dn *)&ifr->ifr_addr;
+ struct dn_dev *dn_db;
+ struct device *dev;
+ struct dn_ifaddr *ifa = NULL, **ifap = NULL;
+ int exclusive = 0;
+ int ret = 0;
+
+ if (copy_from_user(ifr, arg, DN_IFREQ_SIZE))
+ return -EFAULT;
+ ifr->ifr_name[IFNAMSIZ-1] = 0;
+
+#ifdef CONFIG_KMOD
+ dev_load(ifr->ifr_name);
+#endif
+
+ switch(cmd) {
+ case SIOCGIFADDR:
+ break;
+ case SIOCSIFADDR:
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+ if (sdn->sdn_family != AF_DECnet)
+ return -EINVAL;
+ rtnl_lock();
+ exclusive = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((dev = dev_get(ifr->ifr_name)) == NULL) {
+ ret = -ENODEV;
+ goto done;
+ }
+
+ if ((dn_db = dev->dn_ptr) != NULL) {
+ for (ifap = &dn_db->ifa_list; (ifa=*ifap) != NULL; ifap = &ifa->ifa_next)
+ if (strcmp(ifr->ifr_name, ifa->ifa_label) == 0)
+ break;
+ }
+
+ if (ifa == NULL && cmd != SIOCSIFADDR) {
+ ret = -EADDRNOTAVAIL;
+ goto done;
+ }
+
+ switch(cmd) {
+ case SIOCGIFADDR:
+ *((dn_address *)sdn->sdn_nodeaddr) = ifa->ifa_local;
+ goto rarok;
+
+ case SIOCSIFADDR:
+ if (!ifa) {
+ if ((ifa = dn_dev_alloc_ifa()) == NULL) {
+ ret = -ENOBUFS;
+ break;
+ }
+ memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
+ } else {
+ if (ifa->ifa_local == dn_saddr2dn(sdn))
+ break;
+ dn_dev_del_ifa(dn_db, ifap, 0);
+ }
+
+ ifa->ifa_local = dn_saddr2dn(sdn);
+
+ ret = dn_dev_set_ifa(dev, ifa);
+ }
+done:
+ if (exclusive)
+ rtnl_unlock();
+
+ return ret;
+rarok:
+ if (copy_to_user(arg, ifr, DN_IFREQ_SIZE))
+ return -EFAULT;
+
+ return 0;
+}
+
+#ifdef CONFIG_RTNETLINK
+
+static int dn_dev_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct rtattr **rta = arg;
+ struct dn_dev *dn_db;
+ struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
+ struct dn_ifaddr *ifa, **ifap;
+
+ if ((dn_db = dn_dev_by_index(ifm->ifa_index)) == NULL)
+ return -EADDRNOTAVAIL;
+
+ for(ifap = &dn_db->ifa_list; (ifa=*ifap) != NULL; ifap = &ifa->ifa_next) {
+ void *tmp = rta[IFA_LOCAL-1];
+ if ((tmp && memcmp(RTA_DATA(tmp), &ifa->ifa_local, 2)) ||
+ (rta[IFA_LABEL-1] && strcmp(RTA_DATA(rta[IFA_LABEL-1]), ifa->ifa_label)))
+ continue;
+
+ dn_dev_del_ifa(dn_db, ifap, 1);
+ return 0;
+ }
+
+ return -EADDRNOTAVAIL;
+}
+
+static int dn_dev_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct rtattr **rta = arg;
+ struct device *dev;
+ struct dn_dev *dn_db;
+ struct ifaddrmsg *ifm = NLMSG_DATA(nlh);
+ struct dn_ifaddr *ifa;
+
+ if (rta[IFA_LOCAL-1] == NULL)
+ return -EINVAL;
+
+ if ((dev = dev_get_by_index(ifm->ifa_index)) == NULL)
+ return -ENODEV;
+
+ if ((dn_db = dev->dn_ptr) == NULL) {
+ int err;
+ dn_db = dn_dev_create(dev, &err);
+ if (!dn_db)
+ return err;
+ }
+
+ if ((ifa = dn_dev_alloc_ifa()) == NULL)
+ return -ENOBUFS;
+
+ memcpy(&ifa->ifa_local, RTA_DATA(rta[IFA_LOCAL-1]), 2);
+ ifa->ifa_flags = ifm->ifa_flags;
+ ifa->ifa_scope = ifm->ifa_scope;
+ ifa->ifa_dev = dn_db;
+ if (rta[IFA_LABEL-1])
+ memcpy(ifa->ifa_label, RTA_DATA(rta[IFA_LABEL-1]), IFNAMSIZ);
+ else
+ memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
+
+ return dn_dev_insert_ifa(dn_db, ifa);
+}
+
+static int dn_dev_fill_ifaddr(struct sk_buff *skb, struct dn_ifaddr *ifa,
+ u32 pid, u32 seq, int event)
+{
+ struct ifaddrmsg *ifm;
+ struct nlmsghdr *nlh;
+ unsigned char *b = skb->tail;
+
+ nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*ifm));
+ ifm = NLMSG_DATA(nlh);
+
+ ifm->ifa_family = AF_DECnet;
+ ifm->ifa_prefixlen = 0;
+ ifm->ifa_flags = ifa->ifa_flags | IFA_F_PERMANENT;
+ ifm->ifa_scope = ifa->ifa_scope;
+ ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
+ RTA_PUT(skb, IFA_LOCAL, 2, &ifa->ifa_local);
+ if (ifa->ifa_label[0])
+ RTA_PUT(skb, IFA_LABEL, IFNAMSIZ, &ifa->ifa_label);
+ nlh->nlmsg_len = skb->tail - b;
+ return skb->len;
+nlmsg_failure:
+rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+static void rtmsg_ifa(int event, struct dn_ifaddr *ifa)
+{
+ struct sk_buff *skb;
+ int size = NLMSG_SPACE(sizeof(struct ifaddrmsg)+128);
+
+ skb = alloc_skb(size, GFP_KERNEL);
+ if (!skb) {
+ netlink_set_err(rtnl, 0, RTMGRP_DECnet_IFADDR, ENOBUFS);
+ return;
+ }
+ if (dn_dev_fill_ifaddr(skb, ifa, 0, 0, event) < 0) {
+ kfree_skb(skb);
+ netlink_set_err(rtnl, 0, RTMGRP_DECnet_IFADDR, EINVAL);
+ return;
+ }
+ NETLINK_CB(skb).dst_groups = RTMGRP_DECnet_IFADDR;
+ netlink_broadcast(rtnl, skb, 0, RTMGRP_DECnet_IFADDR, GFP_KERNEL);
+}
+
+static int dn_dev_fill_ifinfo(struct sk_buff *skb, struct device *dev,
+ int type, u32 pid, u32 seq)
+{
+ struct ifinfomsg *r;
+ struct nlmsghdr *nlh;
+ unsigned char *b = skb->tail;
+ struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
+ unsigned char priority = dn_db->parms.priority;
+ unsigned short cost = dn_db->parms.cost;
+
+ nlh = NLMSG_PUT(skb, pid, seq, type, sizeof(*r));
+ if (pid) nlh->nlmsg_flags |= NLM_F_MULTI;
+ r = NLMSG_DATA(nlh);
+
+ r->ifi_family = AF_DECnet;
+ r->ifi_type = dev->type;
+ r->ifi_index = dev->ifindex;
+ r->ifi_flags = dev->flags;
+ r->ifi_change = ~0U;
+
+ RTA_PUT(skb, IFLA_IFNAME, strlen(dev->name)+1, dev->name);
+ RTA_PUT(skb, IFLA_COST, sizeof(unsigned short), &cost);
+ RTA_PUT(skb, IFLA_PRIORITY, sizeof(unsigned char), &priority);
+
+ nlh->nlmsg_len = skb->tail - b;
+ return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+static int dn_dev_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int idx, dn_idx;
+ int s_idx, s_dn_idx;
+ struct device *dev;
+ struct dn_dev *dn_db;
+ struct dn_ifaddr *ifa;
+
+ s_idx = cb->args[0];
+ s_dn_idx = dn_idx = cb->args[1];
+ for(dev = dev_base, idx = 0; dev; dev = dev->next, idx++) {
+ if (idx < s_idx)
+ continue;
+ if (idx > s_idx)
+ s_dn_idx = 0;
+ if ((dn_db = dev->dn_ptr) == NULL)
+ continue;
+
+ for(ifa = dn_db->ifa_list, dn_idx = 0; ifa; ifa = ifa->ifa_next, dn_idx++) {
+ if (dn_idx < s_dn_idx)
+ continue;
+
+ if (dn_dev_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, RTM_NEWADDR) <= 0)
+ goto done;
+ }
+ }
+done:
+ cb->args[0] = idx;
+ cb->args[1] = dn_idx;
+
+ return skb->len;
+}
+
+static int dn_dev_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int idx;
+ int s_idx = cb->args[0];
+ struct device *dev;
+
+ for(dev=dev_base, idx=0; dev; dev = dev->next) {
+ if (!dev->dn_ptr)
+ continue;
+ idx++;
+ if (idx < s_idx)
+ continue;
+ if (dn_dev_fill_ifinfo(skb, dev, RTM_NEWLINK, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq) <= 0)
+ break;
+ }
+ cb->args[0] = idx;
+
+ return skb->len;
+}
+
+static void dn_dev_ifinfo(int type, struct device *dev)
+{
+ struct sk_buff *skb;
+ int size = NLMSG_GOODSIZE;
+
+ skb = alloc_skb(size, GFP_KERNEL);
+ if (!skb)
+ return;
+
+ if (dn_dev_fill_ifinfo(skb, dev, type, 0, 0) < 0) {
+ kfree_skb(skb);
+ return;
+ }
+
+ NETLINK_CB(skb).dst_groups = RTMGRP_LINK;
+ netlink_broadcast(rtnl, skb, 0, NETLINK_CB(skb).dst_groups, GFP_KERNEL);
+}
+#endif /* CONFIG_RTNETLINK */
+
+static void dn_send_endnode_hello(struct device *dev)
+{
+ struct endnode_hello_message *msg;
+ struct sk_buff *skb = NULL;
+ unsigned short int *pktlen;
+ struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
+
+ if ((skb = dn_alloc_skb(NULL, sizeof(*msg), GFP_ATOMIC)) == NULL)
+ return;
+
+ skb->dev = dev;
+
+ msg = (struct endnode_hello_message *)skb_put(skb,sizeof(*msg));
+
+ msg->msgflg = 0x0D;
+ memcpy(msg->tiver, dn_eco_version, 3);
+ memcpy(msg->id, decnet_ether_address, 6);
+ msg->iinfo = DN_RT_INFO_ENDN;
+ msg->blksize = dn_htons(dn_db->parms.blksize);
+ msg->area = 0x00;
+ memset(msg->seed, 0, 8);
+ memcpy(msg->neighbor, dn_hiord, ETH_ALEN);
+
+ if (dn_db->router) {
+ struct dn_neigh *dn = (struct dn_neigh *)dn_db->router;
+ memcpy(msg->neighbor, dn->addr, ETH_ALEN);
+ }
+
+ msg->timer = dn_htons((unsigned short)dn_db->parms.t3);
+ msg->mpd = 0x00;
+ msg->datalen = 0x02;
+ memset(msg->data, 0xAA, 2);
+
+ pktlen = (unsigned short *)skb_push(skb,2);
+ *pktlen = dn_htons(skb->len - 2);
+
+ skb->nh.raw = skb->data;
+
+ if (dev->hard_header(skb,dev, ETH_P_DNA_RT,dn_rt_all_rt_mcast,
+ decnet_ether_address, skb->len) >= 0)
+ dn_send_skb(skb);
+ else
+ kfree_skb(skb);
+}
+
+
+#ifdef CONFIG_DECNET_ROUTER
+
+#define DRDELAY (5 * HZ)
+
+static int dn_am_i_a_router(struct dn_neigh *dn, struct dn_dev *dn_db)
+{
+ /* First check time since device went up */
+ if ((jiffies - dn_db->uptime) < DRDELAY)
+ return 0;
+
+ /* If there is no router, then yes... */
+ if (!dn_db->router)
+ return 1;
+
+ /* otherwise only if we have a higher priority or.. */
+ if (dn->priority < dn_db->parms.priority)
+ return 1;
+
+ /* if we have equal priority and a higher node number */
+ if (dn->priority != dn_db->parms.priority)
+ return 0;
+
+ if (dn_ntohs(dn_eth2dn(dn->addr)) < dn_ntohs(decnet_address))
+ return 1;
+
+ return 0;
+}
+
+static void dn_send_router_hello(struct device *dev)
+{
+ int n;
+ struct dn_dev *dn_db = dev->dn_ptr;
+ struct dn_neigh *dn = (struct dn_neigh *)dn_db->router;
+ struct sk_buff *skb;
+ size_t size;
+ unsigned char *ptr;
+ unsigned char *i1, *i2;
+ unsigned short *pktlen;
+
+ if (dn_db->parms.blksize < (26 + 7))
+ return;
+
+ n = dn_db->parms.blksize - 26;
+ n /= 7;
+
+ if (n > 32)
+ n = 32;
+
+ size = 2 + 26 + 7 * n;
+
+ if ((skb = dn_alloc_skb(NULL, size, GFP_ATOMIC)) == NULL)
+ return;
+
+ skb->dev = dev;
+ ptr = skb_put(skb, size);
+
+ *ptr++ = DN_RT_PKT_CNTL | DN_RT_PKT_ERTH;
+ *ptr++ = 2; /* ECO */
+ *ptr++ = 0;
+ *ptr++ = 0;
+ memcpy(ptr, decnet_ether_address, ETH_ALEN);
+ ptr += ETH_ALEN;
+ *ptr++ = (unsigned char)decnet_node_type;
+ *((unsigned short *)ptr) = dn_htons(dn_db->parms.blksize);
+ ptr += 2;
+ *ptr++ = 0; /* Priority */
+ *ptr++ = 0; /* Area: Reserved */
+ *((unsigned short *)ptr) = dn_htons((unsigned short)dn_db->parms.t3);
+ ptr += 2;
+ *ptr++ = 0; /* MPD: Reserved */
+ i1 = ptr++;
+ memset(ptr, 0, 7); /* Name: Reserved */
+ i2 = ptr++;
+
+ n = dn_neigh_elist(dev, ptr, n);
+
+ *i2 = 7 * n;
+ *i1 = 8 + *i2;
+
+ skb_trim(skb, (26 + *i2));
+
+ pktlen = (unsigned short *)skb_push(skb, 2);
+ *pktlen = dn_htons(skb->len - 2);
+
+ skb->nh.raw = skb->data;
+
+ if (dn_am_i_a_router(dn, dn_db)) {
+ struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
+ if (skb2) {
+ if (dev->hard_header(skb2, dev, ETH_P_DNA_RT,
+ dn_rt_all_end_mcast,
+ decnet_ether_address,
+ skb2->len) >= 0)
+ dn_send_skb(skb2);
+ else
+ kfree_skb(skb2);
+ }
+ }
+
+ if (dev->hard_header(skb, dev, ETH_P_DNA_RT, dn_rt_all_rt_mcast,
+ decnet_ether_address, skb->len) >= 0)
+ dn_send_skb(skb);
+ else
+ kfree_skb(skb);
+}
+
+static void dn_send_brd_hello(struct device *dev)
+{
+ if (decnet_node_type == DN_RT_INFO_ENDN)
+ dn_send_endnode_hello(dev);
+ else
+ dn_send_router_hello(dev);
+}
+#else
+static void dn_send_brd_hello(struct device *dev)
+{
+ dn_send_endnode_hello(dev);
+}
+#endif
+
+static void dn_send_ptp_hello(struct device *dev)
+{
+ int tdlen = 16;
+ int size = dev->hard_header_len + 2 + 4 + tdlen;
+ struct sk_buff *skb = dn_alloc_skb(NULL, size, GFP_ATOMIC);
+ struct dn_dev *dn_db = dev->dn_ptr;
+ unsigned char *ptr;
+ int i;
+
+ if (skb == NULL)
+ return ;
+
+ skb->dev = dev;
+ skb_push(skb, dev->hard_header_len);
+ ptr = skb_put(skb, 2 + 4 + tdlen);
+
+ *ptr++ = DN_RT_PKT_HELO;
+ *((dn_address *)ptr) = decnet_address;
+ ptr += 2;
+ *ptr++ = tdlen;
+
+ for(i = 0; i < tdlen; i++)
+ *ptr++ = 0252;
+
+ if (dn_db->router) {
+ struct dn_neigh *dn = (struct dn_neigh *)dn_db->router;
+ if (memcmp(dn->addr, decnet_ether_address, ETH_ALEN) == 0) {
+ struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2 && dev->hard_header(skb2, dev, ETH_P_DNA_RT,
+ dn_rt_all_end_mcast,
+ decnet_ether_address,
+ skb->len) >= 0) {
+
+ dn_send_skb(skb2);
+ }
+ }
+ }
+
+ if (dev->hard_header(skb, dev, ETH_P_DNA_RT, dn_rt_all_rt_mcast,
+ decnet_ether_address, skb->len) < 0);
+ return;
+
+ dn_send_skb(skb);
+}
+
+static int dn_eth_up(struct device *dev)
+{
+ struct dn_dev *dn_db = dev->dn_ptr;
+
+ if (decnet_node_type == DN_RT_INFO_ENDN)
+ dev_mc_add(dev, dn_rt_all_end_mcast, ETH_ALEN, 0);
+
+ if (decnet_node_type == DN_RT_INFO_L1RT || decnet_node_type == DN_RT_INFO_L2RT)
+ dev_mc_add(dev, dn_rt_all_rt_mcast, ETH_ALEN, 0);
+
+ dev_mc_upload(dev);
+
+ dn_db->use_long = 1;
+
+ return 0;
+}
+
+static int dn_dev_eth_setsrc(struct sk_buff *skb)
+{
+ struct ethhdr *h = skb->mac.ethernet;
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+
+ if (h == NULL)
+ return -1;
+
+ cb->neigh = dn_eth2dn(h->h_source);
+
+ return 0;
+}
+
+static int dn_dev_lo_setsrc(struct sk_buff *skb)
+{
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+
+ cb->neigh = decnet_address;
+
+ return 0;
+}
+
+static int dn_dev_eth_neigh_setup(struct neighbour *n)
+{
+ struct dn_neigh *dn = (struct dn_neigh *)n;
+
+ memcpy(n->ha, dn->addr, ETH_ALEN);
+
+ return 0;
+}
+
+static int dn_dev_ptp_setsrc(struct sk_buff *skb)
+{
+ struct device *dev = skb->dev;
+ struct dn_dev *dn_db = dev->dn_ptr;
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+
+ if (!dn_db->peer)
+ return -1;
+
+ cb->neigh = dn_eth2dn(((struct dn_neigh *)dn_db->peer)->addr);
+
+ return 0;
+}
+
+static void dn_dev_set_timer(struct device *dev);
+
+static void dn_dev_timer_func(unsigned long arg)
+{
+ struct device *dev = (struct device *)arg;
+ struct dn_dev *dn_db = dev->dn_ptr;
+
+#ifdef CONFIG_DECNET_ROUTER
+ if (decnet_node_type == DN_RT_INFO_L1RT || decnet_node_type == DN_RT_INFO_L2RT) {
+ if (dn_db->t1 <= dn_db->parms.t2) {
+ if (dn_db->parms.timer1)
+ dn_db->parms.timer1(dev);
+ dn_db->t1 = dn_db->parms.t1;
+ } else {
+ dn_db->t1 -= dn_db->parms.t2;
+ }
+ }
+#endif /* CONFIG_DECNET_ROUTER */
+
+ if (dn_db->t3 <= dn_db->parms.t2) {
+ if (dn_db->parms.timer3)
+ dn_db->parms.timer3(dev);
+ dn_db->t3 = dn_db->parms.t3;
+ } else {
+ dn_db->t3 -= dn_db->parms.t2;
+ }
+
+ dn_dev_set_timer(dev);
+}
+
+static void dn_dev_set_timer(struct device *dev)
+{
+ struct dn_dev *dn_db = dev->dn_ptr;
+
+#ifdef CONFIG_DECNET_ROUTER
+ if (dn_db->parms.t2 > dn_db->parms.t1)
+ dn_db->parms.t2 = dn_db->parms.t1;
+#endif
+
+ if (dn_db->parms.t2 > dn_db->parms.t3)
+ dn_db->parms.t2 = dn_db->parms.t3;
+
+ dn_db->timer.data = (unsigned long)dev;
+ dn_db->timer.function = dn_dev_timer_func;
+ dn_db->timer.expires = jiffies + (dn_db->parms.t2 * HZ);
+
+ add_timer(&dn_db->timer);
+}
+
+struct dn_dev *dn_dev_create(struct device *dev, int *err)
+{
+ int i;
+ struct dn_dev_parms *p = dn_dev_list;
+ struct dn_dev *dn_db;
+
+ for(i = 0; i < DN_DEV_LIST_SIZE; i++, p++) {
+ if (p->type == dev->type)
+ break;
+ }
+
+ *err = -ENODEV;
+ if (i == DN_DEV_LIST_SIZE)
+ return NULL;
+
+ *err = -ENOBUFS;
+ if ((dn_db = kmalloc(sizeof(struct dn_dev), GFP_ATOMIC)) == NULL)
+ return NULL;
+
+ memset(dn_db, 0, sizeof(struct dn_dev));
+ memcpy(&dn_db->parms, p, sizeof(struct dn_dev_parms));
+ dev->dn_ptr = dn_db;
+ dn_db->dev = dev;
+ init_timer(&dn_db->timer);
+
+ memcpy(dn_db->addr, decnet_ether_address, ETH_ALEN); /* To go... */
+
+ dn_db->uptime = jiffies;
+ if (dn_db->parms.up) {
+ if (dn_db->parms.up(dev) < 0) {
+ dev->dn_ptr = NULL;
+ kfree(dn_db);
+ return NULL;
+ }
+ }
+
+ dn_db->neigh_parms = neigh_parms_alloc(dev, &dn_neigh_table);
+ dn_db->neigh_parms->neigh_setup = dn_db->parms.neigh_setup;
+
+ dn_dev_sysctl_register(dev, &dn_db->parms);
+
+ dn_dev_set_timer(dev);
+
+#ifdef CONFIG_RTNETLINK
+ dn_dev_ifinfo(RTM_NEWLINK, dev);
+#endif
+
+ *err = 0;
+ return dn_db;
+}
+
+
+/*
+ * This processes a device up event. We only start up
+ * the loopback device & ethernet devices with correct
+ * MAC addreses automatically. Others must be started
+ * specifically.
+ */
+void dn_dev_up(struct device *dev)
+{
+ struct dn_ifaddr *ifa;
+
+ if ((dev->type != ARPHRD_ETHER) && (dev->type != ARPHRD_LOOPBACK))
+ return;
+
+ if (dev->type == ARPHRD_ETHER)
+ if (memcmp(dev->dev_addr, decnet_ether_address, ETH_ALEN) != 0)
+ return;
+
+ if ((ifa = dn_dev_alloc_ifa()) == NULL)
+ return;
+
+ ifa->ifa_local = decnet_address;
+ ifa->ifa_flags = 0;
+ ifa->ifa_scope = RT_SCOPE_UNIVERSE;
+ strcpy(ifa->ifa_label, dev->name);
+
+ dn_dev_set_ifa(dev, ifa);
+}
+
+static void dn_dev_delete(struct device *dev)
+{
+ struct dn_dev *dn_db = dev->dn_ptr;
+ unsigned long cpuflags;
+
+ if (dn_db == NULL)
+ return;
+
+ save_flags(cpuflags);
+ cli();
+ del_timer(&dn_db->timer);
+ restore_flags(cpuflags);
+
+#ifdef CONFIG_RTNETLINK
+ dn_dev_ifinfo(RTM_DELLINK, dev);
+#endif
+
+ dn_dev_sysctl_unregister(&dn_db->parms);
+
+ neigh_ifdown(&dn_neigh_table, dev);
+
+ if (dev == decnet_default_device)
+ decnet_default_device = NULL;
+
+ if (dn_db->parms.down)
+ dn_db->parms.down(dev);
+
+ dev->dn_ptr = NULL;
+
+ neigh_parms_release(&dn_neigh_table, dn_db->neigh_parms);
+
+ if (dn_db->router)
+ neigh_release(dn_db->router);
+ if (dn_db->peer)
+ neigh_release(dn_db->peer);
+
+ kfree(dn_db);
+}
+
+void dn_dev_down(struct device *dev)
+{
+ struct dn_dev *dn_db = dev->dn_ptr;
+ struct dn_ifaddr *ifa;
+
+ if (dn_db == NULL)
+ return;
+
+ while((ifa = dn_db->ifa_list) != NULL) {
+ dn_dev_del_ifa(dn_db, &dn_db->ifa_list, 0);
+ dn_dev_free_ifa(ifa);
+ }
+
+ dn_dev_delete(dev);
+}
+
+void dn_dev_init_pkt(struct sk_buff *skb)
+{
+ return;
+}
+
+void dn_dev_veri_pkt(struct sk_buff *skb)
+{
+ return;
+}
+
+void dn_dev_hello(struct sk_buff *skb)
+{
+ return;
+}
+
+void dn_dev_devices_off(void)
+{
+ struct device *dev;
+
+ for(dev = dev_base; dev; dev = dev->next)
+ dn_dev_down(dev);
+
+}
+
+void dn_dev_devices_on(void)
+{
+ struct device *dev;
+
+ for(dev = dev_base; dev; dev = dev->next) {
+ if (dev->flags & IFF_UP)
+ dn_dev_up(dev);
+ }
+}
+
+
+#ifdef CONFIG_DECNET_SIOCGIFCONF
+/*
+ * Now we support multiple addresses per interface.
+ * Since we don't want to break existing code, you have to enable
+ * it as a compile time option. Probably you should use the
+ * rtnetlink interface instead.
+ */
+int dnet_gifconf(struct device *dev, char *buf, int len)
+{
+ struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
+ struct dn_ifaddr *ifa;
+ struct ifreq *ifr = (struct ifreq *)buf;
+ int done = 0;
+
+ if ((dn_db == NULL) || ((ifa = dn_db->ifa_list) == NULL))
+ return 0;
+
+ for(; ifa; ifa = ifa->ifa_next) {
+ if (!ifr) {
+ done += sizeof(DN_IFREQ_SIZE);
+ continue;
+ }
+ if (len < DN_IFREQ_SIZE)
+ return done;
+ memset(ifr, 0, DN_IFREQ_SIZE);
+
+ if (ifa->ifa_label)
+ strcpy(ifr->ifr_name, ifa->ifa_label);
+ else
+ strcpy(ifr->ifr_name, dev->name);
+
+ (*(struct sockaddr_dn *) &ifr->ifr_addr).sdn_family = AF_DECnet;
+ (*(struct sockaddr_dn *) &ifr->ifr_addr).sdn_add.a_len = 2;
+ (*(dn_address *)(*(struct sockaddr_dn *) &ifr->ifr_addr).sdn_add.a_addr) = ifa->ifa_local;
+
+ ifr = (struct ifreq *)((char *)ifr + DN_IFREQ_SIZE);
+ len -= DN_IFREQ_SIZE;
+ done += DN_IFREQ_SIZE;
+ }
+
+ return done;
+}
+#endif /* CONFIG_DECNET_SIOCGIFCONF */
+
+
+#ifdef CONFIG_PROC_FS
+
+static char *dn_type2asc(char type)
+{
+ switch(type) {
+ case DN_DEV_BCAST:
+ return "B";
+ case DN_DEV_UCAST:
+ return "U";
+ case DN_DEV_MPOINT:
+ return "M";
+ }
+
+ return "?";
+}
+
+static int decnet_dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ struct dn_dev *dn_db;
+ struct device *dev;
+ int len = 0;
+ off_t pos = 0;
+ off_t begin = 0;
+ char peer_buf[DN_ASCBUF_LEN];
+ char router_buf[DN_ASCBUF_LEN];
+
+ cli();
+
+ len += sprintf(buffer, "Name Flags T1 Timer1 T3 Timer3 BlkSize Pri State DevType Router Peer\n");
+
+ for (dev = dev_base; dev; dev = dev->next) {
+ if ((dn_db = (struct dn_dev *)dev->dn_ptr) == NULL)
+ continue;
+
+ len += sprintf(buffer + len, "%-8s %1s %04lu %04lu %04lu %04lu %04hu %03d %02x %-10s %-7s %-7s\n",
+ dev->name ? dev->name : "???",
+ dn_type2asc(dn_db->parms.mode),
+ dn_db->t1, dn_db->parms.t1,
+ dn_db->t3, dn_db->parms.t3,
+ dn_db->parms.blksize,
+ dn_db->parms.priority,
+ dn_db->parms.state, dn_db->parms.name,
+ dn_db->router ? dn_addr2asc(dn_eth2dn(dn_db->router->primary_key), router_buf) : "",
+ dn_db->peer ? dn_addr2asc(dn_eth2dn(dn_db->peer->primary_key), peer_buf) : "");
+
+
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ break;
+ }
+
+ sti();
+
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
+
+ if (len > length) len = length;
+
+ return(len);
+}
+
+static struct proc_dir_entry proc_net_decnet_dev = {
+ PROC_NET_DN_DEV, 10, "decnet_dev",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ decnet_dev_get_info
+};
+
+#endif /* CONFIG_PROC_FS */
+
+#ifdef CONFIG_RTNETLINK
+static struct rtnetlink_link dnet_rtnetlink_table[RTM_MAX-RTM_BASE+1] =
+{
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, dn_dev_dump_ifinfo, },
+ { NULL, NULL, },
+
+ { dn_dev_rtm_newaddr, NULL, },
+ { dn_dev_rtm_deladdr, NULL, },
+ { NULL, dn_dev_dump_ifaddr, },
+ { NULL, NULL, },
+#ifdef CONFIG_DECNET_ROUTER
+ { dn_fib_rtm_newroute, NULL, },
+ { dn_fib_rtm_delroute, NULL, },
+ { dn_fib_rtm_getroute, dn_fib_dump, },
+ { NULL, NULL, },
+#else
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, NULL, },
+#endif
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, NULL, },
+
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, NULL, },
+ { NULL, NULL, }
+};
+#endif /* CONFIG_RTNETLINK */
+
+void __init dn_dev_init(void)
+{
+
+ dn_dev_devices_on();
+#ifdef CONFIG_DECNET_SIOCGIFCONF
+ register_gifconf(PF_DECnet, dnet_gifconf);
+#endif /* CONFIG_DECNET_SIOCGIFCONF */
+
+#ifdef CONFIG_RTNETLINK
+ rtnetlink_links[PF_DECnet] = dnet_rtnetlink_table;
+#endif /* CONFIG_RTNETLINK */
+
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_net_decnet_dev);
+#endif /* CONFIG_PROC_FS */
+
+#ifdef CONFIG_SYSCTL
+ {
+ int i;
+ for(i = 0; i < DN_DEV_LIST_SIZE; i++)
+ dn_dev_sysctl_register(NULL, &dn_dev_list[i]);
+ }
+#endif /* CONFIG_SYSCTL */
+}
+
+#if defined(CONFIG_DECNET_MODULE)
+void dn_dev_cleanup(void)
+{
+#ifdef CONFIG_RTNETLINK
+ rtnetlink_links[PF_DECnet] = NULL;
+#endif /* CONFIG_RTNETLINK */
+
+#ifdef CONFIG_DECNET_SIOCGIFCONF
+ unregister_gifconf(PF_DECnet);
+#endif /* CONFIG_DECNET_SIOCGIFCONF */
+
+#ifdef CONFIG_SYSCTL
+ {
+ int i;
+ for(i = 0; i < DN_DEV_LIST_SIZE; i++)
+ dn_dev_sysctl_unregister(&dn_dev_list[i]);
+ }
+#endif /* CONFIG_SYSCTL */
+
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(PROC_NET_DN_DEV);
+#endif /* CONFIG_PROC_FS */
+
+ dn_dev_devices_off();
+}
+#endif /* CONFIG_DECNET_MODULE */
diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c
new file mode 100644
index 000000000..16f2e3e03
--- /dev/null
+++ b/net/decnet/dn_fib.c
@@ -0,0 +1,805 @@
+/*
+ * DECnet An implementation of the DECnet protocol suite for the LINUX
+ * operating system. DECnet is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * DECnet Routing Forwarding Information Base
+ *
+ * Author: Steve Whitehouse <SteveW@ACM.org>
+ *
+ *
+ * Changes:
+ *
+ */
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+#include <linux/timer.h>
+#include <linux/rtnetlink.h>
+#include <asm/spinlock.h>
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+#include <net/neighbour.h>
+#include <net/dst.h>
+#include <net/dn.h>
+#include <net/dn_fib.h>
+#include <net/dn_neigh.h>
+#include <net/dn_dev.h>
+
+/*
+ * N.B. Some of the functions here should really be inlines, but
+ * I'll sort out that when its all working properly, for now the
+ * stack frames will be useful for debugging.
+ */
+#define DN_NUM_TABLES 255
+#define DN_MIN_TABLE 1
+#define DN_L1_TABLE 1
+#define DN_L2_TABLE 2
+
+#ifdef CONFIG_RTNETLINK
+static int dn_fib_table_dump(struct dn_fib_table *t, struct sk_buff *skb, struct netlink_callback *cb);
+static void dn_rtmsg_fib(int event, int table, struct dn_fib_action *fa, struct nlmsghdr *nlh, struct netlink_skb_parms *req);
+#endif /* CONFIG_RTNETLINK */
+
+static void dn_fib_del_tree(struct dn_fib_table *t);
+
+static struct dn_fib_table *dn_fib_tables[DN_NUM_TABLES + 1];
+static int dn_fib_allocs = 0;
+static int dn_fib_actions = 0;
+
+static struct dn_fib_node *dn_fib_alloc(void)
+{
+ struct dn_fib_node *fn;
+
+ fn = kmalloc(sizeof(struct dn_fib_node), GFP_KERNEL);
+
+ if (fn) {
+ memset(fn, 0, sizeof(struct dn_fib_node));
+ dn_fib_allocs++;
+ }
+
+ return fn;
+}
+
+
+static __inline__ void dn_fib_free(struct dn_fib_node *fn)
+{
+ kfree_s(fn, sizeof(struct dn_fib_node));
+ dn_fib_allocs--;
+}
+
+static struct dn_fib_action *dn_fib_new_action(void)
+{
+ struct dn_fib_action *fa;
+
+ fa = kmalloc(sizeof(struct dn_fib_action), GFP_KERNEL);
+
+ if (fa) {
+ memset(fa, 0, sizeof(struct dn_fib_action));
+ dn_fib_actions++;
+ }
+
+ return fa;
+}
+
+static __inline__ void dn_fib_del_action(struct dn_fib_action *fa)
+{
+ if ((fa->fa_type == RTN_UNICAST) && fa->fa_neigh)
+ neigh_release(fa->fa_neigh);
+
+ kfree_s(fa, sizeof(struct dn_fib_action));
+ dn_fib_actions--;
+}
+
+static struct dn_fib_node *dn_fib_follow(struct dn_fib_node *fn, dn_address key)
+{
+ while(fn->fn_action == NULL)
+ fn = DN_FIB_NEXT(fn, key);
+
+ return fn;
+}
+
+
+static struct dn_fib_node *dn_fib_follow1(struct dn_fib_node *fn, dn_address key)
+{
+ while((fn->fn_action == NULL) && (((key ^ fn->fn_key) >> fn->fn_shift) == 0))
+ fn = DN_FIB_NEXT(fn, key);
+
+ return fn;
+}
+
+
+static int dn_fib_table_insert1(struct dn_fib_table *t, struct dn_fib_node *leaf)
+{
+ struct dn_fib_node *fn, *fn1, *fn2;
+ int shift = -1;
+ dn_address match;
+ dn_address cmpmask = 1;
+
+ if (!t->root) {
+ t->root = leaf;
+ t->count++;
+ return 0;
+ }
+
+ fn1 = dn_fib_follow1(t->root, leaf->fn_key);
+ fn2 = fn1->fn_up;
+
+ if (fn1->fn_key == leaf->fn_key)
+ return -EEXIST;
+
+ if ((fn = dn_fib_alloc()) == NULL)
+ return -ENOBUFS;
+
+ fn->fn_key = leaf->fn_key;
+ match = fn1->fn_key ^ fn->fn_key;
+
+ while(match) {
+ match >>= 1;
+ shift++;
+ }
+ cmpmask <<= shift;
+
+ fn->fn_cmpmask = cmpmask;
+ fn->fn_shift = shift;
+
+ if (fn2) {
+ DN_FIB_NEXT(fn2, fn->fn_key) = fn;
+ } else {
+ t->root = fn;
+ }
+
+ t->count++;
+ fn->fn_up = fn2;
+ DN_FIB_NEXT(fn, fn1->fn_key) = fn1;
+ DN_FIB_NEXT(fn, leaf->fn_key) = leaf;
+
+ return 0;
+}
+
+static __inline__ int dn_maskcmp(dn_address m1, dn_address m2)
+{
+ int cmp = 0;
+
+ while(m1 || m2) {
+ if (m1 & 0x8000)
+ cmp++;
+ if (m2 & 0x8000)
+ cmp--;
+ m1 <<= 1;
+ m2 <<= 1;
+ }
+
+ return cmp;
+}
+
+
+static int dn_fib_table_insert(struct dn_fib_table *t, struct dn_fib_action *fa)
+{
+ struct dn_fib_node *fn;
+ struct dn_fib_action **fap;
+ int err;
+ int cmp;
+
+ if (t->root && ((fn = dn_fib_follow(t->root, fa->fa_key)) != NULL) &&
+ (fn->fn_key == fa->fa_key))
+ goto add_action;
+
+ if ((fn = dn_fib_alloc()) == NULL)
+ return -ENOBUFS;
+
+ fn->fn_key = fa->fa_key;
+ fn->fn_action = fa;
+
+ if ((err = dn_fib_table_insert1(t, fn)) < 0)
+ dn_fib_free(fn);
+
+#ifdef CONFIG_RTNETLINK
+ if (!err)
+ dn_rtmsg_fib(RTM_NEWROUTE, t->n, fa, NULL, NULL);
+#endif /* CONFIG_RTNETLINK */
+
+ return err;
+
+add_action:
+ fap = &fn->fn_action;
+
+ for(; *fap; fap = &((*fap)->fa_next)) {
+ if ((cmp = dn_maskcmp((*fap)->fa_mask, fa->fa_mask)) > 0)
+ break;
+ if (cmp < 0)
+ continue;
+ if ((*fap)->fa_cost > fa->fa_cost)
+ break;
+ }
+
+ fa->fa_next = *fap;
+ *fap = fa;
+
+#ifdef CONFIG_RTNETLINK
+ dn_rtmsg_fib(RTM_NEWROUTE, t->n, fa, NULL, NULL);
+#endif /* CONFIG_RTNETLINK */
+
+ return 0;
+}
+
+static int dn_fib_table_delete1(struct dn_fib_table *t, struct dn_fib_node *fn)
+{
+ struct dn_fib_node *fn1 = fn->fn_up;
+ struct dn_fib_node *fn2;
+ struct dn_fib_node *fn3;
+
+ if (fn == t->root) {
+ t->root = NULL;
+ t->count--;
+ return 0;
+ }
+
+ if (fn1 == NULL)
+ return -EINVAL;
+
+ fn2 = fn1->fn_up;
+ fn3 = DN_FIB_NEXT(fn1, ~fn->fn_key);
+
+ if (fn2)
+ DN_FIB_NEXT(fn2, fn1->fn_key) = fn3;
+ else
+ t->root = fn3;
+
+ fn3->fn_up = fn2;
+
+ dn_fib_free(fn1);
+ t->count--;
+ return 0;
+}
+
+static int dn_fib_table_delete(struct dn_fib_table *t, struct dn_fib_action *fa)
+{
+ struct dn_fib_res res;
+ struct dn_fib_node *fn;
+ struct dn_fib_action **fap, *old;
+ int err;
+
+ res.res_type = 0;
+ res.res_addr = fa->fa_key;
+ res.res_mask = fa->fa_mask;
+ res.res_ifindex = fa->fa_ifindex;
+ res.res_proto = fa->fa_proto;
+ res.res_cost = fa->fa_cost;
+
+ if ((err = t->lookup(t, &res)) < 0)
+ return err;
+
+ fn = res.res_fn;
+ fap = &fn->fn_action;
+ while((*fap) != res.res_fa)
+ fap = &((*fap)->fa_next);
+ old = *fap;
+ *fap = (*fap)->fa_next;
+
+ if (fn->fn_action == NULL)
+ dn_fib_table_delete1(t, fn);
+
+ if (t->root == NULL)
+ dn_fib_del_tree(t);
+
+#ifdef CONFIG_RTNETLINK
+ dn_rtmsg_fib(RTM_DELROUTE, t->n, old, NULL, NULL);
+#endif /* CONFIG_RTNETLINK */
+
+ dn_fib_del_action(old);
+
+ return 0;
+}
+
+static int dn_fib_search(struct dn_fib_node *fn, struct dn_fib_res *res)
+{
+ struct dn_fib_action *fa = fn->fn_action;
+
+ for(; fa; fa = fa->fa_next) {
+ if ((fa->fa_key ^ res->res_addr) & fa->fa_mask)
+ continue;
+ if (res->res_ifindex && (res->res_ifindex != fa->fa_ifindex))
+ continue;
+ if (res->res_mask && (res->res_mask != fa->fa_mask))
+ continue;
+ if (res->res_proto && (res->res_proto != fa->fa_proto))
+ continue;
+ if (res->res_cost && (res->res_cost != fa->fa_cost))
+ continue;
+
+ res->res_fn = fn;
+ res->res_fa = fa;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int dn_fib_recurse(struct dn_fib_node *fn, struct dn_fib_res *res)
+{
+ struct dn_fib_node *fn1;
+ int err = -ENOENT;
+
+ fn1 = dn_fib_follow(fn, res->res_addr);
+
+ if (dn_fib_search(fn1, res))
+ return 0;
+
+ while((fn1 = fn1->fn_up) != fn)
+ if ((err = dn_fib_recurse(DN_FIB_NEXT(fn1, ~res->res_addr), res)) == 0)
+ break;
+
+ return err;
+}
+
+static int dn_fib_table_lookup(struct dn_fib_table *t, struct dn_fib_res *res)
+{
+ struct dn_fib_node *fn = t->root;
+ int err = -ENOENT;
+
+ if (t->root == NULL)
+ return err;
+
+ fn = dn_fib_follow(t->root, res->res_addr);
+
+ if (dn_fib_search(fn, res))
+ return 0;
+
+ while((fn = fn->fn_up) != NULL)
+ if ((err = dn_fib_recurse(DN_FIB_NEXT(fn, ~res->res_addr), res)) == 0)
+ break;
+
+ return err;
+}
+
+static int dn_fib_table_walk_recurse(struct dn_fib_walker_t *fwt, struct dn_fib_node *fn)
+{
+ struct dn_fib_table *t = fwt->table;
+
+ if (fn->fn_action) {
+ fwt->fxn(fwt, fn);
+ } else {
+ dn_fib_table_walk_recurse(fwt, t->root->fn_children[0]);
+ dn_fib_table_walk_recurse(fwt, t->root->fn_children[1]);
+ }
+
+ return 0;
+}
+
+static int dn_fib_table_walk(struct dn_fib_walker_t *fwt)
+{
+ struct dn_fib_table *t = fwt->table;
+
+ if (t->root != NULL) {
+ if (t->root->fn_action) {
+ fwt->fxn(fwt, t->root);
+ } else {
+ dn_fib_table_walk_recurse(fwt, t->root->fn_children[0]);
+ dn_fib_table_walk_recurse(fwt, t->root->fn_children[1]);
+ }
+ }
+
+ return 0;
+}
+
+static struct dn_fib_table *dn_fib_get_tree(int n, int create)
+{
+ struct dn_fib_table *t;
+
+ if (n < DN_MIN_TABLE)
+ return NULL;
+
+ if (n > DN_NUM_TABLES)
+ return NULL;
+
+ if (dn_fib_tables[n])
+ return dn_fib_tables[n];
+
+ if (!create)
+ return NULL;
+
+ if ((t = kmalloc(sizeof(struct dn_fib_table), GFP_KERNEL)) == NULL)
+ return NULL;
+
+ dn_fib_tables[n] = t;
+ memset(t, 0, sizeof(struct dn_fib_table));
+
+ t->n = n;
+ t->insert = dn_fib_table_insert;
+ t->delete = dn_fib_table_delete;
+ t->lookup = dn_fib_table_lookup;
+ t->walk = dn_fib_table_walk;
+#ifdef CONFIG_RTNETLINK
+ t->dump = dn_fib_table_dump;
+#endif
+
+ return t;
+}
+
+static void dn_fib_del_tree(struct dn_fib_table *t)
+{
+ dn_fib_tables[t->n] = NULL;
+
+ if (t) {
+ kfree_s(t, sizeof(struct dn_fib_table));
+ }
+}
+
+
+int dn_fib_resolve(struct dn_fib_res *res)
+{
+ int table = DN_L1_TABLE;
+ int count = 0;
+ struct dn_fib_action *fa;
+ int err;
+
+ if ((res->res_addr ^ dn_ntohs(decnet_address)) & 0xfc00)
+ table = DN_L2_TABLE;
+
+ for(;;) {
+ struct dn_fib_table *t = dn_fib_get_tree(table, 0);
+
+ if (t == NULL)
+ return -ENOBUFS;
+
+ if ((err = t->lookup(t, res)) < 0)
+ return err;
+
+ if ((fa = res->res_fa) == NULL)
+ return -ENOENT;
+
+ if (fa->fa_type != RTN_THROW)
+ break;
+
+ table = fa->fa_table;
+
+ if (count++ > DN_NUM_TABLES)
+ return -ENOENT;
+ }
+
+ return (fa->fa_type == RTN_PROHIBIT) ? -fa->fa_error : 0;
+}
+
+/*
+ * Punt to user via netlink for example, but for now
+ * we just drop it.
+ */
+int dn_fib_rt_message(struct sk_buff *skb)
+{
+ kfree_skb(skb);
+
+ return 0;
+}
+
+
+#ifdef CONFIG_RTNETLINK
+static int dn_fib_convert_rtm(struct dn_fib_action *fa,
+ struct rtmsg *r, struct rtattr **rta,
+ struct nlmsghdr *n,
+ struct netlink_skb_parms *req)
+{
+ dn_address dst, gw, mask = 0xffff;
+ int ifindex;
+ struct neighbour *neigh;
+ struct device *dev;
+ unsigned char addr[ETH_ALEN];
+
+ if (r->rtm_family != AF_DECnet)
+ return -EINVAL;
+
+ if (rta[RTA_DST-1])
+ memcpy(&dst, RTA_DATA(rta[RTA_DST-1]), 2);
+
+ if (rta[RTA_OIF-1])
+ memcpy(&ifindex, RTA_DATA(rta[RTA_OIF-1]), sizeof(int));
+
+ if (rta[RTA_GATEWAY-1])
+ memcpy(&gw, RTA_DATA(rta[RTA_GATEWAY-1]), 2);
+
+ fa->fa_key = dn_ntohs(dst);
+ fa->fa_mask = mask;
+ fa->fa_ifindex = ifindex;
+ fa->fa_proto = r->rtm_protocol;
+ fa->fa_type = r->rtm_type;
+
+ switch(fa->fa_type) {
+ case RTN_UNICAST:
+ if ((dev = dev_get_by_index(ifindex)) == NULL)
+ return -ENODEV;
+ dn_dn2eth(addr, gw);
+ if ((neigh = __neigh_lookup(&dn_neigh_table, &addr, dev, 1)) == NULL)
+ return -EHOSTUNREACH;
+ fa->fa_neigh = neigh;
+ break;
+ case RTN_THROW:
+ fa->fa_table = 0;
+ break;
+ case RTN_PROHIBIT:
+ fa->fa_error = 0;
+ break;
+ case RTN_UNREACHABLE:
+ fa->fa_error = EHOSTUNREACH;
+ break;
+ }
+
+ return 0;
+}
+
+static int dn_fib_check_attr(struct rtmsg *r, struct rtattr **rta)
+{
+ switch(r->rtm_type) {
+ case RTN_UNICAST:
+ case RTN_BLACKHOLE:
+ case RTN_PROHIBIT:
+ case RTN_UNREACHABLE:
+ case RTN_THROW:
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct dn_fib_table *t;
+ struct rtattr **rta = arg;
+ struct rtmsg *r = NLMSG_DATA(nlh);
+ struct dn_fib_action *fa;
+ int err;
+
+ if (dn_fib_check_attr(r, rta))
+ return -EINVAL;
+
+ if ((fa = dn_fib_new_action()) == NULL)
+ return -ENOBUFS;
+
+ t = dn_fib_get_tree(r->rtm_table, 0);
+ if (t) {
+ if ((err = dn_fib_convert_rtm(fa, r, rta, nlh, &NETLINK_CB(skb))) < 0) {
+ dn_fib_del_action(fa);
+ return err;
+ }
+ err = t->delete(t, fa);
+ dn_fib_del_action(fa);
+ return err;
+ }
+ return -ESRCH;
+}
+
+int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct dn_fib_table *t;
+ struct rtattr **rta = arg;
+ struct rtmsg *r = NLMSG_DATA(nlh);
+ struct dn_fib_action *fa;
+ int err;
+
+ if (dn_fib_check_attr(r, rta))
+ return -EINVAL;
+
+ if ((fa = dn_fib_new_action()) == NULL)
+ return -ENOBUFS;
+
+ t = dn_fib_get_tree(r->rtm_table, 1);
+ if (t) {
+ if ((err = dn_fib_convert_rtm(fa, r, rta, nlh, &NETLINK_CB(skb))) < 0) {
+ dn_fib_del_action(fa);
+ return err;
+ }
+ return t->insert(t, fa);
+ }
+ return -ENOBUFS;
+}
+
+int dn_fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
+ int table, struct dn_fib_action *fa)
+{
+ struct rtmsg *rtm;
+ struct nlmsghdr *nlh;
+ unsigned char *b = skb->tail;
+
+ nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*rtm));
+ rtm = NLMSG_DATA(nlh);
+ rtm->rtm_family = AF_DECnet;
+ rtm->rtm_dst_len = 16;
+ rtm->rtm_src_len = 16;
+ rtm->rtm_tos = 0;
+ rtm->rtm_table = table;
+ rtm->rtm_type = fa->fa_type;
+ rtm->rtm_flags = 0;
+ rtm->rtm_protocol = fa->fa_proto;
+ RTA_PUT(skb, RTA_DST, 2, &fa->fa_key);
+ if (fa->fa_ifindex)
+ RTA_PUT(skb, RTA_OIF, sizeof(int), &fa->fa_ifindex);
+
+ nlh->nlmsg_len = skb->tail - b;
+ return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+static void dn_rtmsg_fib(int event, int table, struct dn_fib_action *fa,
+ struct nlmsghdr *nlh, struct netlink_skb_parms *req)
+{
+ struct sk_buff *skb;
+ u32 pid = req ? req->pid : 0;
+ int size = NLMSG_SPACE(sizeof(struct rtmsg) + 256);
+
+ skb = alloc_skb(size, GFP_KERNEL);
+ if (!skb)
+ return;
+
+ if (dn_fib_dump_info(skb, pid, nlh->nlmsg_seq, event, table, fa) < 0) {
+ kfree_skb(skb);
+ return;
+ }
+ NETLINK_CB(skb).dst_groups = RTMGRP_DECnet_ROUTE;
+ if (nlh->nlmsg_flags & NLM_F_ECHO)
+ atomic_inc(&skb->users);
+ netlink_broadcast(rtnl, skb, pid, RTMGRP_DECnet_ROUTE, GFP_KERNEL);
+ if (nlh->nlmsg_flags & NLM_F_ECHO)
+ netlink_unicast(rtnl, skb, pid, MSG_DONTWAIT);
+}
+
+static int dn_fib_table_dump(struct dn_fib_table *t, struct sk_buff *skb, struct netlink_callback *cb)
+{
+
+ return skb->len;
+}
+
+int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int s_t;
+ struct dn_fib_table *t;
+
+ for(s_t = cb->args[0]; s_t < DN_NUM_TABLES; s_t++) {
+ if (s_t > cb->args[0])
+ memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(int));
+ t = dn_fib_get_tree(s_t, 0);
+ if (t == NULL)
+ continue;
+ if (t->dump(t, skb, cb) < 0)
+ break;
+ }
+
+ cb->args[0] = s_t;
+
+ return skb->len;
+}
+#endif /* CONFIG_RTNETLINK */
+
+int dn_fib_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ switch(cmd) {
+ case SIOCADDRT:
+ case SIOCDELRT:
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+struct dn_fib_procfs {
+ int len;
+ off_t pos;
+ off_t begin;
+ off_t offset;
+ int length;
+ char *buffer;
+};
+
+static int dn_proc_action_list(struct dn_fib_walker_t *fwt, struct dn_fib_node *fn)
+{
+ struct dn_fib_procfs *pinfo = (struct dn_fib_procfs *)fwt->arg;
+ struct dn_fib_action *fa;
+ char ab[DN_ASCBUF_LEN];
+
+ if (pinfo->pos > pinfo->offset + pinfo->length)
+ return 0;
+
+ for(fa = fn->fn_action; fa; fa = fa->fa_next) {
+
+ pinfo->len += sprintf(pinfo->buffer + pinfo->len,
+ "%s/%04hx %02x %02x\n",
+ dn_addr2asc(fa->fa_key, ab),
+ fa->fa_mask,
+ fa->fa_type,
+ fa->fa_proto);
+
+ pinfo->pos = pinfo->begin + pinfo->len;
+ if (pinfo->pos < pinfo->offset) {
+ pinfo->len = 0;
+ pinfo->begin = pinfo->pos;
+ }
+ if (pinfo->pos > pinfo->offset + pinfo->length)
+ break;
+ }
+
+ return 0;
+}
+
+static int decnet_rt_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ struct dn_fib_procfs pinfo;
+ int i;
+ struct dn_fib_table *t;
+ struct dn_fib_walker_t fwt;
+
+ pinfo.pos = 0;
+ pinfo.len = 0;
+ pinfo.begin = 0;
+ pinfo.offset = offset;
+ pinfo.length = length;
+ pinfo.buffer = buffer;
+
+ fwt.arg = &pinfo;
+ fwt.fxn = dn_proc_action_list;
+
+ start_bh_atomic();
+ for(i = 0; i < DN_NUM_TABLES; i++) {
+ if ((t = dn_fib_get_tree(i, 0)) == NULL)
+ continue;
+
+ fwt.table = t;
+ t->walk(&fwt);
+
+ if (pinfo.pos > pinfo.offset + pinfo.length)
+ break;
+ }
+ end_bh_atomic();
+
+ *start = pinfo.buffer + (pinfo.offset - pinfo.begin);
+ pinfo.len -= (pinfo.offset - pinfo.begin);
+
+ if (pinfo.len > pinfo.length)
+ pinfo.len = pinfo.length;
+
+ return pinfo.len;
+}
+
+static struct proc_dir_entry proc_net_decnet_route = {
+ PROC_NET_DN_ROUTE, 12, "decnet_route",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ decnet_rt_get_info
+};
+
+#ifdef CONFIG_DECNET_MODULE
+void dn_fib_cleanup(void)
+{
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(PROC_NET_DN_ROUTE);
+#endif /* CONFIG_PROC_FS */
+}
+#endif /* CONFIG_DECNET_MODULE */
+
+
+void __init dn_fib_init(void)
+{
+ memset(dn_fib_tables, 0, DN_NUM_TABLES * sizeof(struct dn_fib_table *));
+
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_net_decnet_route);
+#endif
+
+}
+
+
diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c
new file mode 100644
index 000000000..3ca6535be
--- /dev/null
+++ b/net/decnet/dn_neigh.c
@@ -0,0 +1,633 @@
+/*
+ * DECnet An implementation of the DECnet protocol suite for the LINUX
+ * operating system. DECnet is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * DECnet Neighbour Functions (Adjacency Database and
+ * On-Ethernet Cache)
+ *
+ * Author: Steve Whitehouse <SteveW@ACM.org>
+ *
+ *
+ * Changes:
+ * Steve Whitehouse : Fixed router listing routine
+ * Steve Whitehouse : Added error_report functions
+ * Steve Whitehouse : Added default router detection
+ * Steve Whitehouse : Hop counts in outgoing messages
+ * Steve Whitehouse : Fixed src/dst in outgoing messages so
+ * forwarding now stands a good chance of
+ * working.
+ * Steve Whitehouse : Fixed neighbour states (for now anyway).
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <asm/atomic.h>
+#include <asm/spinlock.h>
+#include <net/neighbour.h>
+#include <net/dst.h>
+#include <net/dn.h>
+#include <net/dn_dev.h>
+#include <net/dn_neigh.h>
+#include <net/dn_route.h>
+
+static int dn_neigh_construct(struct neighbour *);
+static void dn_long_error_report(struct neighbour *, struct sk_buff *);
+static void dn_short_error_report(struct neighbour *, struct sk_buff *);
+static int dn_long_output(struct sk_buff *);
+static int dn_short_output(struct sk_buff *);
+static int dn_phase3_output(struct sk_buff *);
+
+
+/*
+ * For talking to broadcast devices: Ethernet & PPP
+ */
+static struct neigh_ops dn_long_ops = {
+ AF_DECnet,
+ NULL,
+ NULL,
+ dn_long_error_report,
+ dn_long_output,
+ dn_long_output,
+ dev_queue_xmit,
+ dev_queue_xmit
+};
+
+/*
+ * For talking to pointopoint and multidrop devices: DDCMP and X.25
+ */
+static struct neigh_ops dn_short_ops = {
+ AF_DECnet,
+ NULL,
+ NULL,
+ dn_short_error_report,
+ dn_short_output,
+ dn_short_output,
+ dev_queue_xmit,
+ dev_queue_xmit
+};
+
+/*
+ * For talking to DECnet phase III nodes
+ */
+static struct neigh_ops dn_phase3_ops = {
+ AF_DECnet,
+ NULL,
+ NULL,
+ dn_short_error_report, /* Can use short version here */
+ dn_phase3_output,
+ dn_phase3_output,
+ dev_queue_xmit,
+ dev_queue_xmit
+};
+
+struct neigh_table dn_neigh_table = {
+ NULL,
+ PF_DECnet,
+ sizeof(struct dn_neigh),
+ ETH_ALEN,
+ dn_neigh_construct,
+ NULL, /* pconstructor */
+ NULL, /* pdestructor */
+ NULL, /* proxyredo */
+ {
+ NULL,
+ NULL,
+ &dn_neigh_table,
+ 0,
+ NULL,
+ NULL,
+ 30 * HZ, /* base_reachable_time */
+ 1 * HZ, /* retrans_time */
+ 60 * HZ, /* gc_staletime */
+ 30 * HZ, /* reachable_time */
+ 5 * HZ, /* delay_probe_time */
+ 3, /* queue_len */
+ 0, /* ucast_probes */
+ 0, /* app_probes */
+ 0, /* mcast_probes */
+ 0, /* anycast_delay */
+ 0, /* proxy_delay */
+ 0, /* proxy_qlen */
+ 1 * HZ, /* locktime */
+ },
+ 30 * HZ, /* gc_interval */
+ 128, /* gc_thresh1 */
+ 512, /* gc_thresh2 */
+ 1024, /* gc_thresh3 */
+
+};
+
+static int dn_neigh_construct(struct neighbour *neigh)
+{
+ struct device *dev = neigh->dev;
+ struct dn_neigh *dn = (struct dn_neigh *)neigh;
+ struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
+
+ if (dn_db == NULL)
+ return -EINVAL;
+
+ if (dn_db->neigh_parms)
+ neigh->parms = dn_db->neigh_parms;
+
+ if (dn_db->use_long)
+ neigh->ops = &dn_long_ops;
+ else
+ neigh->ops = &dn_short_ops;
+
+ if (dn->flags & DN_NDFLAG_P3)
+ neigh->ops = &dn_phase3_ops;
+
+ neigh->nud_state = NUD_NOARP;
+ neigh->output = neigh->ops->connected_output;
+
+ dn->blksize = 230;
+
+ return 0;
+}
+
+static void dn_long_error_report(struct neighbour *neigh, struct sk_buff *skb)
+{
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ unsigned char *ptr;
+
+ printk(KERN_DEBUG "dn_long_error_report: called\n");
+
+ if (!(cb->rt_flags & DN_RT_F_RQR)) {
+ kfree_skb(skb);
+ return;
+ }
+
+ skb_push(skb, skb->data - skb->nh.raw);
+ ptr = skb->data;
+
+ *(unsigned short *)ptr = dn_htons(skb->len - 2);
+ ptr += 2;
+
+ if (*ptr & DN_RT_F_PF) {
+ char padlen = (*ptr & ~DN_RT_F_PF);
+ ptr += padlen;
+ }
+
+ *ptr++ |= (cb->rt_flags & ~DN_RT_F_RQR) | DN_RT_F_RTS;
+
+ ptr += 2;
+ dn_dn2eth(ptr, dn_ntohs(cb->src));
+ ptr += 8;
+ dn_dn2eth(ptr, dn_ntohs(cb->dst));
+ ptr += 6;
+ *ptr = 0;
+
+ skb->dst->neighbour->ops->queue_xmit(skb);
+}
+
+
+static void dn_short_error_report(struct neighbour *neigh, struct sk_buff *skb)
+{
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ unsigned char *ptr;
+
+ printk(KERN_DEBUG "dn_short_error_report: called\n");
+
+ if (!(cb->rt_flags & DN_RT_F_RQR)) {
+ kfree_skb(skb);
+ return;
+ }
+
+ skb_push(skb, skb->data - skb->nh.raw);
+ ptr = skb->data;
+
+ *(unsigned short *)ptr = dn_htons(skb->len - 2);
+ ptr += 2;
+ *ptr++ = (cb->rt_flags & ~DN_RT_F_RQR) | DN_RT_F_RTS;
+
+ *(dn_address *)ptr = cb->src;
+ ptr += 2;
+ *(dn_address *)ptr = cb->dst;
+ ptr += 2;
+ *ptr = 0;
+
+ skb->dst->neighbour->ops->queue_xmit(skb);
+}
+
+
+static int dn_long_output(struct sk_buff *skb)
+{
+ struct dst_entry *dst = skb->dst;
+ struct neighbour *neigh = dst->neighbour;
+ struct device *dev = neigh->dev;
+ struct dn_dev *dn_db = dev->dn_ptr;
+ int headroom = dev->hard_header_len + sizeof(struct dn_long_packet) + 3;
+ unsigned char *data;
+ struct dn_long_packet *lp;
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+
+
+ if (skb_headroom(skb) < headroom) {
+ struct sk_buff *skb2 = skb_realloc_headroom(skb, headroom);
+ if (skb2 == NULL) {
+ if (net_ratelimit())
+ printk(KERN_CRIT "dn_long_output: no memory\n");
+ kfree_skb(skb);
+ return -ENOBUFS;
+ }
+ kfree_skb(skb);
+ skb = skb2;
+ if (net_ratelimit())
+ printk(KERN_INFO "dn_long_output: Increasing headroom\n");
+ }
+
+ data = skb_push(skb, sizeof(struct dn_long_packet) + 3);
+ lp = (struct dn_long_packet *)(data+3);
+
+ *((unsigned short *)data) = dn_htons(skb->len - 2);
+ *(data + 2) = 1 | DN_RT_F_PF; /* Padding */
+
+ lp->msgflg = DN_RT_PKT_LONG|(cb->rt_flags&(DN_RT_F_IE|DN_RT_F_RQR|DN_RT_F_RTS));
+ lp->d_area = lp->d_subarea = 0;
+ dn_dn2eth(lp->d_id, cb->dst);
+ lp->s_area = lp->s_subarea = 0;
+ dn_dn2eth(lp->s_id, cb->src);
+ lp->nl2 = 0;
+ lp->visit_ct = cb->hops & 0x3f;
+ lp->s_class = 0;
+ lp->pt = 0;
+
+ skb->nh.raw = skb->data;
+
+ if (dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha,
+ dn_db->addr, skb->len) >= 0)
+ return neigh->ops->queue_xmit(skb);
+
+ if (net_ratelimit())
+ printk(KERN_DEBUG "dn_long_output: oops, can't sent packet\n");
+
+ kfree_skb(skb);
+ return -EINVAL;
+}
+
+static int dn_short_output(struct sk_buff *skb)
+{
+ struct dst_entry *dst = skb->dst;
+ struct neighbour *neigh = dst->neighbour;
+ struct device *dev = neigh->dev;
+ int headroom = dev->hard_header_len + sizeof(struct dn_short_packet) + 2;
+ struct dn_short_packet *sp;
+ unsigned char *data;
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+
+
+ if (skb_headroom(skb) < headroom) {
+ struct sk_buff *skb2 = skb_realloc_headroom(skb, headroom);
+ if (skb2 == NULL) {
+ if (net_ratelimit())
+ printk(KERN_CRIT "dn_short_output: no memory\n");
+ kfree_skb(skb);
+ return -ENOBUFS;
+ }
+ kfree_skb(skb);
+ skb = skb2;
+ if (net_ratelimit())
+ printk(KERN_INFO "dn_short_output: Increasing headroom\n");
+ }
+
+ data = skb_push(skb, sizeof(struct dn_short_packet) + 2);
+ *((unsigned short *)data) = dn_htons(skb->len - 2);
+ sp = (struct dn_short_packet *)(data+2);
+
+ sp->msgflg = DN_RT_PKT_SHORT|(cb->rt_flags&(DN_RT_F_RQR|DN_RT_F_RTS));
+ sp->dstnode = cb->dst;
+ sp->srcnode = cb->src;
+ sp->forward = cb->hops & 0x3f;
+
+ skb->nh.raw = skb->data;
+
+ if (dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha,
+ NULL, skb->len) >= 0)
+ return neigh->ops->queue_xmit(skb);
+
+ kfree_skb(skb);
+ return -EINVAL;
+}
+
+/*
+ * Phase 3 output is the same is short output, execpt that
+ * it clears the area bits before transmission.
+ */
+static int dn_phase3_output(struct sk_buff *skb)
+{
+ struct dst_entry *dst = skb->dst;
+ struct neighbour *neigh = dst->neighbour;
+ struct device *dev = neigh->dev;
+ int headroom = dev->hard_header_len + sizeof(struct dn_short_packet) + 2;
+ struct dn_short_packet *sp;
+ unsigned char *data;
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+
+ if (skb_headroom(skb) < headroom) {
+ struct sk_buff *skb2 = skb_realloc_headroom(skb, headroom);
+ if (skb2 == NULL) {
+ if (net_ratelimit())
+ printk(KERN_CRIT "dn_phase3_output: no memory\n");
+ kfree_skb(skb);
+ return -ENOBUFS;
+ }
+ kfree_skb(skb);
+ skb = skb2;
+ if (net_ratelimit())
+ printk(KERN_INFO "dn_phase3_output: Increasing headroom\n");
+ }
+
+ data = skb_push(skb, sizeof(struct dn_short_packet) + 2);
+ ((unsigned short *)data) = dn_htons(skb->len - 2);
+ sp = (struct dn_short_packet *)(data + 2);
+
+ sp->msgflg = DN_RT_PKT_SHORT|(cb->rt_flags&(DN_RT_F_RQR|DN_RT_F_RTS));
+ sp->dstnode = cb->dst & __constant_htons(0x03ff);
+ sp->srcnode = cb->src & __constant_htons(0x03ff);
+ sp->forward = cb->hops & 0x3f;
+
+ skb->nh.raw = skb->data;
+
+ if (dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha,
+ NULL, skb->len) >= 0)
+ return neigh->ops->queue_xmit(skb);
+
+ kfree_skb(skb);
+ return -EINVAL;
+}
+
+/*
+ * Unfortunately, the neighbour code uses the device in its hash
+ * function, so we don't get any advantage from it. This function
+ * basically does a neigh_lookup(), but without comparing the device
+ * field. This is required for the On-Ethernet cache
+ */
+struct neighbour *dn_neigh_lookup(struct neigh_table *tbl, void *ptr)
+{
+ int i;
+ struct neighbour *neigh;
+
+ start_bh_atomic();
+ for(i = 0; i < NEIGH_HASHMASK; i++) {
+ for(neigh = tbl->hash_buckets[i]; neigh != NULL; neigh = neigh->next) {
+ if (memcmp(neigh->primary_key, ptr, ETH_ALEN) == 0) {
+ atomic_inc(&neigh->refcnt);
+ end_bh_atomic();
+ return neigh;
+ }
+ }
+ }
+ end_bh_atomic();
+
+ return NULL;
+}
+
+
+/*
+ * Any traffic on a pointopoint link causes the timer to be reset
+ * for the entry in the neighbour table.
+ */
+void dn_neigh_pointopoint_notify(struct sk_buff *skb)
+{
+ return;
+}
+
+/*
+ * Pointopoint link receives a hello message
+ */
+void dn_neigh_pointopoint_hello(struct sk_buff *skb)
+{
+ kfree_skb(skb);
+}
+
+/*
+ * Ethernet router hello message received
+ */
+void dn_neigh_router_hello(struct sk_buff *skb)
+{
+ struct rtnode_hello_message *msg = (struct rtnode_hello_message *)skb->data;
+
+ struct neighbour *neigh;
+ struct dn_neigh *dn;
+ struct dn_dev *dn_db;
+
+ start_bh_atomic();
+ neigh = __neigh_lookup(&dn_neigh_table, msg->id, skb->dev, 1);
+ end_bh_atomic();
+
+ dn = (struct dn_neigh *)neigh;
+
+ if (neigh) {
+ neigh_update(neigh, msg->id, NUD_NOARP, 1, 0);
+ neigh->used = jiffies;
+
+ dn_db = (struct dn_dev *)neigh->dev->dn_ptr;
+
+ dn->blksize = dn_ntohs(msg->blksize);
+ dn->priority = msg->priority;
+
+ dn->flags &= ~DN_NDFLAG_P3;
+
+ switch(msg->iinfo & DN_RT_INFO_TYPE) {
+ case DN_RT_INFO_L1RT:
+ dn->flags &=~DN_NDFLAG_R2;
+ dn->flags |= DN_NDFLAG_R1;
+ case DN_RT_INFO_L2RT:
+ dn->flags |= DN_NDFLAG_R2;
+ }
+
+ if (!dn_db->router) {
+ dn_db->router = neigh_clone(neigh);
+ } else {
+ if (msg->priority > ((struct dn_neigh *)dn_db->router)->priority)
+ neigh_release(xchg(&dn_db->router, neigh_clone(neigh)));
+ }
+
+ neigh_release(neigh);
+ }
+
+ kfree_skb(skb);
+}
+
+/*
+ * Endnode hello message received
+ */
+void dn_neigh_endnode_hello(struct sk_buff *skb)
+{
+ struct endnode_hello_message *msg = (struct endnode_hello_message *)skb->data;
+ struct neighbour *neigh;
+ struct dn_neigh *dn;
+
+ start_bh_atomic();
+ neigh = __neigh_lookup(&dn_neigh_table, msg->id, skb->dev, 1);
+ end_bh_atomic();
+
+ dn = (struct dn_neigh *)neigh;
+
+ if (neigh) {
+ neigh_update(neigh, msg->id, NUD_NOARP, 1, 0);
+ neigh->used = jiffies;
+
+ dn->flags &= ~(DN_NDFLAG_R1 | DN_NDFLAG_R2);
+ dn->blksize = dn_ntohs(msg->blksize);
+ dn->priority = 0;
+
+ neigh_release(neigh);
+ }
+
+ kfree_skb(skb);
+}
+
+
+#ifdef CONFIG_DECNET_ROUTER
+static char *dn_find_slot(char *base, int max, int priority)
+{
+ int i;
+ unsigned char *min = NULL;
+
+ base += 6; /* skip first id */
+
+ for(i = 0; i < max; i++) {
+ if (!min || (*base < *min))
+ min = base;
+ base += 7; /* find next priority */
+ }
+
+ if (!min)
+ return NULL;
+
+ return (*min < priority) ? (min - 6) : NULL;
+}
+
+int dn_neigh_elist(struct device *dev, unsigned char *ptr, int n)
+{
+ int t = 0;
+ int i;
+ struct neighbour *neigh;
+ struct dn_neigh *dn;
+ struct neigh_table *tbl = &dn_neigh_table;
+ unsigned char *rs = ptr;
+
+ start_bh_atomic();
+
+ for(i = 0; i < NEIGH_HASHMASK; i++) {
+ for(neigh = tbl->hash_buckets[i]; neigh != NULL; neigh = neigh->next) {
+ if (neigh->dev != dev)
+ continue;
+ dn = (struct dn_neigh *)neigh;
+ if (!(dn->flags & (DN_NDFLAG_R1|DN_NDFLAG_R2)))
+ continue;
+ if (decnet_node_type == DN_RT_INFO_L1RT && (dn->flags & DN_NDFLAG_R2))
+ continue;
+ if (t == n)
+ rs = dn_find_slot(ptr, n, dn->priority);
+ else
+ t++;
+ if (rs == NULL)
+ continue;
+ memcpy(rs, dn->addr, ETH_ALEN);
+ rs += 6;
+ *rs = neigh->nud_state & NUD_CONNECTED ? 0x80 : 0x0;
+ *rs |= dn->priority;
+ rs++;
+ }
+ }
+
+ end_bh_atomic();
+
+ return t;
+}
+#endif /* CONFIG_DECNET_ROUTER */
+
+
+
+#ifdef CONFIG_PROC_FS
+int dn_neigh_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int len = 0;
+ off_t pos = 0;
+ off_t begin = 0;
+ struct neighbour *n;
+ int i;
+ char buf[DN_ASCBUF_LEN];
+
+ len += sprintf(buffer + len, "Addr Flags State Use Blksize Dev\n");
+
+ for(i=0;i <= NEIGH_HASHMASK; i++) {
+ read_lock_bh(&dn_neigh_table.lock);
+ n = dn_neigh_table.hash_buckets[i];
+ for(; n != NULL; n = n->next) {
+ struct dn_neigh *dn = (struct dn_neigh *)n;
+
+ read_lock(&n->lock);
+ len += sprintf(buffer+len, "%-7s %s%s%s %02x %02d %07ld %-8s\n",
+ dn_addr2asc(dn_ntohs(dn_eth2dn(dn->addr)), buf),
+ (dn->flags&DN_NDFLAG_R1) ? "1" : "-",
+ (dn->flags&DN_NDFLAG_R2) ? "2" : "-",
+ (dn->flags&DN_NDFLAG_P3) ? "3" : "-",
+ dn->n.nud_state,
+ atomic_read(&dn->n.refcnt),
+ dn->blksize,
+ (dn->n.dev) ? dn->n.dev->name : "?");
+ read_unlock(&n->lock);
+
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ if (pos > offset + length) {
+ read_unlock_bh(&dn_neigh_table.lock);
+ goto done;
+ }
+ }
+ read_unlock_bh(&dn_neigh_table.lock);
+ }
+
+done:
+
+ *start = buffer + (offset - begin);
+ len -= offset - begin;
+
+ if (len > length) len = length;
+
+ return len;
+}
+
+static struct proc_dir_entry proc_net_dn_neigh = {
+ PROC_NET_DN_ADJ, 12, "decnet_neigh",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ dn_neigh_get_info
+};
+
+#endif
+
+void __init dn_neigh_init(void)
+{
+ neigh_table_init(&dn_neigh_table);
+
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_net_dn_neigh);
+#endif /* CONFIG_PROC_FS */
+}
+
+#ifdef CONFIG_DECNET_MODULE
+void dn_neigh_cleanup(void)
+{
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(PROC_NET_DN_ADJ);
+#endif /* CONFIG_PROC_FS */
+ neigh_table_clear(&dn_neigh_table);
+}
+#endif /* CONFIG_DECNET_MODULE */
diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c
new file mode 100644
index 000000000..6d10df045
--- /dev/null
+++ b/net/decnet/dn_nsp_in.c
@@ -0,0 +1,703 @@
+
+/*
+ * DECnet An implementation of the DECnet protocol suite for the LINUX
+ * operating system. DECnet is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * DECnet Network Services Protocol (Input)
+ *
+ * Author: Eduardo Marcelo Serrat <emserrat@geocities.com>
+ *
+ * Changes:
+ *
+ * Steve Whitehouse: Split into dn_nsp_in.c and dn_nsp_out.c from
+ * original dn_nsp.c.
+ * Steve Whitehouse: Updated to work with my new routing architecture.
+ * Steve Whitehouse: Add changes from Eduardo Serrat's patches.
+ * Steve Whitehouse: Put all ack handling code in a common routine.
+ * Steve Whitehouse: Put other common bits into dn_nsp_rx()
+ * Steve Whitehouse: More checks on skb->len to catch bogus packets
+ * Fixed various race conditions and possible nasties.
+ * Steve Whitehouse: Now handles returned conninit frames.
+ */
+
+/******************************************************************************
+ (c) 1995-1998 E.M. Serrat emserrat@geocities.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
+ 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.
+*******************************************************************************/
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/inet.h>
+#include <linux/route.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/termios.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <net/neighbour.h>
+#include <net/dst.h>
+#include <net/dn_nsp.h>
+#include <net/dn_dev.h>
+#include <net/dn_route.h>
+#include <net/dn_raw.h>
+
+
+/*
+ * For this function we've flipped the cross-subchannel bit
+ * if the message is an otherdata or linkservice message. Thus
+ * we can use it to work out what to update.
+ */
+static void dn_ack(struct sock *sk, struct sk_buff *skb, unsigned short ack)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ unsigned short type = ((ack >> 12) & 0x0003);
+ int wakeup = 0;
+
+ /* printk(KERN_DEBUG "dn_ack: %hd 0x%04hx\n", type, ack); */
+
+ switch(type) {
+ case 0: /* ACK - Data */
+ if (after(ack, scp->ackrcv_dat)) {
+ scp->ackrcv_dat = ack & 0x0fff;
+ wakeup |= dn_nsp_check_xmit_queue(sk, skb, &scp->data_xmit_queue, ack);
+ }
+ break;
+ case 1: /* NAK - Data */
+ break;
+ case 2: /* ACK - OtherData */
+ if (after(ack, scp->ackrcv_oth)) {
+ scp->ackrcv_oth = ack & 0x0fff;
+ wakeup |= dn_nsp_check_xmit_queue(sk, skb, &scp->other_xmit_queue, ack);
+ }
+ break;
+ case 3: /* NAK - OtherData */
+ break;
+ }
+
+ if (wakeup && !sk->dead)
+ sk->state_change(sk);
+}
+
+/*
+ * This function is a universal ack processor.
+ */
+static int dn_process_ack(struct sock *sk, struct sk_buff *skb, int oth)
+{
+ unsigned short *ptr = (unsigned short *)skb->data;
+ int len = 0;
+ unsigned short ack;
+
+ if (skb->len < 2)
+ return len;
+
+ if ((ack = dn_ntohs(*ptr)) & 0x8000) {
+ skb_pull(skb, 2);
+ ptr++;
+ len += 2;
+ if ((ack & 0x4000) == 0) {
+ if (oth)
+ ack ^= 0x2000;
+ dn_ack(sk, skb, ack);
+ }
+ }
+
+ if (skb->len < 2)
+ return len;
+
+ if ((ack = dn_ntohs(*ptr)) & 0x8000) {
+ skb_pull(skb, 2);
+ len += 2;
+ if ((ack & 0x4000) == 0) {
+ if (oth)
+ ack ^= 0x2000;
+ dn_ack(sk, skb, ack);
+ }
+ }
+
+ return len;
+}
+
+
+/*
+ * This function uses a slightly different lookup method
+ * to find its sockets, since it searches on object name/number
+ * rather than port numbers
+ */
+static int dn_conninit_rx(struct sk_buff *skb)
+{
+ struct sock *sk;
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ struct nsp_conn_init_msg *msg = (struct nsp_conn_init_msg *)skb->data;
+ struct sockaddr_dn addr;
+ unsigned char type = 0;
+
+ memset(&addr, 0, sizeof(struct sockaddr_dn));
+
+ cb->src_port = msg->srcaddr;
+ cb->dst_port = msg->dstaddr;
+ cb->services = msg->services;
+ cb->info = msg->info;
+ cb->segsize = dn_ntohs(msg->segsize);
+
+ skb_pull(skb, sizeof(*msg));
+
+ /* printk(KERN_DEBUG "username2sockaddr 1\n"); */
+ if (dn_username2sockaddr(skb->data, skb->len, &addr, &type) < 0)
+ goto free_out;
+
+ if (type > 1)
+ goto free_out;
+
+ /* printk(KERN_DEBUG "looking for listener...\n"); */
+ if ((sk = dn_sklist_find_listener(&addr)) == NULL)
+ return 1;
+
+ /* printk(KERN_DEBUG "checking backlog...\n"); */
+ if (sk->ack_backlog >= sk->max_ack_backlog)
+ goto free_out;
+
+ /* printk(KERN_DEBUG "waking up socket...\n"); */
+ sk->ack_backlog++;
+ skb_queue_tail(&sk->receive_queue, skb);
+ sk->state_change(sk);
+
+ return 0;
+
+free_out:
+ kfree_skb(skb);
+ return 0;
+}
+
+static void dn_nsp_conn_conf(struct sock *sk, struct sk_buff *skb)
+{
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ struct dn_scp *scp = &sk->protinfo.dn;
+
+ if (skb->len < 3)
+ goto out;
+
+ cb->services = *skb->data;
+ cb->info = *(skb->data+1);
+ skb_pull(skb, 2);
+ cb->segsize = dn_ntohs(*(__u16 *)skb->data);
+ skb_pull(skb, 2);
+
+ /*
+ * FIXME: Check out services and info fields to check that
+ * we can talk to this kind of node.
+ */
+
+ if ((scp->state == DN_CI) || (scp->state == DN_CD)) {
+ scp->persist = 0;
+ scp->addrrem = cb->src_port;
+ sk->state = TCP_ESTABLISHED;
+ scp->state = DN_RUN;
+
+ if (scp->mss > cb->segsize)
+ scp->mss = cb->segsize;
+ if (scp->mss < 230)
+ scp->mss = 230;
+
+ if (skb->len > 0) {
+ unsigned char dlen = *skb->data;
+ if ((dlen <= 16) && (dlen <= skb->len)) {
+ scp->conndata_in.opt_optl = dlen;
+ memcpy(scp->conndata_in.opt_data, skb->data + 1, dlen);
+ }
+ }
+ dn_nsp_send_lnk(sk, DN_NOCHANGE);
+ if (!sk->dead)
+ sk->state_change(sk);
+ }
+
+out:
+ kfree_skb(skb);
+}
+
+static void dn_nsp_conn_ack(struct sock *sk, struct sk_buff *skb)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+
+ if (scp->state == DN_CI) {
+ scp->state = DN_CD;
+ scp->persist = 0;
+ }
+
+ kfree_skb(skb);
+}
+
+static void dn_nsp_disc_init(struct sock *sk, struct sk_buff *skb)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ unsigned short reason;
+
+ /* printk(KERN_DEBUG "DECnet: discinit %d\n", skb->len); */
+
+ if (skb->len < 2)
+ goto out;
+
+ reason = dn_ntohs(*(__u16 *)skb->data);
+ skb_pull(skb, 2);
+
+ scp->discdata_in.opt_status = reason;
+ scp->discdata_in.opt_optl = 0;
+ memset(scp->discdata_in.opt_data, 0, 16);
+
+ if (skb->len > 0) {
+ unsigned char dlen = *skb->data;
+ if ((dlen <= 16) && (dlen <= skb->len)) {
+ scp->discdata_in.opt_optl = dlen;
+ memcpy(scp->discdata_in.opt_data, skb->data + 1, dlen);
+ }
+ }
+
+ scp->addrrem = cb->src_port;
+ sk->state = TCP_CLOSE;
+
+ /* printk(KERN_DEBUG "DECnet: discinit\n"); */
+
+ switch(scp->state) {
+ case DN_CI:
+ case DN_CD:
+ scp->state = DN_RJ;
+ break;
+ case DN_RUN:
+ sk->shutdown |= SHUTDOWN_MASK;
+ scp->state = DN_DN;
+ break;
+ case DN_DI:
+ scp->state = DN_DIC;
+ break;
+ }
+
+ if (!sk->dead)
+ sk->state_change(sk);
+
+ dn_destroy_sock(sk);
+
+out:
+ kfree_skb(skb);
+}
+
+/*
+ * disc_conf messages are also called no_resources or no_link
+ * messages depending upon the "reason" field.
+ */
+static void dn_nsp_disc_conf(struct sock *sk, struct sk_buff *skb)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ unsigned short reason;
+
+ if (skb->len != 2)
+ goto out;
+
+ reason = dn_ntohs(*(__u16 *)skb->data);
+
+ sk->state = TCP_CLOSE;
+
+ switch(scp->state) {
+ case DN_CI:
+ scp->state = DN_NR;
+ break;
+ case DN_DR:
+ if (reason == NSP_REASON_DC)
+ scp->state = DN_DRC;
+ if (reason == NSP_REASON_NL)
+ scp->state = DN_CN;
+ break;
+ case DN_DI:
+ scp->state = DN_DIC;
+ break;
+ case DN_RUN:
+ sk->shutdown |= SHUTDOWN_MASK;
+ case DN_CC:
+ scp->state = DN_CN;
+ }
+
+ if (!sk->dead)
+ sk->state_change(sk);
+
+ dn_destroy_sock(sk);
+
+out:
+ kfree_skb(skb);
+}
+
+static void dn_nsp_linkservice(struct sock *sk, struct sk_buff *skb)
+{
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ unsigned short segnum;
+ unsigned char lsflags;
+ char fcval;
+
+ if (skb->len != 4)
+ goto out;
+
+ cb->segnum = segnum = dn_ntohs(*(__u16 *)skb->data);
+ skb_pull(skb, 2);
+ lsflags = *(unsigned char *)skb->data;
+ skb_pull(skb, 1);
+ fcval = *(char *)skb->data;
+
+ if (lsflags & 0xf0)
+ goto out;
+
+ if (((sk->protinfo.dn.numoth_rcv + 1) & 0x0FFF) == (segnum & 0x0FFF)) {
+ sk->protinfo.dn.numoth_rcv += 1;
+ switch(lsflags & 0x03) {
+ case 0x00:
+ break;
+ case 0x01:
+ sk->protinfo.dn.flowrem_sw = DN_DONTSEND;
+ break;
+ case 0x02:
+ sk->protinfo.dn.flowrem_sw = DN_SEND;
+ dn_nsp_output(sk);
+ if (!sk->dead)
+ sk->state_change(sk);
+ }
+
+ }
+
+ dn_nsp_send_oth_ack(sk);
+
+out:
+ kfree_skb(skb);
+}
+
+/*
+ * Copy of sock_queue_rcv_skb (from net/core/datagram.c) to
+ * queue other data segments. Also we send SIGURG here instead
+ * of the normal SIGIO, 'cos its out of band data.
+ */
+static __inline__ int dn_queue_other_skb(struct sock *sk, struct sk_buff *skb)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+
+ /* Cast skb->rcvbuf to unsigned... It's pointless, but reduces
+ number of warnings when compiling with -W --ANK
+ */
+ if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf
+)
+ return -ENOMEM;
+
+#ifdef CONFIG_FILTER
+ if (sk->filter)
+ {
+ if (sk_filter(skb, sk->filter))
+ return -EPERM; /* Toss packet */
+ }
+#endif /* CONFIG_FILTER */
+
+ skb_set_owner_r(skb, sk);
+ skb_queue_tail(&scp->other_receive_queue, skb);
+
+ if (!sk->dead) {
+ struct socket *sock = sk->socket;
+ wake_up_interruptible(sk->sleep);
+ if (!(sock->flags & SO_WAITDATA) && sock->fasync_list)
+ kill_fasync(sock->fasync_list, SIGURG);
+ }
+
+ return 0;
+}
+
+static void dn_nsp_otherdata(struct sock *sk, struct sk_buff *skb)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ unsigned short segnum;
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ int queued = 0;
+
+ if (skb->len < 2)
+ goto out;
+
+ cb->segnum = segnum = dn_ntohs(*(__u16 *)skb->data);
+ skb_pull(skb, 2);
+
+ if (((sk->protinfo.dn.numoth_rcv + 1) & 0x0fff) == (segnum & 0x0fff)) {
+
+ if (dn_queue_other_skb(sk, skb) == 0) {
+ sk->protinfo.dn.numoth_rcv++;
+ scp->other_report = 0;
+ queued = 1;
+ }
+ }
+
+ dn_nsp_send_oth_ack(sk);
+out:
+ if (!queued)
+ kfree_skb(skb);
+}
+
+static void dn_nsp_data(struct sock *sk, struct sk_buff *skb)
+{
+ int queued = 0;
+ unsigned short segnum;
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ struct dn_scp *scp = &sk->protinfo.dn;
+
+ if (skb->len < 2)
+ goto out;
+
+ cb->segnum = segnum = dn_ntohs(*(__u16 *)skb->data);
+ skb_pull(skb, 2);
+
+ if (((sk->protinfo.dn.numdat_rcv + 1) & 0x0FFF) ==
+ (segnum & 0x0FFF)) {
+
+ if (sock_queue_rcv_skb(sk, skb) == 0) {
+ sk->protinfo.dn.numdat_rcv++;
+ queued = 1;
+ }
+
+ if ((scp->flowloc_sw == DN_SEND) && dn_congested(sk)) {
+ scp->flowloc_sw = DN_DONTSEND;
+ dn_nsp_send_lnk(sk, DN_DONTSEND);
+ }
+ }
+
+ dn_nsp_send_data_ack(sk);
+out:
+ if (!queued)
+ kfree_skb(skb);
+}
+
+/*
+ * If one of our conninit messages is returned, this function
+ * deals with it. It puts the socket into the NO_COMMUNICATION
+ * state.
+ */
+static void dn_returned_conninit(struct sk_buff *skb)
+{
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ struct sock *sk;
+
+ cb->dst_port = cb->src_port;
+ cb->src_port = 0;
+
+ if ((sk = dn_find_by_skb(skb)) != NULL) {
+ struct dn_scp *scp = &sk->protinfo.dn;
+ if (scp->state == DN_CI) {
+ scp->state = DN_NC;
+ sk->state = TCP_CLOSE;
+ if (!sk->dead)
+ sk->state_change(sk);
+ }
+ }
+
+ kfree_skb(skb);
+}
+
+int dn_nsp_rx(struct sk_buff *skb)
+{
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ struct sock *sk = NULL;
+ unsigned char *ptr = (unsigned char *)skb->data;
+
+ skb->h.raw = skb->data;
+ cb->nsp_flags = *ptr++;
+
+ if (decnet_debug_level & 1)
+ printk(KERN_DEBUG "dn_nsp_rx: Message type 0x%02x\n", (int)cb->nsp_flags);
+
+#ifdef CONFIG_DECNET_RAW
+ dn_raw_rx_nsp(skb);
+#endif /* CONFIG_DECNET_RAW */
+
+ if (skb->len < 2)
+ goto free_out;
+
+ if (cb->nsp_flags & 0x83)
+ goto free_out;
+
+ /*
+ * Returned packets...
+ */
+ if (cb->rt_flags & DN_RT_F_RTS) {
+ if ((cb->nsp_flags & 0x0c) == 0x08) {
+ switch(cb->nsp_flags & 0x70) {
+ case 0x10:
+ case 0x60:
+ dn_returned_conninit(skb);
+ goto out;
+ }
+ }
+ goto free_out;
+ }
+
+ /*
+ * Filter out conninits and useless packet types
+ */
+ if ((cb->nsp_flags & 0x0c) == 0x08) {
+ switch(cb->nsp_flags & 0x70) {
+ case 0x00: /* NOP */
+ case 0x70: /* Reserved */
+ case 0x50: /* Reserved, Phase II node init */
+ goto free_out;
+ case 0x10:
+ case 0x60:
+ return dn_conninit_rx(skb);
+ }
+ }
+
+ if (skb->len < 3)
+ goto free_out;
+
+ /*
+ * Grab the destination address.
+ */
+ cb->dst_port = *(unsigned short *)ptr;
+ cb->src_port = 0;
+ ptr += 2;
+
+ /*
+ * If not a connack, grab the source address too.
+ */
+ if (skb->len >= 5) {
+ cb->src_port = *(unsigned short *)ptr;
+ ptr += 2;
+ skb_pull(skb, 5);
+ }
+
+ /*
+ * Find the socket to which this skb is destined.
+ */
+ if ((sk = dn_find_by_skb(skb)) != NULL) {
+ struct dn_scp *scp = &sk->protinfo.dn;
+ int ret;
+ /* printk(KERN_DEBUG "dn_nsp_rx: Found a socket\n"); */
+
+ /* Reset backoff */
+ scp->nsp_rxtshift = 0;
+
+ bh_lock_sock(sk);
+ ret = 0;
+ if (sk->lock.users == 0)
+ ret = dn_nsp_backlog_rcv(sk, skb);
+ else
+ sk_add_backlog(sk, skb);
+ bh_unlock_sock(sk);
+
+ return ret;
+ }
+ return 1;
+
+free_out:
+ kfree_skb(skb);
+out:
+ return 0;
+}
+
+/*
+ * This is the main receive routine for sockets. It is called
+ * from the above when the socket is not busy, and also from
+ * sock_release() when there is a backlog queued up.
+ */
+int dn_nsp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+
+ /*
+ * Control packet.
+ */
+ if ((cb->nsp_flags & 0x0c) == 0x08) {
+ /* printk(KERN_DEBUG "control type\n"); */
+ switch(cb->nsp_flags & 0x70) {
+ case 0x20:
+ dn_nsp_conn_conf(sk, skb);
+ break;
+ case 0x30:
+ dn_nsp_disc_init(sk, skb);
+ break;
+ case 0x40:
+ dn_nsp_disc_conf(sk, skb);
+ break;
+ }
+
+ } else if (cb->nsp_flags == 0x24) {
+ /*
+ * Special for connacks, 'cos they don't have
+ * ack data or ack otherdata info.
+ */
+ dn_nsp_conn_ack(sk, skb);
+ } else {
+ int other = 1;
+
+ if ((cb->nsp_flags & 0x1c) == 0)
+ other = 0;
+ if (cb->nsp_flags == 0x04)
+ other = 0;
+
+ /*
+ * Read out ack data here, this applies equally
+ * to data, other data, link serivce and both
+ * ack data and ack otherdata.
+ */
+ dn_process_ack(sk, skb, other);
+
+ /*
+ * If we've some sort of data here then call a
+ * suitable routine for dealing with it, otherwise
+ * the packet is an ack and can be discarded. All
+ * data frames can also kick a CC socket into RUN.
+ */
+ if ((cb->nsp_flags & 0x0c) == 0) {
+
+ if ((scp->state == DN_CC) && !sk->dead) {
+ scp->state = DN_RUN;
+ sk->state = TCP_ESTABLISHED;
+ sk->state_change(sk);
+ }
+
+ if (scp->state != DN_RUN)
+ goto free_out;
+
+ switch(cb->nsp_flags) {
+ case 0x10: /* LS */
+ dn_nsp_linkservice(sk, skb);
+ break;
+ case 0x30: /* OD */
+ dn_nsp_otherdata(sk, skb);
+ break;
+ default:
+ dn_nsp_data(sk, skb);
+ }
+
+ } else { /* Ack, chuck it out here */
+free_out:
+ kfree_skb(skb);
+ }
+ }
+
+ return 0;
+}
+
diff --git a/net/decnet/dn_nsp_out.c b/net/decnet/dn_nsp_out.c
new file mode 100644
index 000000000..b4211e91b
--- /dev/null
+++ b/net/decnet/dn_nsp_out.c
@@ -0,0 +1,640 @@
+
+/*
+ * DECnet An implementation of the DECnet protocol suite for the LINUX
+ * operating system. DECnet is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * DECnet Network Services Protocol (Output)
+ *
+ * Author: Eduardo Marcelo Serrat <emserrat@geocities.com>
+ *
+ * Changes:
+ *
+ * Steve Whitehouse: Split into dn_nsp_in.c and dn_nsp_out.c from
+ * original dn_nsp.c.
+ * Steve Whitehouse: Updated to work with my new routing architecture.
+ * Steve Whitehouse: Added changes from Eduardo Serrat's patches.
+ * Steve Whitehouse: Now conninits have the "return" bit set.
+ * Steve Whitehouse: Fixes to check alloc'd skbs are non NULL!
+ * Moved output state machine into one function
+ * Steve Whitehouse: New output state machine
+ */
+
+/******************************************************************************
+ (c) 1995-1998 E.M. Serrat emserrat@geocities.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
+ 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.
+*******************************************************************************/
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/inet.h>
+#include <linux/route.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/termios.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/if_packet.h>
+#include <net/neighbour.h>
+#include <net/dst.h>
+#include <net/dn_nsp.h>
+#include <net/dn_dev.h>
+#include <net/dn_route.h>
+
+
+static int nsp_backoff[NSP_MAXRXTSHIFT + 1] = { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
+
+/*
+ * If sk == NULL, then we assume that we are supposed to be making
+ * a routing layer skb. If sk != NULL, then we are supposed to be
+ * creating an skb for the NSP layer. The dn_send_skb() function will
+ * recognise skbs on the same basis.
+ *
+ * The eventual aim is for each socket to have a cached header size
+ * for its outgoing packets, and to set hdr from this when sk != NULL.
+ */
+struct sk_buff *dn_alloc_skb(struct sock *sk, int size, int pri)
+{
+ struct sk_buff *skb;
+ int hdr = 64;
+
+ if ((skb = alloc_skb(size + hdr, pri)) == NULL)
+ return NULL;
+
+ skb->protocol = __constant_htons(ETH_P_DNA_RT);
+ skb->sk = sk;
+ skb->pkt_type = PACKET_OUTGOING;
+
+ skb_reserve(skb, hdr);
+
+ return skb;
+}
+
+/*
+ * Wrapper for the above, for allocs of data skbs. We try and get the
+ * whole size thats been asked for (plus 11 bytes of header). If this
+ * fails, then we try for any size over 16 bytes for SOCK_STREAMS.
+ */
+struct sk_buff *dn_alloc_send_skb(struct sock *sk, int *size, int noblock, int *err)
+{
+ int space;
+ int len;
+ struct sk_buff *skb = NULL;
+
+ *err = 0;
+
+ while(skb == NULL) {
+ if (signal_pending(current)) {
+ *err = ERESTARTSYS;
+ break;
+ }
+
+ if (sk->shutdown & SEND_SHUTDOWN) {
+ *err = EINVAL;
+ break;
+ }
+
+ if (sk->err)
+ break;
+
+ len = *size + 11;
+ space = sk->sndbuf - atomic_read(&sk->wmem_alloc);
+
+ if (space < len) {
+ if ((sk->socket->type == SOCK_STREAM) && (space >= (16 + 11)))
+ len = space;
+ }
+
+ if (space < len) {
+ sk->socket->flags |= SO_NOSPACE;
+ if (noblock) {
+ *err = EWOULDBLOCK;
+ break;
+ }
+
+ sk->socket->flags &= ~SO_NOSPACE;
+ SOCK_SLEEP_PRE(sk)
+
+ if ((sk->sndbuf - atomic_read(&sk->wmem_alloc)) < len)
+ schedule();
+
+ SOCK_SLEEP_POST(sk)
+ continue;
+ }
+
+ if ((skb = dn_alloc_skb(sk, len, GFP_KERNEL)) == NULL)
+ continue;
+
+ skb->destructor = sock_wfree;
+ atomic_add(skb->truesize, &sk->wmem_alloc);
+
+ *size = len - 11;
+ }
+
+ return skb;
+}
+
+/*
+ * Calculate persist timer based upon the smoothed round
+ * trip time and the variance. Backoff according to the
+ * nsp_backoff[] array.
+ */
+unsigned long dn_nsp_persist(struct sock *sk)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+
+ unsigned long t = ((scp->nsp_srtt >> 2) + scp->nsp_rttvar) >> 1;
+
+ t *= nsp_backoff[scp->nsp_rxtshift];
+
+ if (t < HZ) t = HZ;
+ if (t > (600*HZ)) t = (600*HZ);
+
+ if (scp->nsp_rxtshift < NSP_MAXRXTSHIFT)
+ scp->nsp_rxtshift++;
+
+ /* printk(KERN_DEBUG "rxtshift %lu, t=%lu\n", scp->nsp_rxtshift, t); */
+
+ return t;
+}
+
+/*
+ * This is called each time we get an estimate for the rtt
+ * on the link.
+ */
+static void dn_nsp_rtt(struct sock *sk, long rtt)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ long srtt = (long)scp->nsp_srtt;
+ long rttvar = (long)scp->nsp_rttvar;
+ long delta;
+
+ /*
+ * If the jiffies clock flips over in the middle of timestamp
+ * gathering this value might turn out negative, so we make sure
+ * that is it always positive here.
+ */
+ if (rtt < 0)
+ rtt = -rtt;
+ /*
+ * Add new rtt to smoothed average
+ */
+ delta = ((rtt << 3) - srtt);
+ srtt += (delta >> 3);
+ if (srtt >= 1)
+ scp->nsp_srtt = (unsigned long)srtt;
+ else
+ scp->nsp_srtt = 1;
+
+ /*
+ * Add new rtt varience to smoothed varience
+ */
+ delta >>= 1;
+ rttvar += ((((delta>0)?(delta):(-delta)) - rttvar) >> 2);
+ if (rttvar >= 1)
+ scp->nsp_rttvar = (unsigned long)rttvar;
+ else
+ scp->nsp_rttvar = 1;
+
+ /* printk(KERN_DEBUG "srtt=%lu rttvar=%lu\n", scp->nsp_srtt, scp->nsp_rttvar); */
+}
+
+/*
+ * Walk the queues, otherdata/linkservice first. Send as many
+ * frames as the window allows, increment send counts on all
+ * skbs which are sent. Reduce the window if we are retransmitting
+ * frames.
+ */
+void dn_nsp_output(struct sock *sk)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ unsigned long win = scp->snd_window;
+ struct sk_buff *skb, *skb2, *list;
+ struct dn_skb_cb *cb;
+ int reduce_win = 0;
+
+ /* printk(KERN_DEBUG "dn_nsp_output: ping\n"); */
+
+ /*
+ * First we check for otherdata/linkservice messages
+ */
+ skb = scp->other_xmit_queue.next;
+ list = (struct sk_buff *)&scp->other_xmit_queue;
+ while(win && (skb != list)) {
+ if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+ cb = (struct dn_skb_cb *)skb;
+ if (cb->xmit_count > 0)
+ reduce_win = 1;
+ else
+ cb->stamp = jiffies;
+ cb->xmit_count++;
+ skb2->sk = sk;
+ dn_send_skb(skb2);
+ }
+ skb = skb->next;
+ win--;
+ }
+
+ /*
+ * If we may not send any data, we don't.
+ * Should this apply to otherdata as well ? - SJW
+ */
+ if (scp->flowrem_sw != DN_SEND)
+ goto recalc_window;
+
+ skb = scp->data_xmit_queue.next;
+ list = (struct sk_buff *)&scp->data_xmit_queue;
+ while(win && (skb != list)) {
+ if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+ cb = (struct dn_skb_cb *)skb;
+ if (cb->xmit_count > 0)
+ reduce_win = 1;
+ else
+ cb->stamp = jiffies;
+ cb->xmit_count++;
+ skb2->sk = sk;
+ dn_send_skb(skb2);
+ }
+ skb = skb->next;
+ win--;
+ }
+
+ /*
+ * If we've sent any frame more than once, we cut the
+ * send window size in half. There is always a minimum
+ * window size of one available.
+ */
+recalc_window:
+ if (reduce_win) {
+ /* printk(KERN_DEBUG "Window reduction %ld\n", scp->snd_window); */
+ scp->snd_window >>= 1;
+ if (scp->snd_window < NSP_MIN_WINDOW)
+ scp->snd_window = NSP_MIN_WINDOW;
+ }
+}
+
+int dn_nsp_xmit_timeout(struct sock *sk)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+
+ dn_nsp_output(sk);
+
+ if (skb_queue_len(&scp->data_xmit_queue) || skb_queue_len(&scp->other_xmit_queue))
+ scp->persist = dn_nsp_persist(sk);
+
+ return 0;
+}
+
+void dn_nsp_queue_xmit(struct sock *sk, struct sk_buff *skb, int oth)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ unsigned long t = ((scp->nsp_srtt >> 2) + scp->nsp_rttvar) >> 1;
+ struct sk_buff *skb2;
+
+ if (t < HZ) t = HZ;
+ /*
+ * Slow start: If we have been idle for more than
+ * one RTT, then reset window to min size.
+ */
+ if ((jiffies - scp->stamp) > t)
+ scp->snd_window = NSP_MIN_WINDOW;
+
+ /* printk(KERN_DEBUG "Window: %lu\n", scp->snd_window); */
+
+ cb->xmit_count = 0;
+
+ if (oth)
+ skb_queue_tail(&scp->other_xmit_queue, skb);
+ else
+ skb_queue_tail(&scp->data_xmit_queue, skb);
+
+ if (scp->flowrem_sw != DN_SEND)
+ return;
+
+ if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+ cb->stamp = jiffies;
+ cb->xmit_count++;
+ skb2->sk = sk;
+ dn_send_skb(skb2);
+ }
+}
+
+int dn_nsp_check_xmit_queue(struct sock *sk, struct sk_buff *skb, struct sk_buff_head *q, unsigned short acknum)
+{
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ struct dn_scp *scp = &sk->protinfo.dn;
+ struct sk_buff *skb2, *list, *ack = NULL;
+ int wakeup = 0;
+ unsigned long reftime = cb->stamp;
+ unsigned long pkttime;
+ unsigned short xmit_count;
+ unsigned short segnum;
+
+ skb2 = q->next;
+ list = (struct sk_buff *)q;
+ while(list != skb2) {
+ struct dn_skb_cb *cb2 = (struct dn_skb_cb *)skb2->cb;
+
+ if (before_or_equal(cb2->segnum, acknum))
+ ack = skb2;
+
+ /* printk(KERN_DEBUG "ack: %s %04x %04x\n", ack ? "ACK" : "SKIP", (int)cb2->segnum, (int)acknum); */
+
+ skb2 = skb2->next;
+
+ if (ack == NULL)
+ continue;
+
+ /* printk(KERN_DEBUG "check_xmit_queue: %04x, %d\n", acknum, cb2->xmit_count); */
+
+ wakeup = 1;
+ pkttime = cb2->stamp;
+ xmit_count = cb2->xmit_count;
+ segnum = cb2->segnum;
+ skb_unlink(ack);
+ kfree_skb(ack);
+ ack = NULL;
+ if (xmit_count == 1) {
+ if (equal(segnum, acknum))
+ dn_nsp_rtt(sk, (long)(pkttime - reftime));
+
+ if (scp->snd_window < NSP_MAX_WINDOW)
+ scp->snd_window++;
+ }
+ }
+
+#if 0 /* Turned off due to possible interference in socket shutdown */
+ if ((skb_queue_len(&scp->data_xmit_queue) == 0) &&
+ (skb_queue_len(&scp->other_xmit_queue) == 0))
+ scp->persist = 0;
+#endif
+
+ return wakeup;
+}
+
+void dn_nsp_send_data_ack(struct sock *sk)
+{
+ struct sk_buff *skb = NULL;
+ struct nsp_data_ack_msg *msg;
+
+ if ((skb = dn_alloc_skb(sk, 200, GFP_ATOMIC)) == NULL)
+ return;
+
+ msg = (struct nsp_data_ack_msg *)skb_put(skb,sizeof(*msg));
+
+ msg->msgflg = 0x04; /* data ack message */
+ msg->dstaddr = sk->protinfo.dn.addrrem;
+ msg->srcaddr = sk->protinfo.dn.addrloc;
+ msg->acknum = dn_htons((sk->protinfo.dn.numdat_rcv & 0x0FFF) | 0x8000);
+
+ sk->protinfo.dn.ackxmt_dat = sk->protinfo.dn.numdat_rcv;
+
+ dn_send_skb(skb);
+}
+
+void dn_nsp_send_oth_ack(struct sock *sk)
+{
+ struct sk_buff *skb = NULL;
+ struct nsp_data_ack_msg *msg;
+
+ if ((skb = dn_alloc_skb(sk, 200, GFP_ATOMIC)) == NULL)
+ return;
+
+ /* printk(KERN_DEBUG "dn_send_oth_ack\n"); */
+
+ msg = (struct nsp_data_ack_msg *)skb_put(skb,sizeof(*msg));
+
+ msg->msgflg = 0x14; /* oth ack message */
+ msg->dstaddr = sk->protinfo.dn.addrrem;
+ msg->srcaddr = sk->protinfo.dn.addrloc;
+ msg->acknum = dn_htons((sk->protinfo.dn.numoth_rcv & 0x0FFF) | 0x8000);
+
+ sk->protinfo.dn.ackxmt_oth = sk->protinfo.dn.numoth_rcv;
+
+ dn_send_skb(skb);
+}
+
+
+void dn_send_conn_ack (struct sock *sk)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ struct sk_buff *skb = NULL;
+ struct nsp_conn_ack_msg *msg;
+
+ if ((skb = dn_alloc_skb(sk, 3, GFP_KERNEL)) == NULL)
+ return;
+
+ msg = (struct nsp_conn_ack_msg *)skb_put(skb, 3);
+ msg->msgflg = 0x24;
+ msg->dstaddr = scp->addrrem;
+
+ dn_send_skb(skb);
+}
+
+void dn_nsp_delayed_ack(struct sock *sk)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+
+ if (scp->ackxmt_oth != scp->numoth_rcv)
+ dn_nsp_send_oth_ack(sk);
+
+ if (scp->ackxmt_dat != scp->numdat_rcv)
+ dn_nsp_send_data_ack(sk);
+}
+
+void dn_send_conn_conf (struct sock *sk)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ struct sk_buff *skb = NULL;
+ struct nsp_conn_init_msg *msg;
+ unsigned short int aux;
+
+ if ((skb = dn_alloc_skb(sk, 50 + scp->conndata_out.opt_optl, GFP_KERNEL)) == NULL)
+ return;
+
+ msg = (struct nsp_conn_init_msg *)skb_put(skb, sizeof(*msg));
+ msg->msgflg = 0x28;
+ msg->dstaddr = scp->addrrem;
+ msg->srcaddr = scp->addrloc;
+ msg->services = 0x01;
+ msg->info = 0x03;
+ msg->segsize = dn_htons(0x05B3);
+
+ if (scp->conndata_out.opt_optl > 0) {
+ aux = scp->conndata_out.opt_optl;
+ *skb_put(skb,1) = aux;
+ memcpy(skb_put(skb, aux), scp->conndata_out.opt_data, aux);
+ }
+
+
+ dn_send_skb(skb);
+}
+
+void dn_send_disc (struct sock *sk, unsigned char msgflg, unsigned short reason)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ struct sk_buff *skb = NULL;
+ int ddl = (msgflg == NSP_DISCINIT || msgflg == 0x38) ? (1 + scp->discdata_out.opt_optl) : 0;
+ int size = 7 + ddl;
+ unsigned char *msg;
+
+ if ((skb = dn_alloc_skb(sk, size, GFP_ATOMIC)) == NULL)
+ return;
+
+ if (reason == 0) reason = scp->discdata_out.opt_status;
+
+ msg = skb_put(skb, size);
+ *msg++ = msgflg;
+ *(__u16 *)msg = scp->addrrem;
+ msg += 2;
+ *(__u16 *)msg = scp->addrloc;
+ msg += 2;
+ *(__u16 *)msg = dn_htons(reason);
+ msg += 2;
+
+ if (ddl) {
+ *msg++ = scp->discdata_out.opt_optl;
+ memcpy(msg, scp->discdata_out.opt_data, scp->discdata_out.opt_optl);
+ }
+
+ dn_send_skb(skb);
+}
+
+void dn_nsp_send_lnk(struct sock *sk, unsigned short flgs)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ struct sk_buff *skb = NULL;
+ struct nsp_data_seg_msg *msg;
+ struct nsp_data_opt_msg *msg1;
+ struct dn_skb_cb *cb;
+
+ if ((skb = dn_alloc_skb(sk, 80, GFP_ATOMIC)) == NULL)
+ return;
+
+ cb = (struct dn_skb_cb *)skb->cb;
+ msg = (struct nsp_data_seg_msg *)skb_put(skb, sizeof(*msg));
+ msg->msgflg = 0x10; /* Link svc message */
+ msg->dstaddr = scp->addrrem;
+ msg->srcaddr = scp->addrloc;
+
+ msg1 = (struct nsp_data_opt_msg *)skb_put(skb, sizeof(*msg1));
+ msg1->acknum = dn_htons((scp->ackxmt_oth & 0x0FFF) | 0x8000);
+ msg1->segnum = dn_htons(cb->segnum = (scp->numoth++ & 0x0FFF));
+ msg1->lsflgs = flgs;
+
+ /* printk(KERN_DEBUG "dn_nsp_send_lnk: %02x\n", flgs); */
+
+ dn_nsp_queue_xmit(sk, skb, 1);
+
+ scp->persist = dn_nsp_persist(sk);
+ scp->persist_fxn = dn_nsp_xmit_timeout;
+
+}
+
+static int dn_nsp_retrans_conninit(struct sock *sk)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+
+ if (scp->state == DN_CI) {
+ dn_nsp_send_conninit(sk, NSP_RCI);
+ scp->persist = dn_nsp_persist(sk);
+ }
+
+ return 0;
+}
+
+void dn_nsp_send_conninit(struct sock *sk, unsigned char msgflg)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ struct sk_buff *skb = NULL;
+ struct nsp_conn_init_msg *msg;
+ unsigned char aux;
+ unsigned char menuver;
+ struct dn_skb_cb *cb;
+ unsigned char type = 1;
+
+ if ((skb = dn_alloc_skb(sk, 200, (msgflg == NSP_CI) ? GFP_KERNEL : GFP_ATOMIC)) == NULL)
+ return;
+
+ cb = (struct dn_skb_cb *)skb->cb;
+ msg = (struct nsp_conn_init_msg *)skb_put(skb,sizeof(*msg));
+
+ msg->msgflg = msgflg;
+ msg->dstaddr = 0x0000; /* Remote Node will assign it*/
+
+ if (msgflg == NSP_CI)
+ sk->protinfo.dn.addrloc = dn_alloc_port();
+
+ msg->srcaddr = sk->protinfo.dn.addrloc;
+ msg->services = 1 | NSP_FC_NONE; /* Requested flow control */
+ msg->info = 0x03; /* Version Number */
+ msg->segsize = dn_htons(1459); /* Max segment size */
+
+ if (scp->peer.sdn_objnum)
+ type = 0;
+
+ skb_put(skb, dn_sockaddr2username(&scp->peer, skb->tail, type));
+ skb_put(skb, dn_sockaddr2username(&scp->addr, skb->tail, 2));
+
+ menuver = DN_MENUVER_ACC | DN_MENUVER_USR;
+ if (scp->peer.sdn_flags & SDF_PROXY)
+ menuver |= DN_MENUVER_PRX;
+ if (scp->peer.sdn_flags & SDF_UICPROXY)
+ menuver |= DN_MENUVER_UIC;
+
+ *skb_put(skb, 1) = menuver; /* Menu Version */
+
+ aux = scp->accessdata.acc_userl;
+ *skb_put(skb, 1) = aux;
+ if (aux > 0)
+ memcpy(skb_put(skb, aux), scp->accessdata.acc_user, aux);
+
+ aux = scp->accessdata.acc_passl;
+ *skb_put(skb, 1) = aux;
+ if (aux > 0)
+ memcpy(skb_put(skb, aux), scp->accessdata.acc_pass, aux);
+
+ aux = scp->accessdata.acc_accl;
+ *skb_put(skb, 1) = aux;
+ if (aux > 0)
+ memcpy(skb_put(skb, aux), scp->accessdata.acc_acc, aux);
+
+ aux = scp->conndata_out.opt_optl;
+ *skb_put(skb, 1) = aux;
+ if (aux > 0)
+ memcpy(skb_put(skb,aux), scp->conndata_out.opt_data, aux);
+
+ sk->protinfo.dn.persist = dn_nsp_persist(sk);
+ sk->protinfo.dn.persist_fxn = dn_nsp_retrans_conninit;
+
+ cb->rt_flags = DN_RT_F_RQR;
+
+ dn_send_skb(skb);
+}
+
diff --git a/net/decnet/dn_raw.c b/net/decnet/dn_raw.c
new file mode 100644
index 000000000..49cc8c4a9
--- /dev/null
+++ b/net/decnet/dn_raw.c
@@ -0,0 +1,383 @@
+/*
+ * DECnet An implementation of the DECnet protocol suite for the LINUX
+ * operating system. DECnet is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * DECnet Raw Sockets Interface
+ *
+ * Author: Steve Whitehouse <SteveW@ACM.org>
+ *
+ *
+ * Changes:
+ * Steve Whitehouse - connect() function.
+ */
+
+#include <linux/config.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <net/sock.h>
+#include <net/dn.h>
+#include <net/dn_raw.h>
+
+static struct sock *dn_raw_nsp_sklist = NULL;
+static struct sock *dn_raw_routing_sklist = NULL;
+#ifdef CONFIG_DECNET_MOP
+static struct sock *dn_raw_mop_sklist = NULL;
+#endif /* CONFIG_DECNET_MOP */
+
+static void dn_raw_autobind(struct sock *sk)
+{
+
+ switch(sk->protocol) {
+ case DNPROTO_NSP:
+ sklist_insert_socket(&dn_raw_nsp_sklist, sk);
+ break;
+ case DNPROTO_ROU:
+ sklist_insert_socket(&dn_raw_routing_sklist, sk);
+ break;
+#ifdef CONFIG_DECNET_MOP
+ case DNPROTO_MOP:
+ sklist_insert_socket(&dn_raw_mop_sklist, sk);
+#endif /* CONFIG_DECNET_MOP */
+ default:
+ printk(KERN_DEBUG "dn_raw_autobind: Unknown protocol\n");
+ return;
+ }
+
+ sk->zapped = 0;
+}
+
+static int dn_raw_release(struct socket *sock, struct socket *peer)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk == NULL)
+ return 0;
+
+ if (!sk->dead) sk->state_change(sk);
+
+ sk->dead = 1;
+ sk->socket = NULL;
+ sock->sk = NULL;
+
+ switch(sk->protocol) {
+ case DNPROTO_NSP:
+ sklist_destroy_socket(&dn_raw_nsp_sklist, sk);
+ break;
+ case DNPROTO_ROU:
+ sklist_destroy_socket(&dn_raw_routing_sklist, sk);
+ break;
+#ifdef CONFIG_DECNET_MOP
+ case DNPROTO_MOP:
+ sklist_destroy_socket(&dn_raw_mop_sklist, sk);
+ break;
+#endif /* CONFIG_DECNET_MOP */
+ }
+
+ return 0;
+}
+
+/*
+ * Bind does odd things with raw sockets. Its basically used to filter
+ * the incomming packets, but this differs with the different layers
+ * at which you extract packets.
+ *
+ * For Routing layer sockets, the object name is a host ordered unsigned
+ * short which is a mask for the 16 different types of possible routing
+ * packet. I'd like to also select by destination address of the packets
+ * but alas, this is rather too dificult to do at the moment.
+ */
+static int dn_raw_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+ struct sock *sk = sock->sk;
+ struct sockaddr_dn *addr = (struct sockaddr_dn *)uaddr;
+
+ if (addr_len != sizeof(struct sockaddr_dn))
+ return -EINVAL;
+
+ if (sk->zapped == 0)
+ return -EINVAL;
+
+ switch(sk->protocol) {
+ case DNPROTO_ROU:
+ if (addr->sdn_objnamel && (addr->sdn_objnamel != 2))
+ return -EINVAL;
+ /* Fall through here */
+ case DNPROTO_NSP:
+ if (addr->sdn_add.a_len && (addr->sdn_add.a_len != 2))
+ return -EINVAL;
+ break;
+ default:
+ return -EPROTONOSUPPORT;
+ }
+
+ if (addr->sdn_objnamel > (DN_MAXOBJL-1))
+ return -EINVAL;
+
+ if (addr->sdn_add.a_len > DN_MAXADDL)
+ return -EINVAL;
+
+
+ memcpy(&sk->protinfo.dn.addr, addr, sizeof(struct sockaddr_dn));
+
+ dn_raw_autobind(sk);
+
+ return 0;
+}
+
+/*
+ * This is to allow send() and write() to work. You set the destination address
+ * with this function.
+ */
+static int dn_raw_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct dn_scp *scp = &sk->protinfo.dn;
+ struct sockaddr_dn *saddr = (struct sockaddr_dn *)uaddr;
+
+ if (addr_len != sizeof(struct sockaddr_dn))
+ return -EINVAL;
+
+ if (saddr->sdn_family != AF_DECnet)
+ return -EINVAL;
+
+ if (saddr->sdn_objnamel > (DN_MAXOBJL-1))
+ return -EINVAL;
+
+ if (saddr->sdn_add.a_len > DN_MAXADDL)
+ return -EINVAL;
+
+ if (sk->zapped)
+ dn_raw_autobind(sk);
+
+ memcpy(&scp->peer, saddr, sizeof(struct sockaddr_dn));
+
+ return 0;
+}
+
+/*
+ * TBD.
+ */
+static int dn_raw_sendmsg(struct socket *sock, struct msghdr *hdr, int size,
+ struct scm_cookie *scm)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk->zapped)
+ dn_raw_autobind(sk);
+
+ if (sk->protocol != DNPROTO_NSP)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+/*
+ * This works fine, execpt that it doesn't report the originating address
+ * or anything at the moment.
+ */
+static int dn_raw_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+ int flags, struct scm_cookie *scm)
+{
+ struct sock *sk = sock->sk;
+ struct sk_buff *skb;
+ int err = 0;
+ int copied = 0;
+
+ if (sk->zapped)
+ dn_raw_autobind(sk);
+
+ if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &err)) == NULL)
+ goto out;
+
+ copied = skb->len;
+
+ if (copied > size) {
+ copied = size;
+ msg->msg_flags |= MSG_TRUNC;
+ }
+
+ if ((err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied)) != 0) {
+ if (flags & MSG_PEEK)
+ atomic_dec(&skb->users);
+ else
+ skb_queue_head(&sk->receive_queue, skb);
+
+ goto out;
+ }
+
+ skb_free_datagram(sk, skb);
+
+out:
+ return copied ? copied : err;
+}
+
+struct proto_ops dn_raw_proto_ops = {
+ AF_DECnet,
+
+ sock_no_dup,
+ dn_raw_release,
+ dn_raw_bind,
+ dn_raw_connect,
+ sock_no_socketpair,
+ sock_no_accept,
+ sock_no_getname,
+ datagram_poll,
+ sock_no_ioctl,
+ sock_no_listen,
+ sock_no_shutdown,
+ sock_no_setsockopt,
+ sock_no_getsockopt,
+ sock_no_fcntl,
+ dn_raw_sendmsg,
+ dn_raw_recvmsg
+};
+
+#ifdef CONFIG_PROC_FS
+int dn_raw_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int len = 0;
+ off_t pos = 0;
+ off_t begin = 0;
+ struct sock *sk;
+
+ cli();
+ for(sk = dn_raw_nsp_sklist; sk; sk = sk->next) {
+ len += sprintf(buffer+len, "NSP\n");
+
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ if (pos > offset + length)
+ goto all_done;
+
+ }
+
+ for(sk = dn_raw_routing_sklist; sk; sk = sk->next) {
+ len += sprintf(buffer+len, "ROU\n");
+
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ if (pos > offset + length)
+ goto all_done;
+ }
+
+#ifdef CONFIG_DECNET_MOP
+ for(sk = dn_raw_mop_sklist; sk; sk = sk->next) {
+ len += sprintf(buffer+len, "MOP\n");
+
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ if (pos > offset + length)
+ goto all_done;
+ }
+#endif /* CONFIG_DECNET_MOP */
+
+all_done:
+ sti();
+
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
+
+ if (len > length) len = length;
+
+ return(len);
+}
+#endif /* CONFIG_PROC_FS */
+
+void dn_raw_rx_nsp(struct sk_buff *skb)
+{
+ struct sock *sk;
+ struct sk_buff *skb2;
+ unsigned long cpuflags;
+
+ save_flags(cpuflags);
+ cli();
+ for(sk = dn_raw_nsp_sklist; sk != NULL; sk = sk->next) {
+ if (skb->len > sock_rspace(sk))
+ continue;
+ if (sk->dead)
+ continue;
+ if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+ skb_set_owner_r(skb2, sk);
+ __skb_queue_tail(&sk->receive_queue, skb2);
+ sk->data_ready(sk, skb->len);
+ }
+ }
+ restore_flags(cpuflags);
+}
+
+void dn_raw_rx_routing(struct sk_buff *skb)
+{
+ struct sock *sk;
+ struct sk_buff *skb2;
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ unsigned long cpuflags;
+ unsigned short rt_flagmask;
+ unsigned short objnamel;
+ struct dn_scp *scp;
+
+ save_flags(cpuflags);
+ cli();
+ for(sk = dn_raw_routing_sklist; sk != NULL; sk = sk->next) {
+ if (skb->len > sock_rspace(sk))
+ continue;
+ if (sk->dead)
+ continue;
+ scp = &sk->protinfo.dn;
+
+ rt_flagmask = *(unsigned short *)scp->addr.sdn_objname;
+ objnamel = dn_ntohs(scp->addr.sdn_objnamel);
+
+ if ((objnamel == 2) && (!((1 << (cb->rt_flags & 0x0f)) & rt_flagmask)))
+ continue;
+
+ if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+ skb_set_owner_r(skb2, sk);
+ __skb_queue_tail(&sk->receive_queue, skb2);
+ sk->data_ready(sk, skb->len);
+ }
+ }
+ restore_flags(cpuflags);
+}
+
+#ifdef CONFIG_DECNET_MOP
+void dn_raw_rx_mop(struct sk_buff *skb)
+{
+ struct sock *sk;
+ struct sk_buff *skb2;
+ unsigned long cpuflags;
+
+ save_flags(cpuflags);
+ cli();
+ for(sk = dn_raw_mop_sklist; sk != NULL; sk = sk->next) {
+ if (skb->len > sock_rspace(sk))
+ continue;
+ if (sk->dead)
+ continue;
+ if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+ skb_set_owner_r(skb2, sk);
+ __skb_queue_tail(&sk->receive_queue, skb2);
+ sk->data_ready(sk, skb->len);
+ }
+ }
+ restore_flags(cpuflags);
+}
+#endif /* CONFIG_DECNET_MOP */
diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c
new file mode 100644
index 000000000..06001b5f5
--- /dev/null
+++ b/net/decnet/dn_route.c
@@ -0,0 +1,1028 @@
+
+/*
+ * DECnet An implementation of the DECnet protocol suite for the LINUX
+ * operating system. DECnet is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * DECnet Routing Functions (Endnode and Router)
+ *
+ * Authors: Steve Whitehouse <SteveW@ACM.org>
+ * Eduardo Marcelo Serrat <emserrat@geocities.com>
+ *
+ * Changes:
+ * Steve Whitehouse : Fixes to allow "intra-ethernet" and
+ * "return-to-sender" bits on outgoing
+ * packets.
+ * Steve Whitehouse : Timeouts for cached routes.
+ * Steve Whitehouse : Use dst cache for input routes too.
+ * Steve Whitehouse : Fixed error values in dn_send_skb.
+ */
+
+/******************************************************************************
+ (c) 1995-1998 E.M. Serrat emserrat@geocities.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
+ 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.
+*******************************************************************************/
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/inet.h>
+#include <linux/route.h>
+#include <net/sock.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/firewall.h>
+#include <linux/rtnetlink.h>
+#include <linux/string.h>
+#include <asm/errno.h>
+#include <net/neighbour.h>
+#include <net/dst.h>
+#include <net/dn.h>
+#include <net/dn_dev.h>
+#include <net/dn_nsp.h>
+#include <net/dn_route.h>
+#include <net/dn_neigh.h>
+#include <net/dn_fib.h>
+#include <net/dn_raw.h>
+
+extern struct neigh_table dn_neigh_table;
+
+#define DN_HASHBUCKETS 16
+
+static unsigned char dn_hiord_addr[6] = {0xAA,0x00,0x04,0x00,0x00,0x00};
+
+static int dn_dst_gc(void);
+static struct dst_entry *dn_dst_check(struct dst_entry *, __u32);
+static struct dst_entry *dn_dst_reroute(struct dst_entry *, struct sk_buff *skb);
+static struct dst_entry *dn_dst_negative_advice(struct dst_entry *);
+static void dn_dst_link_failure(struct sk_buff *);
+static int dn_route_input(struct sk_buff *);
+
+static struct dn_route *dn_route_cache[DN_HASHBUCKETS];
+static struct timer_list dn_route_timer = { NULL, NULL, 0, 0L, NULL };
+int decnet_dst_gc_interval = 2;
+
+static struct dst_ops dn_dst_ops = {
+ PF_DECnet,
+ __constant_htons(ETH_P_DNA_RT),
+ 128,
+ dn_dst_gc,
+ dn_dst_check,
+ dn_dst_reroute,
+ NULL,
+ dn_dst_negative_advice,
+ dn_dst_link_failure,
+ ATOMIC_INIT(0)
+};
+
+static __inline__ unsigned dn_hash(unsigned short dest)
+{
+ unsigned short tmp = (dest&0xff) ^ (dest>>8);
+ return (tmp&0x0f) ^ (tmp>>4);
+}
+
+static void dn_dst_check_expire(unsigned long dummy)
+{
+ int i;
+ struct dn_route *rt, **rtp;
+ unsigned long now = jiffies;
+ unsigned long expire = 120 * HZ;
+
+ for(i = 0; i < DN_HASHBUCKETS; i++) {
+ rtp = &dn_route_cache[i];
+ for(;(rt=*rtp); rtp = &rt->u.rt_next) {
+ if (atomic_read(&rt->u.dst.use) ||
+ (now - rt->u.dst.lastuse) < expire)
+ continue;
+ *rtp = rt->u.rt_next;
+ rt->u.rt_next = NULL;
+ dst_free(&rt->u.dst);
+ }
+
+ if ((jiffies - now) > 0)
+ break;
+ }
+
+ dn_route_timer.expires = now + decnet_dst_gc_interval * HZ;
+ add_timer(&dn_route_timer);
+}
+
+static int dn_dst_gc(void)
+{
+ struct dn_route *rt, **rtp;
+ int i;
+ unsigned long now = jiffies;
+ unsigned long expire = 10 * HZ;
+
+ start_bh_atomic();
+ for(i = 0; i < DN_HASHBUCKETS; i++) {
+ rtp = &dn_route_cache[i];
+ for(; (rt=*rtp); rtp = &rt->u.rt_next) {
+ if (atomic_read(&rt->u.dst.use) ||
+ (now - rt->u.dst.lastuse) < expire)
+ continue;
+ *rtp = rt->u.rt_next;
+ rt->u.rt_next = NULL;
+ dst_free(&rt->u.dst);
+ break;
+ }
+ }
+ end_bh_atomic();
+
+ return 0;
+}
+
+static struct dst_entry *dn_dst_check(struct dst_entry *dst, __u32 cookie)
+{
+ dst_release(dst);
+ return NULL;
+}
+
+static struct dst_entry *dn_dst_reroute(struct dst_entry *dst,
+ struct sk_buff *skb)
+{
+ return NULL;
+}
+
+/*
+ * This is called through sendmsg() when you specify MSG_TRYHARD
+ * and there is already a route in cache.
+ */
+static struct dst_entry *dn_dst_negative_advice(struct dst_entry *dst)
+{
+ dst_release(dst);
+ return NULL;
+}
+
+static void dn_dst_link_failure(struct sk_buff *skb)
+{
+ return;
+}
+
+static void dn_insert_route(struct dn_route *rt)
+{
+ unsigned hash = dn_hash(rt->rt_daddr);
+ unsigned long now = jiffies;
+
+ start_bh_atomic();
+
+ rt->u.rt_next = dn_route_cache[hash];
+ dn_route_cache[hash] = rt;
+
+ atomic_inc(&rt->u.dst.refcnt);
+ atomic_inc(&rt->u.dst.use);
+ rt->u.dst.lastuse = now;
+
+ end_bh_atomic();
+}
+
+#if defined(CONFIG_DECNET_MODULE)
+static void dn_run_flush(unsigned long dummy)
+{
+ int i;
+ struct dn_route *rt, *next;
+
+ for(i = 0; i < DN_HASHBUCKETS; i++) {
+ if ((rt = xchg(&dn_route_cache[i], NULL)) == NULL)
+ continue;
+
+ for(; rt; rt=next) {
+ next = rt->u.rt_next;
+ rt->u.rt_next = NULL;
+ dst_free((struct dst_entry *)rt);
+ }
+ }
+}
+#endif /* CONFIG_DECNET_MODULE */
+
+static int dn_route_rx_long(struct sk_buff *skb)
+{
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ unsigned char *ptr = skb->data;
+
+ if (skb->len < 21) /* 20 for long header, 1 for shortest nsp */
+ goto drop_it;
+
+ skb_pull(skb, 20);
+ skb->h.raw = skb->data;
+
+ /* Destination info */
+ ptr += 2;
+ cb->dst = dn_htons(dn_eth2dn(ptr));
+ if (memcmp(ptr, dn_hiord_addr, 4) != 0)
+ goto drop_it;
+ ptr += 6;
+
+
+ /* Source info */
+ ptr += 2;
+ cb->src = dn_htons(dn_eth2dn(ptr));
+ if (memcmp(ptr, dn_hiord_addr, 4) != 0)
+ goto drop_it;
+ ptr += 6;
+ /* Other junk */
+ ptr++;
+ cb->hops = *ptr++; /* Visit Count */
+
+ if (dn_route_input(skb) == 0) {
+
+#ifdef CONFIG_DECNET_FW
+ struct neighbour *neigh = skb->dst->neighbour;
+
+ switch(call_in_firewall(PF_DECnet, skb->dev, NULL, NULL, &skb)) {
+ case FW_REJECT:
+ neigh->ops->error_report(neigh, skb);
+ return 0;
+ case FW_BLOCK:
+ default:
+ goto drop_it;
+ case FW_ACCEPT:
+ }
+#endif /* CONFIG_DECNET_FW */
+
+ return skb->dst->input(skb);
+ }
+
+drop_it:
+ kfree_skb(skb);
+ return 0;
+}
+
+
+
+static int dn_route_rx_short(struct sk_buff *skb)
+{
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ unsigned char *ptr = skb->data;
+
+ if (skb->len < 6) /* 5 for short header + 1 for shortest nsp */
+ goto drop_it;
+
+ skb_pull(skb, 5);
+ skb->h.raw = skb->data;
+
+ cb->dst = *(dn_address *)ptr;
+ ptr += 2;
+ cb->src = *(dn_address *)ptr;
+ ptr += 2;
+ cb->hops = *ptr & 0x3f;
+
+ if (dn_route_input(skb) == 0) {
+
+#ifdef CONFIG_DECNET_FW
+ struct neighbour *neigh = skb->dst->neighbour;
+
+ switch(call_in_firewall(PF_DECnet, skb->dev, NULL, NULL, &skb)) {
+ case FW_REJECT:
+ neigh->ops->error_report(neigh, skb);
+ return 0;
+ case FW_BLOCK:
+ default:
+ goto drop_it;
+
+ case FW_ACCEPT:
+ }
+#endif /* CONFIG_DECNET_FW */
+
+ return skb->dst->input(skb);
+ }
+
+drop_it:
+ kfree_skb(skb);
+ return 0;
+}
+
+int dn_route_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ unsigned char flags = 0;
+ int padlen = 0;
+ __u16 len = dn_ntohs(*(__u16 *)skb->data);
+ struct dn_dev *dn = (struct dn_dev *)dev->dn_ptr;
+
+ if (dn == NULL)
+ goto dump_it;
+
+ cb->stamp = jiffies;
+ cb->iif = dev->ifindex;
+
+ skb_pull(skb, 2);
+
+ if (len > skb->len)
+ goto dump_it;
+
+ skb_trim(skb, len);
+
+ flags = *skb->data;
+
+ /*
+ * If we have padding, remove it.
+ */
+ if (flags & DN_RT_F_PF) {
+ padlen = flags & ~DN_RT_F_PF;
+ skb_pull(skb, padlen);
+ flags = *skb->data;
+ }
+
+ skb->nh.raw = skb->data;
+
+ /*
+ * Weed out future version DECnet
+ */
+ if (flags & DN_RT_F_VER)
+ goto dump_it;
+
+ cb->rt_flags = flags;
+
+ if (dn->parms.setsrc)
+ dn->parms.setsrc(skb);
+
+ /* printk(KERN_DEBUG "dn_route_rcv: got 0x%02x from %s [%d %d %d]\n", (int)flags,
+ (dev) ? dev->name : "???", len, skb->len, padlen); */
+
+#ifdef CONFIG_DECNET_RAW
+ dn_raw_rx_routing(skb);
+#endif /* CONFIG_DECNET_RAW */
+
+ if (flags & DN_RT_PKT_CNTL) {
+ switch(flags & DN_RT_CNTL_MSK) {
+ case DN_RT_PKT_INIT:
+ dn_dev_init_pkt(skb);
+ break;
+ case DN_RT_PKT_VERI:
+ dn_dev_veri_pkt(skb);
+ break;
+ }
+
+ if (dn->parms.state != DN_DEV_S_RU)
+ goto dump_it;
+
+ switch(flags & DN_RT_CNTL_MSK) {
+ case DN_RT_PKT_HELO:
+ dn_dev_hello(skb);
+ dn_neigh_pointopoint_hello(skb);
+ return 0;
+
+ case DN_RT_PKT_L1RT:
+ case DN_RT_PKT_L2RT:
+#ifdef CONFIG_DECNET_ROUTER
+ return dn_fib_rt_message(skb);
+#else
+ break;
+#endif /* CONFIG_DECNET_ROUTER */
+ case DN_RT_PKT_ERTH:
+ dn_neigh_router_hello(skb);
+ return 0;
+
+ case DN_RT_PKT_EEDH:
+ dn_neigh_endnode_hello(skb);
+ return 0;
+ }
+ } else {
+ if (dn->parms.state != DN_DEV_S_RU)
+ goto dump_it;
+
+ skb_pull(skb, 1); /* Pull flags */
+
+ switch(flags & DN_RT_PKT_MSK) {
+ case DN_RT_PKT_LONG:
+ return dn_route_rx_long(skb);
+ case DN_RT_PKT_SHORT:
+ return dn_route_rx_short(skb);
+ }
+ }
+
+dump_it:
+ if (net_ratelimit())
+ printk(KERN_DEBUG "dn_route_rcv: Dumping packet\n");
+ kfree_skb(skb);
+ return 0;
+}
+
+
+void dn_send_skb(struct sk_buff *skb)
+{
+ struct sock *sk = skb->sk;
+ struct dn_scp *scp = &sk->protinfo.dn;
+
+ if (sk == NULL) {
+ dev_queue_xmit(skb);
+ return ;
+ }
+
+ skb->h.raw = skb->data;
+
+ scp->stamp = jiffies; /* Record time packet was sent */
+
+ /* printk(KERN_DEBUG "dn_send_skb\n"); */
+
+ if (sk->dst_cache && sk->dst_cache->obsolete) {
+ dst_release(sk->dst_cache);
+ sk->dst_cache = NULL;
+ }
+
+ if (sk->dst_cache == NULL) {
+ if (dn_route_output(sk) != 0) {
+ kfree_skb(skb);
+ sk->err = EHOSTUNREACH;
+ if (!sk->dead)
+ sk->state_change(sk);
+ return;
+ }
+ }
+
+ skb->dst = dst_clone(sk->dst_cache);
+
+ sk->dst_cache->output(skb);
+}
+
+
+static int dn_output(struct sk_buff *skb)
+{
+ struct dst_entry *dst = skb->dst;
+ struct dn_route *rt = (struct dn_route *)dst;
+ struct device *dev = dst->dev;
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ int err = -EINVAL;
+
+ if (!dst->neighbour)
+ goto error;
+
+ skb->dev = dev;
+
+ cb->src = rt->rt_saddr;
+ cb->dst = rt->rt_daddr;
+
+ /*
+ * Always set the Intra-Ethernet bit on all outgoing packets
+ * originated on this node. Only valid flag from upper layers
+ * is return-to-sender-requested. Set hop count to 0 too.
+ */
+ cb->rt_flags &= ~DN_RT_F_RQR;
+ cb->rt_flags |= DN_RT_F_IE;
+ cb->hops = 0;
+
+ /*
+ * Filter through the outgoing firewall
+ */
+#ifdef CONFIG_DECNET_FW
+ switch(call_out_firewall(PF_DECnet, dst->dev, NULL, NULL, &skb)) {
+ case FW_REJECT:
+ err = -EPERM;
+ goto drop;
+ case FW_BLOCK:
+ default:
+ err = 0;
+ goto drop;
+ case FW_ACCEPT:
+ }
+#endif /* CONFIG_DECNET_FW */
+
+ return dst->neighbour->output(skb);
+
+error:
+ if (net_ratelimit())
+ printk(KERN_DEBUG "dn_output: This should not happen\n");
+
+#ifdef CONFIG_DECNET_FW
+drop:
+#endif
+ kfree_skb(skb);
+
+ return err;
+}
+
+#ifdef CONFIG_DECNET_ROUTER
+static int dn_l2_forward(struct sk_buff *skb)
+{
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ struct dst_entry *dst = skb->dst;
+ int err = -EINVAL;
+
+ if (!dst->neighbour)
+ goto error;
+
+ /*
+ * Hop count exceeded.
+ */
+ err = 0;
+ if (++cb->hops > 30)
+ goto drop;
+
+ /*
+ * Forwarding firewall
+ */
+#ifdef CONFIG_DECNET_FW
+ switch(call_fw_firewall(PF_DECnet, dst->dev, NULL, NULL, &skb)) {
+ case FW_REJECT:
+ dst->neighbour->ops->error_report(dst->neighbour, skb);
+ return -EPERM;
+ case FW_BLOCK:
+ default:
+ goto drop;
+ case FW_ACCEPT:
+ }
+#endif /* CONFIG_DECNET_FW */
+
+ skb->dev = dst->dev;
+
+ /*
+ * If packet goes out same interface it came in on, then set
+ * the Intra-Ethernet bit. This has no effect for short
+ * packets, so we don't need to test for them here.
+ */
+ if (cb->iif == dst->dev->ifindex)
+ cb->rt_flags |= DN_RT_F_IE;
+ else
+ cb->rt_flags &= ~DN_RT_F_IE;
+
+#ifdef CONFIG_DECNET_FW
+ switch(call_out_firewall(PF_DECnet, dst->dev, NULL, NULL, &skb)) {
+ case FW_REJECT:
+ dst->neighbour->ops->error_report(dst->neighbour, skb);
+ return -EPERM;
+ case FW_BLOCK:
+ default:
+ goto drop;
+ case FW_ACCEPT:
+ }
+#endif /* CONFIG_DECNET_FW */
+
+ return dst->neighbour->output(skb);
+
+
+error:
+ if (net_ratelimit())
+ printk(KERN_DEBUG "dn_forward: This should not happen\n");
+drop:
+ kfree_skb(skb);
+
+ return err;
+}
+
+/*
+ * Simple frontend to the l2 routing function which filters
+ * traffic not in our area when we should only do l1
+ * routing.
+ */
+static int dn_l1_forward(struct sk_buff *skb)
+{
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+
+ if ((dn_ntohs(cb->dst ^ decnet_address) & 0xfc00) == 0)
+ return dn_l2_forward(skb);
+
+ kfree_skb(skb);
+ return 0;
+}
+#endif
+
+/*
+ * Drop packet. This is used for endnodes and for
+ * when we should not be forwarding packets from
+ * this dest.
+ */
+static int dn_blackhole(struct sk_buff *skb)
+{
+ kfree_skb(skb);
+ return 0;
+}
+
+/*
+ * Used to catch bugs. This should never normally get
+ * called.
+ */
+static int dn_rt_bug(struct sk_buff *skb)
+{
+ if (net_ratelimit()) {
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+
+ printk(KERN_DEBUG "dn_rt_bug: skb from:%04x to:%04x\n",
+ cb->src, cb->dst);
+ }
+
+ kfree_skb(skb);
+
+ return -EINVAL;
+}
+
+static int dn_route_output_slow(struct sock *sk)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ dn_address dest = dn_saddr2dn(&scp->peer);
+ struct dn_route *rt = NULL;
+ struct device *dev = decnet_default_device;
+ struct neighbour *neigh = NULL;
+ struct dn_dev *dn_db;
+ unsigned char addr[6];
+
+#ifdef CONFIG_DECNET_ROUTER
+ if ((decnet_node_type == DN_RT_INFO_L1RT) || (decnet_node_type == DN_RT_INFO_L2RT)) {
+#if 0
+ struct dn_fib_ent *fe = dn_fib_lookup(dest, decnet_address);
+
+ if (fe != NULL) {
+ neigh = neigh_clone(fe->neigh);
+ dn_fib_release(fe);
+ goto got_route;
+ }
+#endif
+ }
+#endif
+
+ dn_dn2eth(addr, dest);
+
+ /* Look in On-Ethernet cache first */
+ if ((neigh = dn_neigh_lookup(&dn_neigh_table, &addr)) != NULL)
+ goto got_route;
+
+ if (dev == NULL)
+ return -EINVAL;
+
+ /* FIXME: We need to change this for routing nodes */
+ /* Send to default router if that doesn't work */
+ if ((neigh = neigh_lookup(&dn_neigh_table, &addr, dev)) != NULL)
+ goto got_route;
+
+ /* Send to default device (and hope for the best) if above fail */
+ if ((neigh = __neigh_lookup(&dn_neigh_table, &addr, dev, 1)) != NULL)
+ goto got_route;
+
+
+ return -EINVAL;
+
+got_route:
+
+ if ((rt = dst_alloc(sizeof(struct dn_route), &dn_dst_ops)) == NULL) {
+ neigh_release(neigh);
+ return -EINVAL;
+ }
+
+ dn_db = (struct dn_dev *)neigh->dev->dn_ptr;
+
+ rt->rt_saddr = decnet_address;
+ rt->rt_daddr = dest;
+ rt->rt_oif = neigh->dev->ifindex;
+ rt->rt_iif = 0;
+
+ rt->u.dst.neighbour = neigh;
+ rt->u.dst.dev = neigh->dev;
+ rt->u.dst.lastuse = jiffies;
+ rt->u.dst.output = dn_output;
+ rt->u.dst.input = dn_rt_bug;
+
+ if (dn_dev_islocal(neigh->dev, rt->rt_daddr))
+ rt->u.dst.input = dn_nsp_rx;
+
+ dn_insert_route(rt);
+ sk->dst_cache = &rt->u.dst;
+
+ return 0;
+}
+
+int dn_route_output(struct sock *sk)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ dn_address dest = dn_saddr2dn(&scp->peer);
+ unsigned hash = dn_hash(dest);
+ struct dn_route *rt = NULL;
+ unsigned short src = dn_saddr2dn(&scp->addr);
+
+ start_bh_atomic();
+ for(rt = dn_route_cache[hash]; rt; rt = rt->u.rt_next) {
+ if ((dest == rt->rt_daddr) &&
+ (src == rt->rt_saddr) &&
+ (rt->rt_iif == 0) &&
+ (rt->rt_oif != 0)) {
+ rt->u.dst.lastuse = jiffies;
+ atomic_inc(&rt->u.dst.use);
+ atomic_inc(&rt->u.dst.refcnt);
+ end_bh_atomic();
+ sk->dst_cache = &rt->u.dst;
+ return 0;
+ }
+ }
+ end_bh_atomic();
+
+ return dn_route_output_slow(sk);
+}
+
+static int dn_route_input_slow(struct sk_buff *skb)
+{
+ struct dn_route *rt = NULL;
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ struct device *dev = skb->dev;
+ struct neighbour *neigh = NULL;
+ unsigned char addr[6];
+
+ /*
+ * In this case we've just received a packet from a source
+ * outside ourselves pretending to come from us. We don't
+ * allow it any further to prevent routing loops, spoofing and
+ * other nasties. Loopback packets already have the dst attached
+ * so this only affects packets which have originated elsewhere.
+ */
+ if (dn_dev_islocal(dev, cb->src))
+ return 1;
+
+#ifdef CONFIG_DECNET_ROUTER
+ if ((decnet_node_type == DN_RT_INFO_L1RT) || (decnet_node_type == DN_RT_INFO_L2RT)) {
+#if 0
+ struct dn_fib_ent *fe = NULL;
+
+ fe = dn_fib_lookup(cb->src, cb->dst);
+
+ /* Try routing table first */
+ if (fe != NULL) {
+ neigh = neigh_clone(fe->neigh);
+ dn_fib_release(fe);
+ goto got_route;
+ }
+#endif
+ }
+#endif
+
+ dn_dn2eth(addr, cb->src);
+
+ /* Now see if we are directly connected */
+ if ((neigh = dn_neigh_lookup(&dn_neigh_table, &addr)) != NULL)
+ goto got_route;
+
+ if (dev == NULL)
+ return -EINVAL;
+
+ /* FIXME: Try the default router here .... */
+
+ if ((neigh = __neigh_lookup(&dn_neigh_table, &addr, dev, 1)) != NULL)
+ goto got_route;
+
+ return -EINVAL;
+
+got_route:
+
+ if ((rt = dst_alloc(sizeof(struct dn_route), &dn_dst_ops)) == NULL) {
+ neigh_release(neigh);
+ return -EINVAL;
+ }
+
+ rt->rt_saddr = cb->dst;
+ rt->rt_daddr = cb->src;
+ rt->rt_oif = 0;
+ rt->rt_iif = neigh->dev->ifindex;
+
+ rt->u.dst.neighbour = neigh;
+ rt->u.dst.dev = neigh->dev;
+ rt->u.dst.lastuse = jiffies;
+ rt->u.dst.output = dn_output;
+
+ switch(decnet_node_type) {
+ case DN_RT_INFO_ENDN:
+ rt->u.dst.input = dn_blackhole;
+ break;
+#ifdef CONFIG_DECNET_ROUTER
+ case DN_RT_INFO_L1RT:
+ rt->u.dst.input = dn_l1_forward;
+ break;
+ case DN_RT_INFO_L2RT:
+ rt->u.dst.input = dn_l2_forward;
+ break;
+#endif /* CONFIG_DECNET_ROUTER */
+ default:
+ rt->u.dst.input = dn_blackhole;
+ if (net_ratelimit())
+ printk(KERN_DEBUG "dn_route_input_slow: What kind of node are we?\n");
+ }
+
+ if (dn_dev_islocal(dev, cb->dst))
+ rt->u.dst.input = dn_nsp_rx;
+
+ dn_insert_route(rt);
+ skb->dst = (struct dst_entry *)rt;
+
+ return 0;
+}
+
+int dn_route_input(struct sk_buff *skb)
+{
+ struct dn_route *rt;
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ unsigned hash = dn_hash(cb->src);
+
+ if (skb->dst)
+ return 0;
+
+ for(rt = dn_route_cache[hash]; rt != NULL; rt = rt->u.rt_next) {
+ if ((rt->rt_saddr == cb->dst) &&
+ (rt->rt_daddr == cb->src) &&
+ (rt->rt_oif == 0) &&
+ (rt->rt_iif == cb->iif)) {
+ rt->u.dst.lastuse = jiffies;
+ atomic_inc(&rt->u.dst.use);
+ atomic_inc(&rt->u.dst.refcnt);
+ skb->dst = (struct dst_entry *)rt;
+ return 0;
+ }
+ }
+
+ return dn_route_input_slow(skb);
+}
+
+#ifdef CONFIG_DECNET_ROUTER
+#ifdef CONFIG_RTNETLINK
+static int dn_rt_fill_info(struct sk_buff *skb, u32 pid, u32 seq, int event, int nowait)
+{
+ struct dn_route *rt = (struct dn_route *)skb->dst;
+ struct rtmsg *r;
+ struct nlmsghdr *nlh;
+ unsigned char *b = skb->tail;
+
+ nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*r));
+ r = NLMSG_DATA(nlh);
+ nlh->nlmsg_flags = nowait ? NLM_F_MULTI : 0;
+ r->rtm_family = AF_DECnet;
+ r->rtm_dst_len = 16;
+ r->rtm_src_len = 16;
+ r->rtm_tos = 0;
+ r->rtm_table = 0;
+ r->rtm_type = 0;
+ r->rtm_scope = RT_SCOPE_UNIVERSE;
+ r->rtm_protocol = RTPROT_UNSPEC;
+ RTA_PUT(skb, RTA_DST, 2, &rt->rt_daddr);
+ RTA_PUT(skb, RTA_SRC, 2, &rt->rt_saddr);
+ if (rt->u.dst.dev)
+ RTA_PUT(skb, RTA_OIF, sizeof(int), &rt->u.dst.dev->ifindex);
+ if (rt->u.dst.window)
+ RTA_PUT(skb, RTAX_WINDOW, sizeof(unsigned), &rt->u.dst.window);
+ if (rt->u.dst.rtt)
+ RTA_PUT(skb, RTAX_RTT, sizeof(unsigned), &rt->u.dst.rtt);
+
+ nlh->nlmsg_len = skb->tail - b;
+ return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+ skb_trim(skb, b - skb->data);
+ return -1;
+}
+
+int dn_fib_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct rtattr **rta = arg;
+ /* struct rtmsg *rtm = NLMSG_DATA(nlh); */
+ struct dn_route *rt = NULL;
+ dn_address dst = 0;
+ dn_address src = 0;
+ int iif = 0;
+ int err;
+ struct sk_buff *skb;
+
+ skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (skb == NULL)
+ return -ENOBUFS;
+ skb->mac.raw = skb->data;
+
+ if (rta[RTA_SRC-1])
+ memcpy(&src, RTA_DATA(rta[RTA_SRC-1]), 2);
+ if (rta[RTA_DST-1])
+ memcpy(&dst, RTA_DATA(rta[RTA_DST-1]), 2);
+ if (rta[RTA_IIF-1])
+ memcpy(&iif, RTA_DATA(rta[RTA_IIF-1]), sizeof(int));
+
+ if (iif) {
+ struct device *dev;
+ if ((dev = dev_get_by_index(iif)) == NULL)
+ return -ENODEV;
+ if (!dev->dn_ptr)
+ return -ENODEV;
+ skb->protocol = __constant_htons(ETH_P_DNA_RT);
+ skb->dev = dev;
+ start_bh_atomic();
+ err = dn_route_input(skb);
+ end_bh_atomic();
+ rt = (struct dn_route *)skb->dst;
+ if (!err && rt->u.dst.error)
+ err = rt->u.dst.error;
+ } else {
+ int oif = 0;
+ if (rta[RTA_OIF-1])
+ memcpy(&oif, RTA_DATA(rta[RTA_OIF-1]), sizeof(int));
+ err = -EOPNOTSUPP;
+ }
+ if (err) {
+ kfree_skb(skb);
+ return err;
+ }
+ skb->dst = &rt->u.dst;
+
+ NETLINK_CB(skb).dst_pid = NETLINK_CB(in_skb).pid;
+
+ err = dn_rt_fill_info(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, RTM_NEWROUTE, 0);
+
+ if (err == 0)
+ return 0;
+ if (err < 0)
+ return -EMSGSIZE;
+
+ err = netlink_unicast(rtnl, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);
+
+ return err;
+}
+#endif /* CONFIG_RTNETLINK */
+#endif /* CONFIG_DECNET_ROUTER */
+
+#ifdef CONFIG_PROC_FS
+
+static int decnet_cache_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int len = 0;
+ off_t pos = 0;
+ off_t begin = 0;
+ struct dn_route *rt;
+ int i;
+ char buf1[DN_ASCBUF_LEN], buf2[DN_ASCBUF_LEN];
+
+ start_bh_atomic();
+ for(i = 0; i < DN_HASHBUCKETS; i++) {
+ rt = dn_route_cache[i];
+ for(; rt != NULL; rt = rt->u.rt_next) {
+ len += sprintf(buffer + len, "%-8s %-7s %-7s %04d %04d %04d\n",
+ rt->u.dst.dev ? rt->u.dst.dev->name : "*",
+ dn_addr2asc(dn_ntohs(rt->rt_daddr), buf1),
+ dn_addr2asc(dn_ntohs(rt->rt_saddr), buf2),
+ atomic_read(&rt->u.dst.use),
+ atomic_read(&rt->u.dst.refcnt),
+ (int)rt->u.dst.rtt
+ );
+
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ break;
+ }
+ if (pos > offset + length)
+ break;
+ }
+ end_bh_atomic();
+
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
+
+ if (len > length) len = length;
+
+ return(len);
+}
+
+static struct proc_dir_entry proc_net_decnet_cache = {
+ PROC_NET_DN_CACHE, 12, "decnet_cache",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ decnet_cache_get_info
+};
+
+#endif /* CONFIG_PROC_FS */
+
+void __init dn_route_init(void)
+{
+ memset(dn_route_cache, 0, sizeof(struct dn_route *) * DN_HASHBUCKETS);
+
+ dn_route_timer.function = dn_dst_check_expire;
+ dn_route_timer.expires = jiffies + decnet_dst_gc_interval * HZ;
+ add_timer(&dn_route_timer);
+
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_net_decnet_cache);
+#endif /* CONFIG_PROC_FS */
+}
+
+#ifdef CONFIG_DECNET_MODULE
+void dn_route_cleanup(void)
+{
+ del_timer(&dn_route_timer);
+ dn_run_flush(0);
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(PROC_NET_DN_CACHE);
+#endif /* CONFIG_PROC_FS */
+}
+#endif /* CONFIG_DECNET_MODULE */
diff --git a/net/decnet/dn_timer.c b/net/decnet/dn_timer.c
new file mode 100644
index 000000000..8cfeaee70
--- /dev/null
+++ b/net/decnet/dn_timer.c
@@ -0,0 +1,164 @@
+/*
+ * DECnet An implementation of the DECnet protocol suite for the LINUX
+ * operating system. DECnet is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * DECnet Socket Timer Functions
+ *
+ * Author: Steve Whitehouse <SteveW@ACM.org>
+ *
+ *
+ * Changes:
+ * Steve Whitehouse : Made keepalive timer part of the same
+ * timer idea.
+ * Steve Whitehouse : Added checks for sk->sock_readers
+ */
+#include <linux/config.h>
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/timer.h>
+#include <net/sock.h>
+#include <asm/spinlock.h>
+#include <asm/atomic.h>
+#include <net/dn.h>
+
+/*
+ * Fast timer is for delayed acks (200mS max)
+ * Slow timer is for everything else (n * 500mS)
+ */
+
+#define FAST_INTERVAL (HZ/5)
+#define SLOW_INTERVAL (HZ/2)
+
+static void dn_slow_timer(unsigned long arg);
+
+void dn_start_slow_timer(struct sock *sk)
+{
+ sk->timer.expires = jiffies + SLOW_INTERVAL;
+ sk->timer.function = dn_slow_timer;
+ sk->timer.data = (unsigned long)sk;
+
+ add_timer(&sk->timer);
+}
+
+void dn_stop_slow_timer(struct sock *sk)
+{
+ unsigned long cpuflags;
+
+ save_flags(cpuflags);
+ cli();
+ del_timer(&sk->timer);
+ restore_flags(cpuflags);
+}
+
+static void dn_slow_timer(unsigned long arg)
+{
+ struct sock *sk = (struct sock *)arg;
+ struct dn_scp *scp = &sk->protinfo.dn;
+
+ bh_lock_sock(sk);
+
+ if (sk->lock.users != 0) {
+ sk->timer.expires = jiffies + HZ / 10;
+ add_timer(&sk->timer);
+ goto out;
+ }
+
+ /*
+ * The persist timer is the standard slow timer used for retransmits
+ * in both connection establishment and disconnection as well as
+ * in the RUN state. The different states are catered for by changing
+ * the function pointer in the socket. Setting the timer to a value
+ * of zero turns it off. We allow the persist_fxn to turn the
+ * timer off in a permant way by returning non-zero, so that
+ * timer based routines may remove sockets.
+ */
+ if (scp->persist && scp->persist_fxn) {
+ if (scp->persist <= SLOW_INTERVAL) {
+ scp->persist = 0;
+
+ if (scp->persist_fxn(sk))
+ goto out;
+ } else {
+ scp->persist -= SLOW_INTERVAL;
+ }
+ }
+
+ /*
+ * Check for keepalive timeout. After the other timer 'cos if
+ * the previous timer caused a retransmit, we don't need to
+ * do this. scp->stamp is the last time that we sent a packet.
+ * The keepalive function sends a link service packet to the
+ * other end. If it remains unacknowledged, the standard
+ * socket timers will eventually shut the socket down. Each
+ * time we do this, scp->stamp will be updated, thus
+ * we won't try and send another until scp->keepalive has passed
+ * since the last successful transmission.
+ */
+ if (scp->keepalive && scp->keepalive_fxn && (scp->state == DN_RUN)) {
+ if ((jiffies - scp->stamp) >= scp->keepalive)
+ scp->keepalive_fxn(sk);
+ }
+
+ sk->timer.expires = jiffies + SLOW_INTERVAL;
+
+ add_timer(&sk->timer);
+out:
+ bh_unlock_sock(sk);
+}
+
+static void dn_fast_timer(unsigned long arg)
+{
+ struct sock *sk = (struct sock *)arg;
+ struct dn_scp *scp = &sk->protinfo.dn;
+
+ bh_lock_sock(sk);
+ if (sk->lock.users != 0) {
+ scp->delack_timer.expires = jiffies + HZ / 20;
+ add_timer(&scp->delack_timer);
+ goto out;
+ }
+
+ scp->delack_pending = 0;
+
+ if (scp->delack_fxn)
+ scp->delack_fxn(sk);
+out:
+ bh_unlock_sock(sk);
+}
+
+void dn_start_fast_timer(struct sock *sk)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ unsigned long cpuflags;
+
+ save_flags(cpuflags);
+ cli();
+ if (!scp->delack_pending) {
+ scp->delack_pending = 1;
+ scp->delack_timer.next =
+ scp->delack_timer.prev = NULL;
+ scp->delack_timer.expires = jiffies + FAST_INTERVAL;
+ scp->delack_timer.data = (unsigned long)sk;
+ scp->delack_timer.function = dn_fast_timer;
+ add_timer(&scp->delack_timer);
+ }
+ restore_flags(cpuflags);
+}
+
+void dn_stop_fast_timer(struct sock *sk)
+{
+ struct dn_scp *scp = &sk->protinfo.dn;
+ unsigned long cpuflags;
+
+ save_flags(cpuflags);
+ cli();
+ if (scp->delack_pending) {
+ scp->delack_pending = 0;
+ del_timer(&scp->delack_timer);
+ }
+ restore_flags(cpuflags);
+}
+
diff --git a/net/decnet/sysctl_net_decnet.c b/net/decnet/sysctl_net_decnet.c
new file mode 100644
index 000000000..134ac585b
--- /dev/null
+++ b/net/decnet/sysctl_net_decnet.c
@@ -0,0 +1,473 @@
+/*
+ * DECnet An implementation of the DECnet protocol suite for the LINUX
+ * operating system. DECnet is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * DECnet sysctl support functions
+ *
+ * Author: Steve Whitehouse <SteveW@ACM.org>
+ *
+ *
+ * Changes:
+ *
+ */
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+#include <linux/fs.h>
+#include <linux/netdevice.h>
+#include <linux/string.h>
+#include <net/neighbour.h>
+#include <net/dst.h>
+
+#include <asm/uaccess.h>
+
+#include <net/dn.h>
+#include <net/dn_dev.h>
+#include <net/dn_route.h>
+
+
+int decnet_debug_level = 0;
+int decnet_time_wait = 30;
+int decnet_dn_count = 3;
+int decnet_di_count = 5;
+int decnet_dr_count = 5;
+extern int decnet_dst_gc_interval;
+static int min_decnet_time_wait[] = { 5 };
+static int max_decnet_time_wait[] = { 600 };
+static int min_state_count[] = { 1 };
+static int max_state_count[] = { NSP_MAXRXTSHIFT };
+static int min_decnet_dst_gc_interval[] = { 1 };
+static int max_decnet_dst_gc_interval[] = { 60 };
+static char node_name[7] = "???";
+
+static struct ctl_table_header *dn_table_header = NULL;
+
+/*
+ * ctype.h :-)
+ */
+#define ISNUM(x) (((x) >= '0') && ((x) <= '9'))
+#define ISLOWER(x) (((x) >= 'a') && ((x) <= 'z'))
+#define ISUPPER(x) (((x) >= 'A') && ((x) <= 'Z'))
+#define ISALPHA(x) (ISLOWER(x) || ISUPPER(x))
+#define INVALID_END_CHAR(x) (ISNUM(x) || ISALPHA(x))
+
+/*
+ * Simple routine to parse an ascii DECnet address
+ * into a network order address.
+ */
+static int parse_addr(dn_address *addr, char *str)
+{
+ dn_address area, node;
+
+ while(*str && !ISNUM(*str)) str++;
+
+ if (*str == 0)
+ return -1;
+
+ area = (*str++ - '0');
+ if (ISNUM(*str)) {
+ area *= 10;
+ area += (*str++ - '0');
+ }
+
+ if (*str++ != '.')
+ return -1;
+
+ if (!ISNUM(*str))
+ return -1;
+
+ node = *str++ - '0';
+ if (ISNUM(*str)) {
+ node *= 10;
+ node += (*str++ - '0');
+ }
+ if (ISNUM(*str)) {
+ node *= 10;
+ node += (*str++ - '0');
+ }
+ if (ISNUM(*str)) {
+ node *= 10;
+ node += (*str++ - '0');
+ }
+
+ if ((node > 1023) || (area > 63))
+ return -1;
+
+ if (INVALID_END_CHAR(*str))
+ return -1;
+
+ *addr = dn_htons((area << 10) | node);
+
+ return 0;
+}
+
+static char *node2str(int n)
+{
+ switch(n) {
+ case DN_RT_INFO_ENDN:
+ return "EndNode\n";
+ case DN_RT_INFO_L1RT:
+ return "Level 1 Router\n";
+ case DN_RT_INFO_L2RT:
+ return "Level 2 Router\n";
+ }
+
+ return "Unknown\n";
+}
+
+static int dn_node_type_strategy(ctl_table *table, int *name, int nlen,
+ void *oldval, size_t *oldlenp,
+ void *newval, size_t newlen,
+ void **context)
+{
+ int len;
+ int type;
+
+ if (oldval && oldlenp) {
+ if (get_user(len, oldlenp))
+ return -EFAULT;
+ if (len) {
+ if (len != sizeof(int))
+ return -EINVAL;
+ if (put_user(decnet_node_type, (int *)oldval))
+ return -EFAULT;
+ }
+ }
+
+ if (newval && newlen) {
+ if (newlen != sizeof(int))
+ return -EINVAL;
+
+ if (get_user(type, (int *)newval))
+ return -EFAULT;
+
+ switch(type) {
+ case DN_RT_INFO_ENDN: /* EndNode */
+#ifdef CONFIG_DECNET_ROUTER
+ case DN_RT_INFO_L1RT: /* Level 1 Router */
+ case DN_RT_INFO_L2RT: /* Level 2 Router */
+#endif
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (decnet_node_type != type) {
+ dn_dev_devices_off();
+ decnet_node_type = type;
+ dn_dev_devices_on();
+ }
+ }
+ return 0;
+}
+
+static int dn_node_type_handler(ctl_table *table, int write,
+ struct file * filp,
+ void *buffer, size_t *lenp)
+{
+ char *s = node2str(decnet_node_type);
+ int len = strlen(s);
+
+ if (!*lenp || (filp->f_pos && !write)) {
+ *lenp = 0;
+ return 0;
+ }
+
+ if (write) {
+ char c = *(char *)buffer;
+ int type = 0;
+
+ switch(c) {
+ case 'e':
+ case 'E':
+ case '0':
+ type = DN_RT_INFO_ENDN;
+ break;
+#ifdef CONFIG_DECNET_ROUTER
+ case 'r':
+ case '1':
+ type = DN_RT_INFO_L1RT;
+ break;
+ case 'R':
+ case '2':
+ type = DN_RT_INFO_L2RT;
+ break;
+#endif /* CONFIG_DECNET_ROUTER */
+ default:
+ return -EINVAL;
+ }
+
+ if (decnet_node_type != type) {
+ dn_dev_devices_off();
+ decnet_node_type = type;
+ dn_dev_devices_on();
+ }
+
+ filp->f_pos += 1;
+
+ return 0;
+ }
+
+ if (len > *lenp) len = *lenp;
+
+ if (copy_to_user(buffer, s, len))
+ return -EFAULT;
+
+ *lenp = len;
+ filp->f_pos += len;
+
+ return 0;
+}
+
+static int dn_node_address_strategy(ctl_table *table, int *name, int nlen,
+ void *oldval, size_t *oldlenp,
+ void *newval, size_t newlen,
+ void **context)
+{
+ int len;
+ dn_address addr;
+
+ if (oldval && oldlenp) {
+ if (get_user(len, oldlenp))
+ return -EFAULT;
+ if (len) {
+ if (len != sizeof(unsigned short))
+ return -EINVAL;
+ if (put_user(decnet_address, (unsigned short *)oldval))
+ return -EFAULT;
+ }
+ }
+ if (newval && newlen) {
+ if (newlen != sizeof(unsigned short))
+ return -EINVAL;
+ if (get_user(addr, (unsigned short *)newval))
+ return -EFAULT;
+
+ dn_dev_devices_off();
+
+ decnet_address = addr;
+ dn_dn2eth(decnet_ether_address, decnet_address);
+
+ dn_dev_devices_on();
+ }
+ return 0;
+}
+
+static int dn_node_address_handler(ctl_table *table, int write,
+ struct file *filp,
+ void *buffer, size_t *lenp)
+{
+ char addr[DN_ASCBUF_LEN];
+ int len;
+ dn_address dnaddr;
+
+ if (!*lenp || (filp->f_pos && !write)) {
+ *lenp = 0;
+ return 0;
+ }
+
+ if (write) {
+ int len = (*lenp < DN_ASCBUF_LEN) ? *lenp : (DN_ASCBUF_LEN-1);
+
+ if (copy_from_user(addr, buffer, len))
+ return -EFAULT;
+
+ addr[len] = 0;
+
+ if (parse_addr(&dnaddr, buffer))
+ return -EINVAL;
+
+ dn_dev_devices_off();
+
+ decnet_address = dnaddr;
+ dn_dn2eth(decnet_ether_address, decnet_address);
+
+ dn_dev_devices_on();
+
+ filp->f_pos += len;
+
+ return 0;
+ }
+
+ dn_addr2asc(dn_ntohs(decnet_address), addr);
+ len = strlen(addr);
+ addr[len++] = '\n';
+
+ if (len > *lenp) len = *lenp;
+
+ if (copy_to_user(buffer, addr, len))
+ return -EFAULT;
+
+ *lenp = len;
+ filp->f_pos += len;
+
+ return 0;
+}
+
+
+static int dn_def_dev_strategy(ctl_table *table, int *name, int nlen,
+ void *oldval, size_t *oldlenp,
+ void *newval, size_t newlen,
+ void **context)
+{
+ size_t len;
+ struct device *dev = decnet_default_device;
+ char devname[17];
+ size_t namel;
+
+ devname[0] = 0;
+
+ if (oldval && oldlenp) {
+ if (get_user(len, oldlenp))
+ return -EFAULT;
+ if (len) {
+ if (dev)
+ strcpy(devname, dev->name);
+
+ namel = strlen(devname) + 1;
+ if (len > namel) len = namel;
+
+ if (copy_to_user(oldval, devname, len))
+ return -EFAULT;
+
+ if (put_user(len, oldlenp))
+ return -EFAULT;
+ }
+ }
+
+ if (newval && newlen) {
+ if (newlen > 16)
+ return -E2BIG;
+
+ if (copy_from_user(devname, newval, newlen))
+ return -EFAULT;
+
+ devname[newlen] = 0;
+
+ if ((dev = dev_get(devname)) == NULL)
+ return -ENODEV;
+
+ if (dev->dn_ptr == NULL)
+ return -ENODEV;
+
+ decnet_default_device = dev;
+ }
+
+ return 0;
+}
+
+
+static int dn_def_dev_handler(ctl_table *table, int write,
+ struct file * filp,
+ void *buffer, size_t *lenp)
+{
+ int len;
+ struct device *dev = decnet_default_device;
+ char devname[17];
+
+ if (!*lenp || (filp->f_pos && !write)) {
+ *lenp = 0;
+ return 0;
+ }
+
+ if (write) {
+
+ if (*lenp > 16)
+ return -E2BIG;
+
+ if (copy_from_user(devname, buffer, *lenp))
+ return -EFAULT;
+
+ devname[*lenp] = 0;
+
+ if ((dev = dev_get(devname)) == NULL)
+ return -ENODEV;
+
+ if (dev->dn_ptr == NULL)
+ return -ENODEV;
+
+ decnet_default_device = dev;
+ filp->f_pos += *lenp;
+
+ return 0;
+ }
+
+ if (dev == NULL) {
+ *lenp = 0;
+ return 0;
+ }
+
+ strcpy(devname, dev->name);
+ len = strlen(devname);
+ devname[len++] = '\n';
+
+ if (len > *lenp) len = *lenp;
+
+ if (copy_to_user(buffer, devname, len))
+ return -EFAULT;
+
+ *lenp = len;
+ filp->f_pos += len;
+
+ return 0;
+}
+
+static ctl_table dn_table[] = {
+ {NET_DECNET_NODE_TYPE, "node_type", NULL, 1, 0644, NULL,
+ dn_node_type_handler, dn_node_type_strategy, NULL,
+ NULL, NULL},
+ {NET_DECNET_NODE_ADDRESS, "node_address", NULL, 7, 0644, NULL,
+ dn_node_address_handler, dn_node_address_strategy, NULL,
+ NULL, NULL},
+ {NET_DECNET_NODE_NAME, "node_name", node_name, 7, 0644, NULL,
+ &proc_dostring, &sysctl_string, NULL, NULL, NULL},
+ {NET_DECNET_DEFAULT_DEVICE, "default_device", NULL, 16, 0644, NULL,
+ dn_def_dev_handler, dn_def_dev_strategy, NULL, NULL, NULL},
+ {NET_DECNET_TIME_WAIT, "time_wait", &decnet_time_wait,
+ sizeof(int), 0644,
+ NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL,
+ &min_decnet_time_wait, &max_decnet_time_wait},
+ {NET_DECNET_DN_COUNT, "dn_count", &decnet_dn_count,
+ sizeof(int), 0644,
+ NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL,
+ &min_state_count, &max_state_count},
+ {NET_DECNET_DI_COUNT, "di_count", &decnet_di_count,
+ sizeof(int), 0644,
+ NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL,
+ &min_state_count, &max_state_count},
+ {NET_DECNET_DR_COUNT, "dr_count", &decnet_dr_count,
+ sizeof(int), 0644,
+ NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL,
+ &min_state_count, &max_state_count},
+ {NET_DECNET_DST_GC_INTERVAL, "dst_gc_interval", &decnet_dst_gc_interval,
+ sizeof(int), 0644,
+ NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL,
+ &min_decnet_dst_gc_interval, &max_decnet_dst_gc_interval},
+ {NET_DECNET_DEBUG_LEVEL, "debug", &decnet_debug_level,
+ sizeof(int), 0644,
+ NULL, &proc_dointvec, &sysctl_intvec, NULL,
+ NULL, NULL},
+ {0}
+};
+
+static ctl_table dn_dir_table[] = {
+ {NET_DECNET, "decnet", NULL, 0, 0555, dn_table},
+ {0}
+};
+
+static ctl_table dn_root_table[] = {
+ {CTL_NET, "net", NULL, 0, 0555, dn_dir_table},
+ {0}
+};
+
+void dn_register_sysctl(void)
+{
+ dn_table_header = register_sysctl_table(dn_root_table, 1);
+}
+
+void dn_unregister_sysctl(void)
+{
+ unregister_sysctl_table(dn_table_header);
+}
+