Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@penguin.transmeta.com>2002-02-24 20:10:37 -0800
committerLinus Torvalds <torvalds@penguin.transmeta.com>2002-02-24 20:10:37 -0800
commit7c6fc5d0e9acae2214018c63ed8de2ff379940cd (patch)
tree296695c3831f1a4ef4f15a0d6a41fc5c692206d1
parenta99dcd865e3ac3b326ba15b5873fcb043197fc6a (diff)
parenta01be2d5be713d095e947c273e75a7d5c9a274ac (diff)
Merge http://linux-isdn.bkbits.net/linux-2.5.isdn
into penguin.transmeta.com:/home/penguin/torvalds/repositories/kernel/linux
-rw-r--r--drivers/isdn/Config.help4
-rw-r--r--drivers/isdn/Config.in1
-rw-r--r--drivers/isdn/hisax/Makefile1
-rw-r--r--drivers/isdn/hisax/hisax_hfcpci.c1646
-rw-r--r--drivers/isdn/hisax/hisax_hfcpci.h40
-rw-r--r--drivers/isdn/hisax/st5481_usb.c2
6 files changed, 1693 insertions, 1 deletions
diff --git a/drivers/isdn/Config.help b/drivers/isdn/Config.help
index a2bb99bc1fb8..ea6d042be449 100644
--- a/drivers/isdn/Config.help
+++ b/drivers/isdn/Config.help
@@ -161,6 +161,10 @@ CONFIG_HISAX_NI1
Enable this if you like to use ISDN in US on a NI1 basic rate
interface.
+CONFIG_HISAX_MAX_CARDS
+ This option allows you to specify the maximum number of cards which
+ the HiSax driver will be able to handle.
+
CONFIG_HISAX_16_0
This enables HiSax support for the Teles ISDN-cards S0-16.0, S0-8
and many compatibles.
diff --git a/drivers/isdn/Config.in b/drivers/isdn/Config.in
index 0dd3cc6eaae9..62c59cdd481e 100644
--- a/drivers/isdn/Config.in
+++ b/drivers/isdn/Config.in
@@ -84,6 +84,7 @@ if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then
dep_tristate 'ST5481 USB ISDN modem (EXPERIMENTAL)' CONFIG_HISAX_ST5481 $CONFIG_HISAX $CONFIG_USB $CONFIG_EXPERIMENTAL
dep_tristate 'AVM Fritz!Card PCI/PCIv2/PnP support (EXPERIMENTAL)' CONFIG_HISAX_FRITZ_PCIPNP $CONFIG_HISAX $CONFIG_EXPERIMENTAL
dep_tristate 'AVM Fritz!Card classic support (EXPERIMENTAL)' CONFIG_HISAX_FRITZ_CLASSIC $CONFIG_HISAX $CONFIG_EXPERIMENTAL
+ dep_tristate 'HFC PCI support (EXPERIMENTAL)' CONFIG_HISAX_HFCPCI $CONFIG_HISAX $CONFIG_EXPERIMENTAL
fi
endmenu
diff --git a/drivers/isdn/hisax/Makefile b/drivers/isdn/hisax/Makefile
index 8fdf469432d8..ee9ff7399618 100644
--- a/drivers/isdn/hisax/Makefile
+++ b/drivers/isdn/hisax/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_HISAX_ELSA_CS) += elsa_cs.o
obj-$(CONFIG_HISAX_ST5481) += hisax_st5481.o
obj-$(CONFIG_HISAX_FRITZ_PCIPNP) += hisax_isac.o hisax_fcpcipnp.o
obj-$(CONFIG_HISAX_FRITZ_CLASSIC) += hisax_isac.o hisax_hscx.o hisax_fcclassic.o
+obj-$(CONFIG_HISAX_FRITZ_PCIPNP) += hisax_hfcpci.o
CERT := $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?)
CFLAGS_cert.o := -DCERTIFICATION=$(CERT)
diff --git a/drivers/isdn/hisax/hisax_hfcpci.c b/drivers/isdn/hisax/hisax_hfcpci.c
new file mode 100644
index 000000000000..281e3f970c25
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_hfcpci.c
@@ -0,0 +1,1646 @@
+/*
+ * Driver for HFC PCI based cards
+ *
+ * Author Kai Germaschewski
+ * Copyright 2002 by Kai Germaschewski <kai.germaschewski@gmx.de>
+ * 2000 by Karsten Keil <keil@isdn4linux.de>
+ * 2000 by Werner Cornelius <werner@isdn4linux.de>
+ *
+ * based upon Werner Cornelius's original hfc_pci.c driver
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+// XXX timer3
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <asm/delay.h>
+#include "hisax_hfcpci.h"
+
+// debugging cruft
+#define __debug_variable debug
+#include "hisax_debug.h"
+
+#ifdef CONFIG_HISAX_DEBUG
+static int debug = 0;
+MODULE_PARM(debug, "i");
+#endif
+
+MODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Werner Cornelius <werner@isdn4linux.de>");
+MODULE_DESCRIPTION("HFC PCI ISDN driver");
+
+#define ID(ven, dev, name) \
+ { vendor: PCI_VENDOR_ID_##ven, \
+ device: PCI_DEVICE_ID_##dev, \
+ subvendor: PCI_ANY_ID, \
+ subdevice: PCI_ANY_ID, \
+ class: 0, \
+ class_mask: 0, \
+ driver_data: (unsigned long) name }
+
+static struct pci_device_id hfcpci_ids[] __devinitdata = {
+ ID(CCD, CCD_2BD0, "CCD/Billion/Asuscom 2BD0"),
+ ID(CCD, CCD_B000, "Billion B000"),
+ ID(CCD, CCD_B006, "Billion B006"),
+ ID(CCD, CCD_B007, "Billion B007"),
+ ID(CCD, CCD_B008, "Billion B008"),
+ ID(CCD, CCD_B009, "Billion B009"),
+ ID(CCD, CCD_B00A, "Billion B00A"),
+ ID(CCD, CCD_B00B, "Billion B00B"),
+ ID(CCD, CCD_B00C, "Billion B00C"),
+ ID(CCD, CCD_B100, "Seyeon"),
+ ID(ABOCOM, ABOCOM_2BD1, "Abocom/Magitek"),
+ ID(ASUSTEK, ASUSTEK_0675, "Asuscom/Askey"),
+ ID(BERKOM, BERKOM_T_CONCEPT, "German Telekom T-Concept"),
+ ID(BERKOM, BERKOM_A1T, "German Telekom A1T"),
+ ID(ANIGMA, ANIGMA_MC145575, "Motorola MC145575"),
+ ID(ZOLTRIX, ZOLTRIX_2BD0, "Zoltrix 2BD0"),
+ ID(DIGI, DIGI_DF_M_IOM2_E, "Digi DataFire Micro V IOM2 (Europe)"),
+ ID(DIGI, DIGI_DF_M_E, "Digi DataFire Micro V (Europe)"),
+ ID(DIGI, DIGI_DF_M_IOM2_A, "Digi DataFire Micro V IOM2 (America)"),
+ ID(DIGI, DIGI_DF_M_A, "Digi DataFire Micro V (America)"),
+ { }
+};
+MODULE_DEVICE_TABLE(pci, hfcpci_ids);
+
+#undef ID
+
+static int protocol = 2; /* EURO-ISDN Default */
+MODULE_PARM(protocol, "i");
+
+// ----------------------------------------------------------------------
+//
+
+#define DBG_WARN 0x0001
+#define DBG_INFO 0x0002
+#define DBG_IRQ 0x0010
+#define DBG_L1M 0x0020
+#define DBG_PR 0x0040
+#define DBG_D_XMIT 0x0100
+#define DBG_D_RECV 0x0200
+#define DBG_B_XMIT 0x1000
+#define DBG_B_RECV 0x2000
+
+/* memory window base address offset (in config space) */
+
+#define HFCPCI_MWBA 0x80
+
+/* GCI/IOM bus monitor registers */
+
+#define HCFPCI_C_I 0x08
+#define HFCPCI_TRxR 0x0C
+#define HFCPCI_MON1_D 0x28
+#define HFCPCI_MON2_D 0x2C
+
+
+/* GCI/IOM bus timeslot registers */
+
+#define HFCPCI_B1_SSL 0x80
+#define HFCPCI_B2_SSL 0x84
+#define HFCPCI_AUX1_SSL 0x88
+#define HFCPCI_AUX2_SSL 0x8C
+#define HFCPCI_B1_RSL 0x90
+#define HFCPCI_B2_RSL 0x94
+#define HFCPCI_AUX1_RSL 0x98
+#define HFCPCI_AUX2_RSL 0x9C
+
+/* GCI/IOM bus data registers */
+
+#define HFCPCI_B1_D 0xA0
+#define HFCPCI_B2_D 0xA4
+#define HFCPCI_AUX1_D 0xA8
+#define HFCPCI_AUX2_D 0xAC
+
+/* GCI/IOM bus configuration registers */
+
+#define HFCPCI_MST_EMOD 0xB4
+#define HFCPCI_MST_MODE 0xB8
+#define HFCPCI_CONNECT 0xBC
+
+
+/* Interrupt and status registers */
+
+#define HFCPCI_FIFO_EN 0x44
+#define HFCPCI_TRM 0x48
+#define HFCPCI_B_MODE 0x4C
+#define HFCPCI_CHIP_ID 0x58
+#define HFCPCI_CIRM 0x60
+#define HFCPCI_CTMT 0x64
+#define HFCPCI_INT_M1 0x68
+#define HFCPCI_INT_M2 0x6C
+#define HFCPCI_INT_S1 0x78
+#define HFCPCI_INT_S2 0x7C
+#define HFCPCI_STATUS 0x70
+
+/* S/T section registers */
+
+#define HFCPCI_STATES 0xC0
+#define HFCPCI_SCTRL 0xC4
+#define HFCPCI_SCTRL_E 0xC8
+#define HFCPCI_SCTRL_R 0xCC
+#define HFCPCI_SQ 0xD0
+#define HFCPCI_CLKDEL 0xDC
+#define HFCPCI_B1_REC 0xF0
+#define HFCPCI_B1_SEND 0xF0
+#define HFCPCI_B2_REC 0xF4
+#define HFCPCI_B2_SEND 0xF4
+#define HFCPCI_D_REC 0xF8
+#define HFCPCI_D_SEND 0xF8
+#define HFCPCI_E_REC 0xFC
+
+
+/* bits in status register (READ) */
+#define HFCPCI_PCI_PROC 0x02
+#define HFCPCI_NBUSY 0x04
+#define HFCPCI_TIMER_ELAP 0x10
+#define HFCPCI_STATINT 0x20
+#define HFCPCI_FRAMEINT 0x40
+#define HFCPCI_ANYINT 0x80
+
+/* bits in CTMT (Write) */
+#define HFCPCI_CLTIMER 0x80
+#define HFCPCI_TIM3_125 0x04
+#define HFCPCI_TIM25 0x10
+#define HFCPCI_TIM50 0x14
+#define HFCPCI_TIM400 0x18
+#define HFCPCI_TIM800 0x1C
+#define HFCPCI_AUTO_TIMER 0x20
+#define HFCPCI_TRANSB2 0x02
+#define HFCPCI_TRANSB1 0x01
+
+/* bits in CIRM (Write) */
+#define HFCPCI_AUX_MSK 0x07
+#define HFCPCI_RESET 0x08
+#define HFCPCI_B1_REV 0x40
+#define HFCPCI_B2_REV 0x80
+
+/* bits in INT_M1 and INT_S1 */
+#define HFCPCI_INTS_B1TRANS 0x01
+#define HFCPCI_INTS_B2TRANS 0x02
+#define HFCPCI_INTS_DTRANS 0x04
+#define HFCPCI_INTS_B1REC 0x08
+#define HFCPCI_INTS_B2REC 0x10
+#define HFCPCI_INTS_DREC 0x20
+#define HFCPCI_INTS_L1STATE 0x40
+#define HFCPCI_INTS_TIMER 0x80
+
+/* bits in INT_M2 */
+#define HFCPCI_PROC_TRANS 0x01
+#define HFCPCI_GCI_I_CHG 0x02
+#define HFCPCI_GCI_MON_REC 0x04
+#define HFCPCI_IRQ_ENABLE 0x08
+#define HFCPCI_PMESEL 0x80
+
+/* bits in STATES */
+#define HFCPCI_STATE_MSK 0x0F
+#define HFCPCI_LOAD_STATE 0x10
+#define HFCPCI_ACTIVATE 0x20
+#define HFCPCI_DO_ACTION 0x40
+#define HFCPCI_NT_G2_G3 0x80
+
+/* bits in HFCD_MST_MODE */
+#define HFCPCI_MASTER 0x01
+#define HFCPCI_SLAVE 0x00
+/* remaining bits are for codecs control */
+
+/* bits in HFCD_SCTRL */
+#define SCTRL_B1_ENA 0x01
+#define SCTRL_B2_ENA 0x02
+#define SCTRL_MODE_TE 0x00
+#define SCTRL_MODE_NT 0x04
+#define SCTRL_LOW_PRIO 0x08
+#define SCTRL_SQ_ENA 0x10
+#define SCTRL_TEST 0x20
+#define SCTRL_NONE_CAP 0x40
+#define SCTRL_PWR_DOWN 0x80
+
+/* bits in SCTRL_E */
+#define HFCPCI_AUTO_AWAKE 0x01
+#define HFCPCI_DBIT_1 0x04
+#define HFCPCI_IGNORE_COL 0x08
+#define HFCPCI_CHG_B1_B2 0x80
+
+/* bits in FIFO_EN register */
+#define HFCPCI_FIFOEN_B1 0x03
+#define HFCPCI_FIFOEN_B2 0x0C
+#define HFCPCI_FIFOEN_DTX 0x10
+#define HFCPCI_FIFOEN_DRX 0x20
+#define HFCPCI_FIFOEN_B1TX 0x01
+#define HFCPCI_FIFOEN_B1RX 0x02
+#define HFCPCI_FIFOEN_B2TX 0x04
+#define HFCPCI_FIFOEN_B2RX 0x08
+
+/*
+ * thresholds for transparent B-channel mode
+ * change mask and threshold simultaneously
+ */
+#define HFCPCI_BTRANS_THRESHOLD 128
+#define HFCPCI_BTRANS_THRESMASK 0x00
+
+#define CLKDEL_TE 0x0e /* CLKDEL in TE mode */
+#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */
+
+#define MAX_D_FRAMES 0x10
+#define MAX_B_FRAMES 0x20
+#define B_FIFO_START 0x0200
+#define B_FIFO_END 0x2000
+#define B_FIFO_SIZE (B_FIFO_END - B_FIFO_START)
+#define D_FIFO_START 0x0000
+#define D_FIFO_END 0x0200
+#define D_FIFO_SIZE (D_FIFO_END - D_FIFO_START)
+
+// ----------------------------------------------------------------------
+// push messages to the upper layers
+
+static inline void D_L1L2(struct hfcpci_adapter *adapter, int pr, void *arg)
+{
+ struct hisax_if *ifc = (struct hisax_if *) &adapter->d_if;
+
+ DBG(DBG_PR, "pr %#x", pr);
+ ifc->l1l2(ifc, pr, arg);
+}
+
+static inline void B_L1L2(struct hfcpci_bcs *bcs, int pr, void *arg)
+{
+ struct hisax_if *ifc = (struct hisax_if *) &bcs->b_if;
+
+ DBG(DBG_PR, "pr %#x", pr);
+ ifc->l1l2(ifc, pr, arg);
+}
+
+// ----------------------------------------------------------------------
+// MMIO
+
+static inline void
+hfcpci_writeb(struct hfcpci_adapter *adapter, u8 b, unsigned char offset)
+{
+ writeb(b, adapter->mmio + offset);
+}
+
+static inline u8
+hfcpci_readb(struct hfcpci_adapter *adapter, unsigned char offset)
+{
+ return readb(adapter->mmio + offset);
+}
+
+// ----------------------------------------------------------------------
+// magic to define the various F/Z counter accesses
+
+#define DECL_B_F(r, f) \
+static inline u8 \
+get_b_##r##_##f (struct hfcpci_bcs *bcs) \
+{ \
+ u16 off = bcs->channel ? OFF_B2_##r##_##f : OFF_B1_##r##_##f; \
+ \
+ return *(bcs->adapter->fifo + off); \
+} \
+ \
+static inline void \
+set_b_##r##_##f (struct hfcpci_bcs *bcs, u8 f) \
+{ \
+ u16 off = bcs->channel ? OFF_B2_##r##_##f : OFF_B1_##r##_##f; \
+ \
+ *(bcs->adapter->fifo + off) = f; \
+}
+
+#define OFF_B1_rx_f1 0x6080
+#define OFF_B2_rx_f1 0x6180
+#define OFF_B1_rx_f2 0x6081
+#define OFF_B2_rx_f2 0x6181
+
+#define OFF_B1_tx_f1 0x2080
+#define OFF_B2_tx_f1 0x2180
+#define OFF_B1_tx_f2 0x2081
+#define OFF_B2_tx_f2 0x2181
+
+DECL_B_F(rx, f1)
+DECL_B_F(rx, f2)
+DECL_B_F(tx, f1)
+DECL_B_F(tx, f2)
+
+#undef DECL_B_F
+
+#define DECL_B_Z(r, z) \
+static inline u16 \
+get_b_##r##_##z (struct hfcpci_bcs *bcs, u8 f) \
+{ \
+ u16 off = bcs->channel ? OFF_B2_##r##_##z : OFF_B1_##r##_##z; \
+ \
+ return le16_to_cpu(*((u16 *) (bcs->adapter->fifo + off + f * 4))); \
+} \
+ \
+static inline void \
+set_b_##r##_##z(struct hfcpci_bcs *bcs, u8 f, u16 z) \
+{ \
+ u16 off = bcs->channel ? OFF_B2_##r##_##z : OFF_B1_##r##_##z; \
+ \
+ *((u16 *) (bcs->adapter->fifo + off + f * 4)) = cpu_to_le16(z); \
+}
+
+#define OFF_B1_rx_z1 0x6000
+#define OFF_B2_rx_z1 0x6100
+#define OFF_B1_rx_z2 0x6002
+#define OFF_B2_rx_z2 0x6102
+
+#define OFF_B1_tx_z1 0x2000
+#define OFF_B2_tx_z1 0x2100
+#define OFF_B1_tx_z2 0x2002
+#define OFF_B2_tx_z2 0x2102
+
+DECL_B_Z(rx, z1)
+DECL_B_Z(rx, z2)
+DECL_B_Z(tx, z1)
+DECL_B_Z(tx, z2)
+
+#undef DECL_B_Z
+
+#define DECL_D_F(r, f) \
+static inline u8 \
+get_d_##r##_##f (struct hfcpci_adapter *adapter) \
+{ \
+ u16 off = OFF_D_##r##_##f; \
+ \
+ return *(adapter->fifo + off) & 0xf; \
+} \
+ \
+static inline void \
+set_d_##r##_##f (struct hfcpci_adapter *adapter, u8 f) \
+{ \
+ u16 off = OFF_D_##r##_##f; \
+ \
+ *(adapter->fifo + off) = f | 0x10; \
+}
+
+#define OFF_D_rx_f1 0x60a0
+#define OFF_D_rx_f2 0x60a1
+
+#define OFF_D_tx_f1 0x20a0
+#define OFF_D_tx_f2 0x20a1
+
+DECL_D_F(rx, f1)
+DECL_D_F(rx, f2)
+DECL_D_F(tx, f1)
+DECL_D_F(tx, f2)
+
+#undef DECL_D_F
+
+#define DECL_D_Z(r, z) \
+static inline u16 \
+get_d_##r##_##z (struct hfcpci_adapter *adapter, u8 f) \
+{ \
+ u16 off = OFF_D_##r##_##z; \
+ \
+ return le16_to_cpu(*((u16 *) (adapter->fifo + off + (f | 0x10) * 4)));\
+} \
+ \
+static inline void \
+set_d_##r##_##z(struct hfcpci_adapter *adapter, u8 f, u16 z) \
+{ \
+ u16 off = OFF_D_##r##_##z; \
+ \
+ *((u16 *) (adapter->fifo + off + (f | 0x10) * 4)) = cpu_to_le16(z); \
+}
+
+#define OFF_D_rx_z1 0x6080
+#define OFF_D_rx_z2 0x6082
+
+#define OFF_D_tx_z1 0x2080
+#define OFF_D_tx_z2 0x2082
+
+DECL_D_Z(rx, z1)
+DECL_D_Z(rx, z2)
+DECL_D_Z(tx, z1)
+DECL_D_Z(tx, z2)
+
+#undef DECL_B_Z
+
+// ----------------------------------------------------------------------
+// fill b / d fifos
+
+static inline void
+hfcpci_fill_d_fifo(struct hfcpci_adapter *adapter)
+{
+ u8 f1, f2;
+ u16 z1, z2;
+ int cnt, fcnt;
+ char *fifo_adr = adapter->fifo;
+ struct sk_buff *tx_skb = adapter->tx_skb;
+
+ f1 = get_d_tx_f1(adapter);
+ f2 = get_d_tx_f2(adapter);
+ DBG(DBG_D_XMIT, "f1 %#x f2 %#x", f1, f2);
+
+ fcnt = f1 - f2;
+ if (fcnt < 0)
+ fcnt += MAX_D_FRAMES;
+
+ if (fcnt) {
+ printk("BUG\n");
+ return;
+ }
+
+ z1 = get_d_tx_z1(adapter, f1);
+ z2 = get_d_tx_z2(adapter, f1); //XXX
+ DBG(DBG_D_XMIT, "z1 %#x z2 %#x", z1, z2);
+
+ cnt = z2 - z1;
+ if (cnt <= 0)
+ cnt += D_FIFO_SIZE;
+
+ if (tx_skb->len > cnt) {
+ printk("BUG\n");
+ return;
+ }
+
+ cnt = tx_skb->len;
+ if (z1 + cnt <= D_FIFO_END) {
+ memcpy(fifo_adr + z1, tx_skb->data, cnt);
+ } else {
+ memcpy(fifo_adr + z1, tx_skb->data, D_FIFO_END - z1);
+ memcpy(fifo_adr + D_FIFO_START,
+ tx_skb->data + (D_FIFO_END - z1),
+ cnt - (D_FIFO_END - z1));
+ }
+ z1 += cnt;
+ if (z1 >= D_FIFO_END)
+ z1 -= D_FIFO_SIZE;
+
+ f1 = (f1 + 1) & (MAX_D_FRAMES - 1);
+ mb();
+ set_d_tx_z1(adapter, f1, z1);
+ mb();
+ set_d_tx_f1(adapter, f1);
+}
+
+static inline void
+hfcpci_fill_b_fifo_hdlc(struct hfcpci_bcs *bcs)
+{
+ u8 f1, f2;
+ u16 z1, z2;
+ int cnt, fcnt;
+ char *fifo_adr = bcs->adapter->fifo + (bcs->channel ? 0x2000 : 0x0000);
+ struct sk_buff *tx_skb = bcs->tx_skb;
+
+ f1 = get_b_tx_f1(bcs);
+ f2 = get_b_tx_f2(bcs);
+ DBG(DBG_B_XMIT, "f1 %#x f2 %#x", f1, f2);
+
+ fcnt = f1 - f2;
+ if (fcnt < 0)
+ fcnt += MAX_B_FRAMES;
+
+ if (fcnt) {
+ printk("BUG\n");
+ return;
+ }
+
+ z1 = get_b_tx_z1(bcs, f1);
+ z2 = get_b_tx_z2(bcs, f1); //XXX
+ DBG(DBG_B_XMIT, "z1 %#x z2 %#x", z1, z2);
+
+ cnt = z2 - z1;
+ if (cnt <= 0)
+ cnt += B_FIFO_SIZE;
+
+ if (tx_skb->len > cnt) {
+ printk("BUG\n");
+ return;
+ }
+
+ cnt = tx_skb->len;
+ if (z1 + cnt <= B_FIFO_END) {
+ memcpy(fifo_adr + z1, tx_skb->data, cnt);
+ } else {
+ memcpy(fifo_adr + z1, tx_skb->data, B_FIFO_END - z1);
+ memcpy(fifo_adr + B_FIFO_START,
+ tx_skb->data + (B_FIFO_END - z1),
+ cnt - (B_FIFO_END - z1));
+ }
+ z1 += cnt;
+ if (z1 >= B_FIFO_END)
+ z1 -= B_FIFO_SIZE;
+
+ f1 = (f1 + 1) & (MAX_B_FRAMES - 1);
+ mb();
+ set_b_tx_z1(bcs, f1, z1);
+ mb();
+ set_b_tx_f1(bcs, f1);
+}
+
+static inline void
+hfcpci_fill_b_fifo_trans(struct hfcpci_bcs *bcs)
+{
+ int cnt;
+ char *fifo_adr = bcs->adapter->fifo + (bcs->channel ? 0x2000 : 0x0000);
+ struct sk_buff *tx_skb = bcs->tx_skb;
+ u8 f1, f2;
+ u16 z1, z2;
+
+ f1 = get_b_tx_f1(bcs);
+ f2 = get_b_tx_f2(bcs);
+
+ if (f1 != f2)
+ BUG();
+
+ z1 = get_b_tx_z1(bcs, f1);
+ z2 = get_b_tx_z2(bcs, f1);
+
+ cnt = z2 - z1;
+ if (cnt <= 0)
+ cnt += B_FIFO_SIZE;
+
+ if (tx_skb->len > cnt)
+ BUG();
+
+ if (z1 + cnt <= B_FIFO_END) {
+ memcpy(fifo_adr + z1, tx_skb->data, cnt);
+ } else {
+ memcpy(fifo_adr + z1, tx_skb->data, B_FIFO_END - z1);
+ memcpy(fifo_adr + B_FIFO_START,
+ tx_skb->data + (B_FIFO_END - z1),
+ cnt - (B_FIFO_END - z1));
+ }
+ z1 += cnt;
+ if (z1 >= B_FIFO_END)
+ z1 -= B_FIFO_SIZE;
+
+ mb();
+ set_b_tx_z1(bcs, f1, z1);
+}
+
+static inline void
+hfcpci_fill_b_fifo(struct hfcpci_bcs *bcs)
+{
+ if (!bcs->tx_skb) {
+ DBG(DBG_WARN, "?");
+ return;
+ }
+
+ switch (bcs->mode) {
+ case L1_MODE_TRANS:
+ hfcpci_fill_b_fifo_trans(bcs);
+ break;
+ case L1_MODE_HDLC:
+ hfcpci_fill_b_fifo_hdlc(bcs);
+ break;
+ default:
+ DBG(DBG_WARN, "?");
+ }
+}
+
+static void hfcpci_clear_b_rx_fifo(struct hfcpci_bcs *bcs);
+static void hfcpci_clear_b_tx_fifo(struct hfcpci_bcs *bcs);
+
+static void
+hfcpci_b_mode(struct hfcpci_bcs *bcs, int mode)
+{
+ struct hfcpci_adapter *adapter = bcs->adapter;
+
+ DBG(DBG_B_XMIT, "B%d mode %d --> %d",
+ bcs->channel + 1, bcs->mode, mode);
+
+ if (bcs->mode == mode)
+ return;
+
+ switch (mode) {
+ case L1_MODE_NULL:
+ if (bcs->channel == 0) {
+ adapter->sctrl &= ~SCTRL_B1_ENA;
+ adapter->sctrl_r &= ~SCTRL_B1_ENA;
+ adapter->fifo_en &= ~HFCPCI_FIFOEN_B1;
+ adapter->int_m1 &= ~(HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
+ } else {
+ adapter->sctrl &= ~SCTRL_B2_ENA;
+ adapter->sctrl_r &= ~SCTRL_B2_ENA;
+ adapter->fifo_en &= ~HFCPCI_FIFOEN_B2;
+ adapter->int_m1 &= ~(HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
+ }
+ break;
+ case L1_MODE_TRANS:
+ case L1_MODE_HDLC:
+ hfcpci_clear_b_rx_fifo(bcs);
+ hfcpci_clear_b_tx_fifo(bcs);
+ if (bcs->channel == 0) {
+ adapter->sctrl |= SCTRL_B1_ENA;
+ adapter->sctrl_r |= SCTRL_B1_ENA;
+ adapter->fifo_en |= HFCPCI_FIFOEN_B1;
+ adapter->int_m1 |= (HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
+
+ if (mode == L1_MODE_TRANS)
+ adapter->ctmt |= 1;
+ else
+ adapter->ctmt &= ~1;
+
+ } else {
+ adapter->sctrl |= SCTRL_B2_ENA;
+ adapter->sctrl_r |= SCTRL_B2_ENA;
+ adapter->fifo_en |= HFCPCI_FIFOEN_B2;
+ adapter->int_m1 |= (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
+
+ if (mode == L1_MODE_TRANS)
+ adapter->ctmt |= 2;
+ else
+ adapter->ctmt &= ~2;
+
+ }
+ break;
+ }
+ hfcpci_writeb(adapter, adapter->int_m1, HFCPCI_INT_M1);
+ hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN);
+ hfcpci_writeb(adapter, adapter->sctrl, HFCPCI_SCTRL);
+ hfcpci_writeb(adapter, adapter->sctrl_r, HFCPCI_SCTRL_R);
+ hfcpci_writeb(adapter, adapter->ctmt, HFCPCI_CTMT);
+ hfcpci_writeb(adapter, adapter->conn, HFCPCI_CONNECT);
+
+ bcs->mode = mode;
+}
+
+// ----------------------------------------------------------------------
+// Layer 1 state machine
+
+static struct Fsm l1fsm;
+
+enum {
+ ST_L1_F0,
+ ST_L1_F2,
+ ST_L1_F3,
+ ST_L1_F4,
+ ST_L1_F5,
+ ST_L1_F6,
+ ST_L1_F7,
+ ST_L1_F8,
+};
+
+#define L1_STATE_COUNT (ST_L1_F8+1)
+
+static char *strL1State[] =
+{
+ "ST_L1_F0",
+ "ST_L1_F2",
+ "ST_L1_F3",
+ "ST_L1_F4",
+ "ST_L1_F5",
+ "ST_L1_F6",
+ "ST_L1_F7",
+ "ST_L1_F8",
+};
+
+enum {
+ EV_PH_F0,
+ EV_PH_1,
+ EV_PH_F2,
+ EV_PH_F3,
+ EV_PH_F4,
+ EV_PH_F5,
+ EV_PH_F6,
+ EV_PH_F7,
+ EV_PH_F8,
+ EV_PH_ACTIVATE_REQ,
+ EV_PH_DEACTIVATE_REQ,
+ EV_TIMER3,
+};
+
+#define L1_EVENT_COUNT (EV_TIMER3 + 1)
+
+static char *strL1Event[] =
+{
+ "EV_PH_F0",
+ "EV_PH_1",
+ "EV_PH_F2",
+ "EV_PH_F3",
+ "EV_PH_F4",
+ "EV_PH_F5",
+ "EV_PH_F6",
+ "EV_PH_F7",
+ "EV_PH_F8",
+ "EV_PH_ACTIVATE_REQ",
+ "EV_PH_DEACTIVATE_REQ",
+ "EV_TIMER3",
+};
+
+static void l1_ignore(struct FsmInst *fi, int event, void *arg)
+{
+}
+
+static void l1_go_f3(struct FsmInst *fi, int event, void *arg)
+{
+ FsmChangeState(fi, ST_L1_F3);
+}
+
+static void l1_go_f3_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+ struct hfcpci_adapter *adapter = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_F3);
+ D_L1L2(adapter, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+static void l1_go_f4(struct FsmInst *fi, int event, void *arg)
+{
+ FsmChangeState(fi, ST_L1_F3);
+}
+
+static void l1_go_f5(struct FsmInst *fi, int event, void *arg)
+{
+ FsmChangeState(fi, ST_L1_F3);
+}
+
+static void l1_go_f6(struct FsmInst *fi, int event, void *arg)
+{
+ FsmChangeState(fi, ST_L1_F6);
+}
+
+static void l1_go_f6_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+ struct hfcpci_adapter *adapter = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_F6);
+ D_L1L2(adapter, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+static void l1_go_f7(struct FsmInst *fi, int event, void *arg)
+{
+ FsmChangeState(fi, ST_L1_F7);
+}
+
+static void l1_go_f7_act_ind(struct FsmInst *fi, int event, void *arg)
+{
+ struct hfcpci_adapter *adapter = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_F7);
+ D_L1L2(adapter, PH_ACTIVATE | INDICATION, NULL);
+}
+
+static void l1_go_f8(struct FsmInst *fi, int event, void *arg)
+{
+ FsmChangeState(fi, ST_L1_F8);
+}
+
+static void l1_go_f8_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+ struct hfcpci_adapter *adapter = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_F8);
+ D_L1L2(adapter, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+static void l1_act_req(struct FsmInst *fi, int event, void *arg)
+{
+ struct hfcpci_adapter *adapter = fi->userdata;
+
+ hfcpci_writeb(adapter, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION, HFCPCI_STATES);
+}
+
+static struct FsmNode L1FnList[] __initdata =
+{
+ {ST_L1_F2, EV_PH_F3, l1_go_f3},
+ {ST_L1_F2, EV_PH_F6, l1_go_f6},
+ {ST_L1_F2, EV_PH_F7, l1_go_f7_act_ind},
+
+ {ST_L1_F3, EV_PH_F3, l1_ignore},
+ {ST_L1_F3, EV_PH_F4, l1_go_f4},
+ {ST_L1_F3, EV_PH_F5, l1_go_f5},
+ {ST_L1_F3, EV_PH_F6, l1_go_f6},
+ {ST_L1_F3, EV_PH_F7, l1_go_f7_act_ind},
+ {ST_L1_F3, EV_PH_ACTIVATE_REQ, l1_act_req},
+
+ {ST_L1_F4, EV_PH_F7, l1_ignore},
+ {ST_L1_F4, EV_PH_F3, l1_go_f3},
+ {ST_L1_F4, EV_PH_F5, l1_go_f5},
+ {ST_L1_F4, EV_PH_F6, l1_go_f6},
+ {ST_L1_F4, EV_PH_F7, l1_go_f7},
+
+ {ST_L1_F5, EV_PH_F7, l1_ignore},
+ {ST_L1_F5, EV_PH_F3, l1_go_f3},
+ {ST_L1_F5, EV_PH_F6, l1_go_f6},
+ {ST_L1_F5, EV_PH_F7, l1_go_f7},
+
+ {ST_L1_F6, EV_PH_F7, l1_ignore},
+ {ST_L1_F6, EV_PH_F3, l1_go_f3},
+ {ST_L1_F6, EV_PH_F7, l1_go_f7_act_ind},
+ {ST_L1_F6, EV_PH_F8, l1_go_f8},
+
+ {ST_L1_F7, EV_PH_F7, l1_ignore},
+ {ST_L1_F7, EV_PH_F3, l1_go_f3_deact_ind},
+ {ST_L1_F7, EV_PH_F6, l1_go_f6_deact_ind},
+ {ST_L1_F7, EV_PH_F8, l1_go_f8_deact_ind},
+
+ {ST_L1_F8, EV_PH_F7, l1_ignore},
+ {ST_L1_F8, EV_PH_F3, l1_go_f3},
+ {ST_L1_F8, EV_PH_F6, l1_go_f6},
+ {ST_L1_F8, EV_PH_F7, l1_go_f7_act_ind},
+
+};
+
+static void l1m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+ va_list args;
+ char buf[256];
+
+ va_start(args, fmt);
+ vsprintf(buf, fmt, args);
+ DBG(DBG_L1M, "%s", buf);
+ va_end(args);
+}
+
+// ----------------------------------------------------------------------
+// clear FIFOs
+
+static void
+hfcpci_clear_d_rx_fifo(struct hfcpci_adapter *adapter)
+{
+ u8 fifo_state;
+
+ DBG(DBG_D_RECV, "");
+
+ fifo_state = adapter->fifo_en & HFCPCI_FIFOEN_DRX;
+
+ if (fifo_state) { // enabled
+ // XXX locking
+ adapter->fifo_en &= ~fifo_state;
+ hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN);
+ }
+
+ adapter->last_fcnt = 0;
+
+ set_d_rx_z1(adapter, MAX_D_FRAMES - 1, D_FIFO_END - 1);
+ set_d_rx_z2(adapter, MAX_D_FRAMES - 1, D_FIFO_END - 1);
+ mb();
+ set_d_rx_f1(adapter, MAX_D_FRAMES - 1);
+ set_d_rx_f2(adapter, MAX_D_FRAMES - 1);
+ mb();
+
+ if (fifo_state) {
+ adapter->fifo_en |= fifo_state;
+ hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN);
+ }
+}
+
+static void
+hfcpci_clear_b_rx_fifo(struct hfcpci_bcs *bcs)
+{
+ struct hfcpci_adapter *adapter = bcs->adapter;
+ int nr = bcs->channel;
+ u8 fifo_state;
+
+ DBG(DBG_B_RECV, "");
+
+ fifo_state = adapter->fifo_en &
+ (nr ? HFCPCI_FIFOEN_B2RX : HFCPCI_FIFOEN_B1RX);
+
+ if (fifo_state) { // enabled
+ adapter->fifo_en &= ~fifo_state;
+ hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN);
+ }
+
+ bcs->last_fcnt = 0;
+
+ set_b_rx_z1(bcs, MAX_B_FRAMES - 1, B_FIFO_END - 1);
+ set_b_rx_z2(bcs, MAX_B_FRAMES - 1, B_FIFO_END - 1);
+ mb();
+ set_b_rx_f1(bcs, MAX_B_FRAMES - 1);
+ set_b_rx_f2(bcs, MAX_B_FRAMES - 1);
+ mb();
+
+ if (fifo_state) {
+ adapter->fifo_en |= fifo_state;
+ hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN);
+ }
+}
+
+// XXX clear d_tx_fifo?
+
+static void
+hfcpci_clear_b_tx_fifo(struct hfcpci_bcs *bcs)
+{
+ struct hfcpci_adapter *adapter = bcs->adapter;
+ int nr = bcs->channel;
+ u8 fifo_state;
+
+ fifo_state = adapter->fifo_en &
+ (nr ? HFCPCI_FIFOEN_B2TX : HFCPCI_FIFOEN_B1TX);
+
+ if (fifo_state) { // enabled
+ adapter->fifo_en &= ~fifo_state;
+ hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN);
+ }
+
+ bcs->last_fcnt = 0;
+
+ set_b_rx_z1(bcs, MAX_B_FRAMES - 1, B_FIFO_END - 1);
+ set_b_rx_z2(bcs, MAX_B_FRAMES - 1, B_FIFO_END - 1);
+ mb();
+ set_b_rx_f1(bcs, MAX_B_FRAMES - 1);
+ set_b_rx_f2(bcs, MAX_B_FRAMES - 1);
+ mb();
+
+ if (fifo_state) {
+ adapter->fifo_en |= fifo_state;
+ hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN);
+ }
+}
+
+// ----------------------------------------------------------------------
+// receive messages from upper layers
+
+static void
+hfcpci_d_l2l1(struct hisax_if *ifc, int pr, void *arg)
+{
+ struct hfcpci_adapter *adapter = ifc->priv;
+ struct sk_buff *skb = arg;
+
+ DBG(DBG_PR, "pr %#x", pr);
+
+ switch (pr) {
+ case PH_ACTIVATE | REQUEST:
+ FsmEvent(&adapter->l1m, EV_PH_ACTIVATE_REQ, NULL);
+ break;
+ case PH_DEACTIVATE | REQUEST:
+ FsmEvent(&adapter->l1m, EV_PH_DEACTIVATE_REQ, NULL);
+ break;
+ case PH_DATA | REQUEST:
+ DBG(DBG_PR, "PH_DATA REQUEST len %d", skb->len);
+ DBG_SKB(DBG_D_XMIT, skb);
+ if (adapter->l1m.state != ST_L1_F7) {
+ DBG(DBG_WARN, "L1 wrong state %d", adapter->l1m.state);
+ break;
+ }
+ if (adapter->tx_skb)
+ BUG();
+
+ adapter->tx_skb = skb;
+ hfcpci_fill_d_fifo(adapter);
+ break;
+ }
+}
+
+static void
+hfcpci_b_l2l1(struct hisax_if *ifc, int pr, void *arg)
+{
+ struct hfcpci_bcs *bcs = ifc->priv;
+ struct sk_buff *skb = arg;
+ int mode;
+
+ DBG(DBG_PR, "pr %#x", pr);
+
+ switch (pr) {
+ case PH_DATA | REQUEST:
+ if (bcs->tx_skb)
+ BUG();
+
+ bcs->tx_skb = skb;
+ DBG_SKB(DBG_B_XMIT, skb);
+ hfcpci_fill_b_fifo(bcs);
+ break;
+ case PH_ACTIVATE | REQUEST:
+ mode = (int) arg;
+ DBG(DBG_PR,"B%d,PH_ACTIVATE_REQUEST %d", bcs->channel + 1, mode);
+ hfcpci_b_mode(bcs, mode);
+ B_L1L2(bcs, PH_ACTIVATE | INDICATION, NULL);
+ break;
+ case PH_DEACTIVATE | REQUEST:
+ DBG(DBG_PR,"B%d,PH_DEACTIVATE_REQUEST", bcs->channel + 1);
+ hfcpci_b_mode(bcs, L1_MODE_NULL);
+ B_L1L2(bcs, PH_DEACTIVATE | INDICATION, NULL);
+ break;
+ }
+}
+
+// ----------------------------------------------------------------------
+// receive IRQ
+
+static inline void
+hfcpci_d_recv_irq(struct hfcpci_adapter *adapter)
+{
+ struct sk_buff *skb;
+ char *fifo_adr = adapter->fifo + 0x4000;
+ char *p;
+ int cnt, fcnt;
+ int loop = 5;
+ u8 f1, f2;
+ u16 z1, z2;
+
+ while (loop-- > 0) {
+ f1 = get_d_rx_f1(adapter);
+ f2 = get_d_rx_f2(adapter);
+ DBG(DBG_D_RECV, "f1 %#x f2 %#x", f1, f2);
+
+ fcnt = f1 - f2;
+ if (fcnt < 0)
+ fcnt += 16;
+
+ if (!fcnt)
+ return;
+
+ if (fcnt < adapter->last_fcnt)
+ /* overrun */
+ hfcpci_clear_d_rx_fifo(adapter);
+ // XXX init last_fcnt
+
+ z1 = get_d_rx_z1(adapter, f2);
+ z2 = get_d_rx_z2(adapter, f2);
+ DBG(DBG_D_RECV, "z1 %#x z2 %#x", z1, z2);
+
+ cnt = z1 - z2;
+ if (cnt < 0)
+ cnt += D_FIFO_SIZE;
+ cnt++;
+
+ if (cnt < 4) {
+ DBG(DBG_WARN, "frame too short");
+ goto next;
+ }
+ if (fifo_adr[z1] != 0) {
+ DBG(DBG_WARN, "CRC error");
+ goto next;
+ }
+ cnt -= 3;
+ skb = dev_alloc_skb(cnt);
+ if (!skb) {
+ DBG(DBG_WARN, "no mem");
+ goto next;
+ }
+ p = skb_put(skb, cnt);
+ if (z2 + cnt <= D_FIFO_END) {
+ memcpy(p, fifo_adr + z2, cnt);
+ } else {
+ memcpy(p, fifo_adr + z2, D_FIFO_END - z2);
+ memcpy(p + (D_FIFO_END - z2), fifo_adr + D_FIFO_START,
+ cnt - (D_FIFO_END - z2));
+ }
+
+ DBG_SKB(DBG_D_RECV, skb);
+ D_L1L2(adapter, PH_DATA | INDICATION, skb);
+
+ next:
+ if (++z1 >= D_FIFO_END)
+ z1 -= D_FIFO_START;
+
+ f2 = (f2 + 1) & (MAX_D_FRAMES - 1);
+ mb();
+ set_d_rx_z2(adapter, f2, z1);
+ mb();
+ set_d_rx_f2(adapter, f2);
+
+ adapter->last_fcnt = fcnt - 1;
+ }
+}
+
+static inline void
+hfcpci_b_recv_hdlc_irq(struct hfcpci_adapter *adapter, int nr)
+{
+ struct hfcpci_bcs *bcs = &adapter->bcs[nr];
+ struct sk_buff *skb;
+ char *fifo_adr = adapter->fifo + (nr ? 0x6000 : 0x4000);
+ char *p;
+ int cnt, fcnt;
+ int loop = 5;
+ u8 f1, f2;
+ u16 z1, z2;
+
+ while (loop-- > 0) {
+ f1 = get_b_rx_f1(bcs);
+ f2 = get_b_rx_f2(bcs);
+ DBG(DBG_B_RECV, "f1 %d f2 %d", f1, f2);
+
+ fcnt = f1 - f2;
+ if (fcnt < 0)
+ fcnt += 32;
+
+ if (!fcnt)
+ return;
+
+ if (fcnt < bcs->last_fcnt)
+ /* overrun */
+ hfcpci_clear_b_rx_fifo(bcs);
+ // XXX init last_fcnt
+
+ z1 = get_b_rx_z1(bcs, f2);
+ z2 = get_b_rx_z2(bcs, f2);
+ DBG(DBG_B_RECV, "z1 %d z2 %d", z1, z2);
+
+ cnt = z1 - z2;
+ if (cnt < 0)
+ cnt += B_FIFO_SIZE;
+ cnt++;
+
+ if (cnt < 4) {
+ DBG(DBG_WARN, "frame too short");
+ goto next;
+ }
+ if (fifo_adr[z1] != 0) {
+ DBG(DBG_WARN, "CRC error");
+ goto next;
+ }
+ cnt -= 3;
+ skb = dev_alloc_skb(cnt);
+ if (!skb) {
+ DBG(DBG_WARN, "no mem");
+ goto next;
+ }
+ p = skb_put(skb, cnt);
+ if (z2 + cnt <= B_FIFO_END) {
+ memcpy(p, fifo_adr + z2, cnt);
+ } else {
+ memcpy(p, fifo_adr + z2, B_FIFO_END - z2);
+ memcpy(p + (B_FIFO_END - z2), fifo_adr + B_FIFO_START,
+ cnt - (B_FIFO_END - z2));
+ }
+
+ DBG_SKB(DBG_B_RECV, skb);
+ B_L1L2(bcs, PH_DATA | INDICATION, skb);
+
+ next:
+ if (++z1 >= B_FIFO_END)
+ z1 -= B_FIFO_SIZE;
+
+ f2 = (f2 + 1) & (MAX_B_FRAMES - 1);
+ mb();
+ set_b_rx_z2(bcs, f2, z1);
+ mb();
+ set_b_rx_f2(bcs, f2);
+
+ bcs->last_fcnt = fcnt - 1;
+ }
+}
+
+static inline void
+hfcpci_b_recv_trans_irq(struct hfcpci_adapter *adapter, int nr)
+{
+ struct hfcpci_bcs *bcs = &adapter->bcs[nr];
+ struct sk_buff *skb;
+ char *fifo_adr = adapter->fifo + (nr ? 0x6000 : 0x4000);
+ char *p;
+ int cnt;
+ int loop = 5;
+ u8 f1, f2;
+ u16 z1, z2;
+
+ f1 = get_b_rx_f1(bcs);
+ f2 = get_b_rx_f2(bcs);
+
+ if (f1 != f2)
+ BUG();
+
+ while (loop-- > 0) {
+ z1 = get_b_rx_z1(bcs, f2);
+ z2 = get_b_rx_z2(bcs, f2);
+
+ cnt = z1 - z2;
+ if (!cnt)
+ /* no data available */
+ return;
+
+ if (cnt < 0)
+ cnt += B_FIFO_SIZE;
+
+ if (cnt > HFCPCI_BTRANS_THRESHOLD)
+ cnt = HFCPCI_BTRANS_THRESHOLD;
+
+ skb = dev_alloc_skb(cnt);
+ if (!skb) {
+ DBG(DBG_WARN, "no mem");
+ goto next;
+ }
+
+ p = skb_put(skb, cnt);
+ if (z2 + cnt <= 0x2000) {
+ memcpy(p, fifo_adr + z2, cnt);
+ } else {
+ memcpy(p, fifo_adr + z2, 0x2000 - z2);
+ p += 0x2000 - z2;
+ memcpy(p, fifo_adr + 0x200, cnt - (0x2000 - z2));
+ }
+
+ DBG_SKB(DBG_B_RECV, skb);
+ B_L1L2(bcs, PH_DATA | INDICATION, skb);
+
+ next:
+ z2 += cnt;
+ if (z2 >= 0x2000)
+ z2 -= B_FIFO_SIZE;
+
+ mb();
+ set_b_rx_z2(bcs, f2, z2);
+ // XXX always receive buffers of a given size
+ }
+}
+
+static inline void
+hfcpci_b_recv_irq(struct hfcpci_adapter *adapter, int nr)
+{
+ DBG(DBG_B_RECV, "");
+
+ switch (adapter->bcs[nr].mode) {
+ case L1_MODE_NULL:
+ DBG(DBG_WARN, "?");
+ break;
+
+ case L1_MODE_HDLC:
+ hfcpci_b_recv_hdlc_irq(adapter, nr);
+ break;
+
+ case L1_MODE_TRANS:
+ hfcpci_b_recv_trans_irq(adapter, nr);
+ break;
+ }
+}
+
+// ----------------------------------------------------------------------
+// transmit IRQ
+
+// XXX make xmit FIFO deeper than 1
+
+static inline void
+hfcpci_d_xmit_irq(struct hfcpci_adapter *adapter)
+{
+ struct sk_buff *skb;
+
+ DBG(DBG_D_XMIT, "");
+
+ skb = adapter->tx_skb;
+ if (!skb) {
+ DBG(DBG_WARN, "?");
+ return;
+ }
+
+ adapter->tx_skb = NULL;
+ D_L1L2(adapter, PH_DATA | CONFIRM, (void *) skb->truesize);
+ dev_kfree_skb_irq(skb);
+}
+
+static inline void
+hfcpci_b_xmit_irq(struct hfcpci_adapter *adapter, int nr)
+{
+ struct hfcpci_bcs *bcs = &adapter->bcs[nr];
+ struct sk_buff *skb;
+
+ DBG(DBG_B_XMIT, "");
+
+ skb = bcs->tx_skb;
+ if (!skb) {
+ DBG(DBG_WARN, "?");
+ return;
+ }
+
+ bcs->tx_skb = NULL;
+ B_L1L2(bcs, PH_DATA | CONFIRM, (void *) skb->truesize);
+ dev_kfree_skb_irq(skb);
+}
+
+// ----------------------------------------------------------------------
+// Layer 1 state change IRQ
+
+static inline void
+hfcpci_state_irq(struct hfcpci_adapter *adapter)
+{
+ u8 val;
+
+ val = hfcpci_readb(adapter, HFCPCI_STATES);
+ DBG(DBG_L1M, "STATES %#x", val);
+ FsmEvent(&adapter->l1m, val & 0xf, NULL);
+}
+
+// ----------------------------------------------------------------------
+// Timer IRQ
+
+static inline void
+hfcpci_timer_irq(struct hfcpci_adapter *adapter)
+{
+ hfcpci_writeb(adapter, adapter->ctmt | HFCPCI_CLTIMER, HFCPCI_CTMT);
+}
+
+// ----------------------------------------------------------------------
+// IRQ handler
+
+static void
+hfcpci_irq(int intno, void *dev, struct pt_regs *regs)
+{
+ struct hfcpci_adapter *adapter = dev;
+ int loop = 15;
+ u8 val, stat;
+
+ if (!(adapter->int_m2 & 0x08))
+ return; /* not initialised */ // XX
+
+ stat = hfcpci_readb(adapter, HFCPCI_STATUS);
+ if (!(stat & HFCPCI_ANYINT))
+ return;
+
+ spin_lock(&adapter->hw_lock);
+ while (loop-- > 0) {
+ val = hfcpci_readb(adapter, HFCPCI_INT_S1);
+ DBG(DBG_IRQ, "stat %02x s1 %02x", stat, val);
+ val &= adapter->int_m1;
+
+ if (!val)
+ break;
+
+ if (val & 0x08)
+ hfcpci_b_recv_irq(adapter, 0);
+
+ if (val & 0x10)
+ hfcpci_b_recv_irq(adapter, 1);
+
+ if (val & 0x01)
+ hfcpci_b_xmit_irq(adapter, 0);
+
+ if (val & 0x02)
+ hfcpci_b_xmit_irq(adapter, 1);
+
+ if (val & 0x20)
+ hfcpci_d_recv_irq(adapter);
+
+ if (val & 0x04)
+ hfcpci_d_xmit_irq(adapter);
+
+ if (val & 0x40)
+ hfcpci_state_irq(adapter);
+
+ if (val & 0x80)
+ hfcpci_timer_irq(adapter);
+ }
+ spin_unlock(&adapter->hw_lock);
+}
+
+// ----------------------------------------------------------------------
+// reset hardware
+
+static void
+hfcpci_reset(struct hfcpci_adapter *adapter)
+{
+ /* disable all interrupts */
+ adapter->int_m1 = 0;
+ adapter->int_m2 = 0;
+ hfcpci_writeb(adapter, adapter->int_m1, HFCPCI_INT_M1);
+ hfcpci_writeb(adapter, adapter->int_m2, HFCPCI_INT_M2);
+
+ /* reset */
+ hfcpci_writeb(adapter, HFCPCI_RESET, HFCPCI_CIRM);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((30 * HZ) / 1000);
+ hfcpci_writeb(adapter, 0, HFCPCI_CIRM);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((20 * HZ) / 1000);
+ if (hfcpci_readb(adapter, HFCPCI_STATUS) & 2) // XX
+ printk(KERN_WARNING "HFC-PCI init bit busy\n");
+}
+
+// ----------------------------------------------------------------------
+// init hardware
+
+static void
+hfcpci_hw_init(struct hfcpci_adapter *adapter)
+{
+ adapter->fifo_en = 0x30; /* only D fifos enabled */ // XX
+ hfcpci_writeb(adapter, adapter->fifo_en, HFCPCI_FIFO_EN);
+
+ /* no echo connect , threshold */
+ adapter->trm = HFCPCI_BTRANS_THRESMASK;
+ hfcpci_writeb(adapter, adapter->trm, HFCPCI_TRM);
+
+ /* ST-Bit delay for TE-Mode */
+ hfcpci_writeb(adapter, CLKDEL_TE, HFCPCI_CLKDEL);
+
+ /* S/T Auto awake */
+ adapter->sctrl_e = HFCPCI_AUTO_AWAKE;
+ hfcpci_writeb(adapter, adapter->sctrl_e, HFCPCI_SCTRL_E);
+
+ /* no exchange */
+ adapter->bswapped = 0;
+ /* we are in TE mode */
+ adapter->nt_mode = 0;
+
+ adapter->ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER;
+ hfcpci_writeb(adapter, adapter->ctmt, HFCPCI_CTMT);
+
+ adapter->int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC |
+ HFCPCI_INTS_L1STATE;
+ hfcpci_writeb(adapter, adapter->int_m1, HFCPCI_INT_M1);
+
+ /* clear already pending ints */
+ hfcpci_readb(adapter, HFCPCI_INT_S1);
+
+ adapter->l1m.state = 2;
+ hfcpci_writeb(adapter, HFCPCI_LOAD_STATE | 2, HFCPCI_STATES); // XX /* HFC ST 2 */
+ udelay(10);
+ hfcpci_writeb(adapter, 2, HFCPCI_STATES); /* HFC ST 2 */
+
+ /* HFC Master Mode */
+ adapter->mst_m = HFCPCI_MASTER;
+ hfcpci_writeb(adapter, adapter->mst_m, HFCPCI_MST_MODE);
+
+ /* set tx_lo mode, error in datasheet ! */
+ adapter->sctrl = 0x40;
+ hfcpci_writeb(adapter, adapter->sctrl, HFCPCI_SCTRL);
+
+ adapter->sctrl_r = 0;
+ hfcpci_writeb(adapter, adapter->sctrl_r, HFCPCI_SCTRL_R);
+
+ // XXX
+ /* Init GCI/IOM2 in master mode */
+ /* Slots 0 and 1 are set for B-chan 1 and 2 */
+ /* D- and monitor/CI channel are not enabled */
+ /* STIO1 is used as output for data, B1+B2 from ST->IOM+HFC */
+ /* STIO2 is used as data input, B1+B2 from IOM->ST */
+ /* ST B-channel send disabled -> continous 1s */
+ /* The IOM slots are always enabled */
+ adapter->conn = 0; /* set data flow directions */
+ hfcpci_writeb(adapter, adapter->conn, HFCPCI_CONNECT);
+ hfcpci_writeb(adapter, 0x80, HFCPCI_B1_SSL); /* B1-Slot 0 STIO1 out enabled */
+ hfcpci_writeb(adapter, 0x81, HFCPCI_B2_SSL); /* B2-Slot 1 STIO1 out enabled */
+ hfcpci_writeb(adapter, 0x80, HFCPCI_B1_RSL); /* B1-Slot 0 STIO2 in enabled */
+ hfcpci_writeb(adapter, 0x81, HFCPCI_B2_RSL); /* B2-Slot 1 STIO2 in enabled */
+
+ /* Finally enable IRQ output */
+ adapter->int_m2 = HFCPCI_IRQ_ENABLE;
+ hfcpci_writeb(adapter, adapter->int_m2, HFCPCI_INT_M2);
+
+ hfcpci_readb(adapter, HFCPCI_INT_S2);
+}
+
+// ----------------------------------------------------------------------
+// probe / remove
+
+static struct hfcpci_adapter * __devinit
+new_adapter(struct pci_dev *pdev)
+{
+ struct hfcpci_adapter *adapter;
+ struct hisax_b_if *b_if[2];
+ int i;
+
+ adapter = kmalloc(sizeof(struct hfcpci_adapter), GFP_KERNEL);
+ if (!adapter)
+ return NULL;
+
+ memset(adapter, 0, sizeof(struct hfcpci_adapter));
+
+ SET_MODULE_OWNER(&adapter->d_if);
+ adapter->d_if.ifc.priv = adapter;
+ adapter->d_if.ifc.l2l1 = hfcpci_d_l2l1;
+
+ for (i = 0; i < 2; i++) {
+ adapter->bcs[i].adapter = adapter;
+ adapter->bcs[i].channel = i;
+ adapter->bcs[i].b_if.ifc.priv = &adapter->bcs[i];
+ adapter->bcs[i].b_if.ifc.l2l1 = hfcpci_b_l2l1;
+ }
+
+ pci_set_drvdata(pdev, adapter);
+
+ for (i = 0; i < 2; i++)
+ b_if[i] = &adapter->bcs[i].b_if;
+
+ hisax_register(&adapter->d_if, b_if, "hfcpci", protocol);
+
+ return adapter;
+}
+
+static void delete_adapter(struct hfcpci_adapter *adapter)
+{
+ hisax_unregister(&adapter->d_if);
+ kfree(adapter);
+}
+
+static int __devinit hfcpci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct hfcpci_adapter *adapter;
+ int retval;
+
+ DBG(DBG_INFO, "");
+ retval = -ENOMEM;
+ adapter = new_adapter(pdev);
+ if (!adapter)
+ goto err;
+
+ retval = pci_enable_device(pdev);
+ if (retval)
+ goto err_free;
+
+ adapter->irq = pdev->irq;
+ retval = request_irq(adapter->irq, hfcpci_irq, SA_SHIRQ,
+ "hfcpci", adapter);
+ if (retval)
+ goto err_free;
+
+ retval = -EBUSY;
+ if (!request_mem_region(pci_resource_start(pdev, 1), 256, "hfcpci"))
+ goto err_free_irq;
+
+ adapter->mmio = ioremap(pci_resource_start(pdev, 1), 256); // XX pci_io
+ if (!adapter->mmio)
+ goto err_release_region;
+
+ /* Allocate 32K for FIFOs */
+ if (pci_set_dma_mask(pdev, 0xffffffff))
+ goto err_unmap;
+
+ adapter->fifo = pci_alloc_consistent(pdev, 32768, &adapter->fifo_dma);
+ if (!adapter->fifo)
+ goto err_unmap;
+
+ pci_write_config_dword(pdev, HFCPCI_MWBA, (u32) adapter->fifo_dma);
+ pci_set_master(pdev);
+
+ adapter->l1m.fsm = &l1fsm;
+ adapter->l1m.state = ST_L1_F0;
+#ifdef CONFIG_HISAX_DEBUG
+ adapter->l1m.debug = 1;
+#else
+ adapter->l1m.debug = 0;
+#endif
+ adapter->l1m.userdata = adapter;
+ adapter->l1m.printdebug = l1m_debug;
+ FsmInitTimer(&adapter->l1m, &adapter->timer);
+
+ hfcpci_reset(adapter);
+ hfcpci_hw_init(adapter);
+
+ printk(KERN_INFO "hisax_hfcpci: found adapter %s at %s\n",
+ (char *) ent->driver_data, pdev->slot_name);
+
+ return 0;
+
+ err_unmap:
+ iounmap(adapter->mmio);
+ err_release_region:
+ release_mem_region(pci_resource_start(pdev, 1), 256);
+ err_free_irq:
+ free_irq(adapter->irq, adapter);
+ err_free:
+ delete_adapter(adapter);
+ err:
+ return retval;
+}
+
+static void __devexit hfcpci_remove(struct pci_dev *pdev)
+{
+ struct hfcpci_adapter *adapter = pci_get_drvdata(pdev);
+
+ hfcpci_reset(adapter);
+
+// del_timer(&cs->hw.hfcpci.timer); XX
+
+ /* disable DMA */
+ pci_disable_device(pdev);
+ pci_write_config_dword(pdev, HFCPCI_MWBA, 0);
+ pci_free_consistent(pdev, 32768, adapter->fifo, adapter->fifo_dma);
+
+ iounmap(adapter->mmio);
+ release_mem_region(pci_resource_start(pdev, 1), 256);
+ free_irq(adapter->irq, adapter);
+ delete_adapter(adapter);
+}
+
+static struct pci_driver hfcpci_driver = {
+ name: "hfcpci",
+ probe: hfcpci_probe,
+ remove: hfcpci_remove,
+ id_table: hfcpci_ids,
+};
+
+static int __init hisax_hfcpci_init(void)
+{
+ int retval;
+
+ printk(KERN_INFO "hisax_hfcpcipnp: HFC PCI ISDN driver v0.0.1\n");
+
+ l1fsm.state_count = L1_STATE_COUNT;
+ l1fsm.event_count = L1_EVENT_COUNT;
+ l1fsm.strState = strL1State;
+ l1fsm.strEvent = strL1Event;
+ retval = FsmNew(&l1fsm, L1FnList, ARRAY_SIZE(L1FnList));
+ if (retval)
+ goto err;
+
+ retval = pci_module_init(&hfcpci_driver);
+ if (retval)
+ goto err_fsm;
+
+ return 0;
+
+ err_fsm:
+ FsmFree(&l1fsm);
+ err:
+ return retval;
+}
+
+static void __exit hisax_hfcpci_exit(void)
+{
+ FsmFree(&l1fsm);
+ pci_unregister_driver(&hfcpci_driver);
+}
+
+module_init(hisax_hfcpci_init);
+module_exit(hisax_hfcpci_exit);
diff --git a/drivers/isdn/hisax/hisax_hfcpci.h b/drivers/isdn/hisax/hisax_hfcpci.h
new file mode 100644
index 000000000000..5f2cae4bf5d9
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_hfcpci.h
@@ -0,0 +1,40 @@
+#include "hisax_if.h"
+#include "hisax_isac.h"
+#include <linux/pci.h>
+
+struct hfcpci_bcs {
+ struct hisax_b_if b_if;
+ struct hfcpci_adapter *adapter;
+ int mode;
+ int channel;
+ int last_fcnt;
+
+ struct sk_buff *tx_skb;
+};
+
+struct hfcpci_adapter {
+ struct hisax_d_if d_if;
+ spinlock_t hw_lock;
+ unsigned int irq;
+ void *mmio;
+ u8 *fifo;
+ dma_addr_t fifo_dma;
+
+ struct FsmInst l1m;
+ struct FsmTimer timer;
+ struct sk_buff *tx_skb;
+ int last_fcnt;
+
+ u8 int_m1, int_m2;
+ u8 fifo_en;
+ u8 trm;
+ u8 sctrl, sctrl_r, sctrl_e;
+ u8 nt_mode;
+ u8 ctmt;
+ u8 mst_m;
+ u8 conn;
+ u8 bswapped;
+
+ struct hfcpci_bcs bcs[2];
+};
+
diff --git a/drivers/isdn/hisax/st5481_usb.c b/drivers/isdn/hisax/st5481_usb.c
index bd20feac8b7b..1a03312607b7 100644
--- a/drivers/isdn/hisax/st5481_usb.c
+++ b/drivers/isdn/hisax/st5481_usb.c
@@ -416,7 +416,7 @@ st5481_setup_isocpipes(struct urb* urb[2], struct usb_device *dev,
for (j = 0; j < 2; j++) {
retval = -ENOMEM;
- urb[j] = usb_alloc_urb(num_packet, GFP_KERNEL);
+ urb[j] = usb_alloc_urb(num_packets, GFP_KERNEL);
if (!urb[j])
goto err;