diff --git a/drivers/isdn/Config.in b/drivers/isdn/Config.in index e999cb6b..7ff4ab45 100644 --- a/drivers/isdn/Config.in +++ b/drivers/isdn/Config.in @@ -114,7 +114,9 @@ if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then dep_tristate 'Sedlbauer PCMCIA cards' CONFIG_HISAX_SEDLBAUER_CS $CONFIG_PCMCIA dep_tristate 'ELSA PCMCIA MicroLink cards' CONFIG_HISAX_ELSA_CS $CONFIG_PCMCIA dep_tristate 'Colognechip HFC-USB support' CONFIG_HISAX_HFC_USB_CS $CONFIG_HISAX $CONFIG_USB - dep_tristate 'ST5481 USB ISDN modem' CONFIG_HISAX_ST5481_CS $CONFIG_HISAX $CONFIG_USB + dep_tristate 'ST5481 USB ISDN modem (EXPERIMENTAL)' CONFIG_HISAX_ST5481 $CONFIG_HISAX $CONFIG_USB $CONFIG_EXPERIMENTAL + dep_tristate 'Fritz!PCIv2 support (EXPERIMENTAL)' CONFIG_HISAX_FRITZ_PCIPNP $CONFIG_HISAX $CONFIG_EXPERIMENTAL + if [ "$CONFIG_HISAX_SEDLBAUER_CS" != "n" ]; then define_bool CONFIG_HISAX_SEDLBAUER y fi @@ -127,6 +129,8 @@ if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then if [ "$CONFIG_HISAX_ST5481_CS" != "n" ]; then define_bool CONFIG_HISAX_ST5481 y fi + + bool ' HiSax debugging' CONFIG_HISAX_DEBUG fi endmenu diff --git a/drivers/isdn/hisax/Makefile b/drivers/isdn/hisax/Makefile index 68606fdd..5d80b3f6 100644 --- a/drivers/isdn/hisax/Makefile +++ b/drivers/isdn/hisax/Makefile @@ -2,19 +2,19 @@ # The target object and module list name. -O_TARGET := vmlinux-obj.o +O_TARGET := vmlinux-obj.o # Objects that export symbols. -export-objs := config.o fsm.o +export-objs := config.o fsm.o hisax_isac.o # Multipart objects. -list-multi := hisax.o st5481_mod.o -hisax-objs := config.o isdnl1.o tei.o isdnl2.o isdnl3.o \ - lmgr.o q931.o callc.o fsm.o cert.o -st5481-objs := st5481_init.o st5481_usb.o st5481_d.o st5481_b.o \ - st5481_hdlc.o +list-multi := hisax.o st5481_mod.o +hisax-objs := config.o isdnl1.o tei.o isdnl2.o isdnl3.o \ + lmgr.o q931.o callc.o fsm.o cert.o +hisax_st5481-objs := st5481_init.o st5481_usb.o st5481_d.o st5481_b.o \ + st5481_hdlc.o # Optional parts of multipart objects. hisax-objs-$(CONFIG_HISAX_EURO) += l3dss1.o @@ -28,7 +28,6 @@ hisax-objs-$(CONFIG_HISAX_S0BOX) += s0box.o isac.o arcofi.o hscx.o hisax-objs-$(CONFIG_HISAX_AVM_A1) += avm_a1.o isac.o arcofi.o hscx.o hisax-objs-$(CONFIG_HISAX_AVM_A1_PCMCIA) += avm_a1p.o isac.o arcofi.o hscx.o hisax-objs-$(CONFIG_HISAX_FRITZPCI) += avm_pci.o isac.o arcofi.o -hisax-objs-$(CONFIG_HISAX_AVM_A1) += avm_a1.o isac.o arcofi.o hscx.o hisax-objs-$(CONFIG_HISAX_ELSA) += elsa.o isac.o arcofi.o hscx.o hisax-objs-$(CONFIG_HISAX_IX1MICROR2) += ix1_micro.o isac.o arcofi.o hscx.o hisax-objs-$(CONFIG_HISAX_DIEHLDIVA) += diva.o isac.o arcofi.o hscx.o @@ -60,12 +59,9 @@ obj-$(CONFIG_ISDN_DRV_HISAX) += hisax.o obj-$(CONFIG_HISAX_SEDLBAUER_CS) += sedlbauer_cs.o obj-$(CONFIG_HISAX_ELSA_CS) += elsa_cs.o obj-$(CONFIG_HISAX_HFC_USB_CS) += hfc_usb.o -obj-$(CONFIG_HISAX_ST5481_CS) += st5481.o +obj-$(CONFIG_HISAX_ST5481) += hisax_st5481.o +obj-$(CONFIG_HISAX_FRITZ_PCIPNP) += hisax_fcpcipnp.o hisax_isac.o -MD5FILES := isac.c isdnl1.c isdnl2.c isdnl3.c \ - tei.c callc.c cert.c l3dss1.c l3_1tr6.c \ - elsa.c diva.c sedlbauer.c hfc_pci.c \ - hfc_usbr.c hfc_usb.c CERT := $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?) CFLAGS_cert.o := -DCERTIFICATION=$(CERT) @@ -76,5 +72,5 @@ include $(TOPDIR)/drivers/isdn/Rules.make hisax.o: $(hisax-objs) $(LD) -r -o $@ $(hisax-objs) -st5481.o: $(st5481-objs) - $(LD) -r -o $@ $(st5481-objs) +hisax_st5481.o: $(hisax_st5481-objs) + $(LD) -r -o $@ $(hisax_st5481-objs) diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c index 1e1fcbd3..6ef064f4 100644 --- a/drivers/isdn/hisax/config.c +++ b/drivers/isdn/hisax/config.c @@ -326,7 +326,8 @@ EXPORT_SYMBOL(hfc_init_pcmcia); #define DEFAULT_PROTO_NAME "UNKNOWN" #endif #ifndef DEFAULT_CARD -#error "HiSax: No cards configured" +#define DEFAULT_CARD 0 +#define DEFAULT_CFG {0,0,0,0} #endif int hisax_init_pcmcia(void *, int *, struct IsdnCard *); @@ -1805,6 +1806,7 @@ static int hisax_cardmsg(struct IsdnCardState *cs, int mt, void *arg); static int hisax_bc_setstack(struct PStack *st, struct BCState *bcs); static void hisax_bc_close(struct BCState *bcs); static void hisax_bh(struct IsdnCardState *cs); +static void EChannel_proc_rcv(struct hisax_d_if *d_if); int hisax_register(struct hisax_d_if *hisax_d_if, struct hisax_b_if *b_if[], char *name, int protocol) @@ -1846,13 +1848,17 @@ int hisax_register(struct hisax_d_if *hisax_d_if, struct hisax_b_if *b_if[], hisax_d_if->b_if[i] = b_if[i]; } hisax_d_if->ifc.l1l2 = hisax_d_l1l2; - + skb_queue_head_init(&hisax_d_if->erq); + clear_bit(0, &hisax_d_if->ph_state); + return 0; } void hisax_unregister(struct hisax_d_if *hisax_d_if) { + cards[hisax_d_if->cs->cardnr].typ = 0; HiSax_closecard(hisax_d_if->cs->cardnr); + skb_queue_purge(&hisax_d_if->erq); } #include "isdnl1.h" @@ -1866,8 +1872,22 @@ static void hisax_sched_event(struct IsdnCardState *cs, int event) static void hisax_bh(struct IsdnCardState *cs) { + struct PStack *st; + int pr; + if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) DChannel_proc_rcv(cs); + if (test_and_clear_bit(E_RCVBUFREADY, &cs->event)) + EChannel_proc_rcv(cs->hw.hisax_d_if); + if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) { + if (test_bit(0, &cs->hw.hisax_d_if->ph_state)) + pr = PH_ACTIVATE | INDICATION; + else + pr = PH_DEACTIVATE | INDICATION; + for (st = cs->stlist; st; st = st->next) + st->l1.l1l2(st, pr, NULL); + + } } static void hisax_b_sched_event(struct BCState *bcs, int event) @@ -1898,12 +1918,12 @@ static void hisax_d_l1l2(struct hisax_if *ifc, int pr, void *arg) switch (pr) { case PH_ACTIVATE | INDICATION: - for (st = cs->stlist; st; st = st->next) - st->l1.l1l2(st, pr, NULL); + set_bit(0, &d_if->ph_state); + hisax_sched_event(cs, D_L1STATECHANGE); break; case PH_DEACTIVATE | INDICATION: - for (st = cs->stlist; st; st = st->next) - st->l1.l1l2(st, pr, NULL); + clear_bit(0, &d_if->ph_state); + hisax_sched_event(cs, D_L1STATECHANGE); break; case PH_DATA | INDICATION: skb_queue_tail(&cs->rq, arg); @@ -1913,15 +1933,18 @@ static void hisax_d_l1l2(struct hisax_if *ifc, int pr, void *arg) skb = skb_dequeue(&cs->sq); if (skb) { D_L2L1(d_if, PH_DATA | REQUEST, skb); - } else { - clear_bit(FLG_L1_DBUSY, &cs->HW_Flags); - for (st = cs->stlist; st; st = st->next) { - if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) - - st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); - break; - } + break; } + clear_bit(FLG_L1_DBUSY, &cs->HW_Flags); + for (st = cs->stlist; st; st = st->next) { + if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + break; + } + break; + case PH_DATA_E | INDICATION: + skb_queue_tail(&d_if->erq, arg); + hisax_sched_event(cs, E_RCVBUFREADY); break; default: printk("pr %#x\n", pr); @@ -2058,6 +2081,35 @@ static void hisax_bc_close(struct BCState *bcs) B_L2L1(b_if, PH_DEACTIVATE | REQUEST, NULL); } +static void EChannel_proc_rcv(struct hisax_d_if *d_if) +{ + struct IsdnCardState *cs = d_if->cs; + u_char *ptr; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&d_if->erq)) != NULL) { + if (cs->debug & DEB_DLOG_HEX) { + ptr = cs->dlog; + if ((skb->len) < MAX_DLOG_SPACE / 3 - 10) { + *ptr++ = 'E'; + *ptr++ = 'C'; + *ptr++ = 'H'; + *ptr++ = 'O'; + *ptr++ = ':'; + ptr += QuickHex(ptr, skb->data, skb->len); + ptr--; + *ptr++ = '\n'; + *ptr = 0; + HiSax_putstatus(cs, NULL, cs->dlog); + } else + HiSax_putstatus(cs, "LogEcho: ", + "warning Frame too big (%d)", + skb->len); + } + dev_kfree_skb_any(skb); + } +} + #include static struct pci_device_id hisax_pci_tbl[] __initdata = { diff --git a/drivers/isdn/hisax/fsm.h b/drivers/isdn/hisax/fsm.h index 1ad94698..3a77d1ea 100644 --- a/drivers/isdn/hisax/fsm.h +++ b/drivers/isdn/hisax/fsm.h @@ -1,3 +1,6 @@ +#ifndef __FSM_H__ +#define __FSM_H__ + #include struct FsmInst; @@ -41,3 +44,5 @@ int FsmAddTimer(struct FsmTimer *ft, int millisec, int event, void FsmRestartTimer(struct FsmTimer *ft, int millisec, int event, void *arg, int where); void FsmDelTimer(struct FsmTimer *ft, int where); + +#endif diff --git a/drivers/isdn/hisax/hisax_debug.h b/drivers/isdn/hisax/hisax_debug.h new file mode 100644 index 00000000..0c94449a --- /dev/null +++ b/drivers/isdn/hisax/hisax_debug.h @@ -0,0 +1,80 @@ +/* + * Common debugging macros for use with the hisax driver + * + * Author Frode Isaksen + * Copyright 2001 by Frode Isaksen + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +/* + * How to use: + * + * Before including this file, you need to + * #define __debug_variable my_debug + * where my_debug is a variable in your code which + * determines the debug bitmask. + * + * If CONFIG_HISAX_DEBUG is not set, all macros evaluate to nothing + */ + +#ifndef __HISAX_DEBUG_H__ +#define __HISAX_DEBUG_H__ + +#ifdef CONFIG_HISAX_DEBUG + +#define DBG(level, format, arg...) do { \ +if (level & __debug_variable) \ +printk(KERN_DEBUG __FUNCTION__ ": " format "\n" , ## arg); \ +} while (0) + +#define DBG_PACKET(level,data,count) \ + if (level & __debug_variable) dump_packet(__FUNCTION__,data,count) + +#define DBG_SKB(level,skb) \ + if ((level & __debug_variable) && skb) dump_packet(__FUNCTION__,skb->data,skb->len) + + +static void __attribute__((unused)) +dump_packet(const char *name,const u_char *data,int pkt_len) +{ +#define DUMP_HDR_SIZE 20 +#define DUMP_TLR_SIZE 8 + if (pkt_len) { + int i,len1,len2; + + printk(KERN_DEBUG "%s: length=%d,data=",name,pkt_len); + + if (pkt_len > DUMP_HDR_SIZE+ DUMP_TLR_SIZE) { + len1 = DUMP_HDR_SIZE; + len2 = DUMP_TLR_SIZE; + } else { + len1 = pkt_len > DUMP_HDR_SIZE ? DUMP_HDR_SIZE : pkt_len; + len2 = 0; + } + for (i = 0; i < len1; ++i) { + printk ("%.2x", data[i]); + } + if (len2) { + printk (".."); + for (i = pkt_len-DUMP_TLR_SIZE; i < pkt_len; ++i) { + printk ("%.2x", data[i]); + } + } + printk ("\n"); + } +#undef DUMP_HDR_SIZE +#undef DUMP_TLR_SIZE +} + +#else + +#define DBG(level, format, arg...) do {} while (0) +#define DBG_PACKET(level,data,count) do {} while (0) +#define DBG_SKB(level,skb) do {} while (0) + +#endif + +#endif diff --git a/drivers/isdn/hisax/hisax_fcpcipnp.c b/drivers/isdn/hisax/hisax_fcpcipnp.c new file mode 100644 index 00000000..392a234a --- /dev/null +++ b/drivers/isdn/hisax/hisax_fcpcipnp.c @@ -0,0 +1,1013 @@ +/* + * Driver for AVM Fritz!PCI, Fritz!PCI v2, Fritz!PnP ISDN cards + * + * Author Kai Germaschewski + * Copyright 2001 by Kai Germaschewski + * 2001 by Karsten Keil + * + * based upon Karsten Keil's original avm_pci.c driver + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * Thanks to Wizard Computersysteme GmbH, Bremervoerde and + * SoHaNet Technology GmbH, Berlin + * for supporting the development of this driver + */ + + +/* TODO: + * + * o POWER PC + * o clean up debugging + */ + +#include +#include +#include +#include +#include +#include "hisax_isapnp.h" +#include +#include +#include +#include +#include "hisax_fcpcipnp.h" +#include "hisax_isac.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 "); +MODULE_DESCRIPTION("AVM Fritz!PCI/PnP ISDN driver"); + +#ifndef PCI_DEVICE_ID_AVM_A1_V2 +#define PCI_DEVICE_ID_AVM_A1_V2 0x0e00 +#endif + +static struct pci_device_id fcpci_ids[] __initdata = { + { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1 , PCI_ANY_ID, PCI_ANY_ID, + 0, 0, (unsigned long) "Fritz!Card PCI" }, + { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1_V2, PCI_ANY_ID, PCI_ANY_ID, + 0, 0, (unsigned long) "Fritz!Card PCI v2" }, + { } +}; + +#ifdef __ISAPNP__ +static struct isapnp_device_id fcpnp_ids[] __initdata = { + { ISAPNP_VENDOR('A', 'V', 'M'), ISAPNP_FUNCTION(0x0900), + ISAPNP_VENDOR('A', 'V', 'M'), ISAPNP_FUNCTION(0x0900), + (unsigned long) "Fritz!Card PnP" }, + { } +}; +#endif + +MODULE_DEVICE_TABLE(pci, fcpci_ids); +MODULE_DEVICE_TABLE(isapnp, fcpnp_ids); +static int protocol = 2; /* EURO-ISDN Default */ +MODULE_PARM(protocol, "i"); + +static LIST_HEAD(adapter_list); + +// ---------------------------------------------------------------------- + +#define AVM_INDEX 0x04 +#define AVM_DATA 0x10 + +#define AVM_IDX_HDLC_1 0x00 +#define AVM_IDX_HDLC_2 0x01 +#define AVM_IDX_ISAC_FIFO 0x02 +#define AVM_IDX_ISAC_REG_LOW 0x04 +#define AVM_IDX_ISAC_REG_HIGH 0x06 + +#define AVM_STATUS0 0x02 + +#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 0x03 +#define AVM_STATUS1_ENA_IOM 0x80 + +#define HDLC_FIFO 0x0 +#define HDLC_STATUS 0x4 +#define HDLC_CTRL 0x4 + +#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 + +#define AVM_HDLC_FIFO_1 0x10 +#define AVM_HDLC_FIFO_2 0x18 + +#define AVM_HDLC_STATUS_1 0x14 +#define AVM_HDLC_STATUS_2 0x1c + +#define AVM_ISACSX_INDEX 0x04 +#define AVM_ISACSX_DATA 0x08 + +// ---------------------------------------------------------------------- +// Fritz!PCI + +static unsigned char fcpci_read_isac(struct isac *isac, unsigned char offset) +{ + struct fritz_adapter *adapter = isac->priv; + unsigned char idx = (offset > 0x2f) ? + AVM_IDX_ISAC_REG_HIGH : AVM_IDX_ISAC_REG_LOW; + unsigned char val; + unsigned long flags; + + spin_lock_irqsave(&adapter->hw_lock, flags); + outb(idx, adapter->io + AVM_INDEX); + val = inb(adapter->io + AVM_DATA + (offset & 0xf)); + spin_unlock_irqrestore(&adapter->hw_lock, flags); + DBG(0x1000, __FUNCTION__ " port %#x, value %#x", + offset, val); + return val; +} + +static void fcpci_write_isac(struct isac *isac, unsigned char offset, + unsigned char value) +{ + struct fritz_adapter *adapter = isac->priv; + unsigned char idx = (offset > 0x2f) ? + AVM_IDX_ISAC_REG_HIGH : AVM_IDX_ISAC_REG_LOW; + unsigned long flags; + + DBG(0x1000, __FUNCTION__ " port %#x, value %#x", + offset, value); + spin_lock_irqsave(&adapter->hw_lock, flags); + outb(idx, adapter->io + AVM_INDEX); + outb(value, adapter->io + AVM_DATA + (offset & 0xf)); + spin_unlock_irqrestore(&adapter->hw_lock, flags); +} + +static void fcpci_read_isac_fifo(struct isac *isac, unsigned char * data, + int size) +{ + struct fritz_adapter *adapter = isac->priv; + unsigned long flags; + + spin_lock_irqsave(&adapter->hw_lock, flags); + outb(AVM_IDX_ISAC_FIFO, adapter->io + AVM_INDEX); + insb(adapter->io + AVM_DATA, data, size); + spin_unlock_irqrestore(&adapter->hw_lock, flags); +} + +static void fcpci_write_isac_fifo(struct isac *isac, unsigned char * data, + int size) +{ + struct fritz_adapter *adapter = isac->priv; + unsigned long flags; + + spin_lock_irqsave(&adapter->hw_lock, flags); + outb(AVM_IDX_ISAC_FIFO, adapter->io + AVM_INDEX); + outsb(adapter->io + AVM_DATA, data, size); + spin_unlock_irqrestore(&adapter->hw_lock, flags); +} + +static u32 fcpci_read_hdlc_status(struct fritz_adapter *adapter, int nr) +{ + u32 val; + int idx = nr ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1; + unsigned long flags; + + spin_lock_irqsave(&adapter->hw_lock, flags); + outl(idx, adapter->io + AVM_INDEX); + val = inl(adapter->io + AVM_DATA + HDLC_STATUS); + spin_unlock_irqrestore(&adapter->hw_lock, flags); + return val; +} + +static void __fcpci_write_ctrl(struct fritz_bcs *bcs, int which) +{ + struct fritz_adapter *adapter = bcs->adapter; + int idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1; + + DBG(0x40, "hdlc %c wr%x ctrl %x", + 'A' + bcs->channel, which, bcs->ctrl.ctrl); + + outl(idx, adapter->io + AVM_INDEX); + outl(bcs->ctrl.ctrl, adapter->io + AVM_DATA + HDLC_CTRL); +} + +static void fcpci_write_ctrl(struct fritz_bcs *bcs, int which) +{ + struct fritz_adapter *adapter = bcs->adapter; + unsigned long flags; + + spin_lock_irqsave(&adapter->hw_lock, flags); + __fcpci_write_ctrl(bcs, which); + spin_unlock_irqrestore(&adapter->hw_lock, flags); +} + +// ---------------------------------------------------------------------- +// Fritz!PCI v2 + +static unsigned char fcpci2_read_isac(struct isac *isac, unsigned char offset) +{ + struct fritz_adapter *adapter = isac->priv; + unsigned char val; + unsigned long flags; + + spin_lock_irqsave(&adapter->hw_lock, flags); + outl(offset, adapter->io + AVM_ISACSX_INDEX); + val = inl(adapter->io + AVM_ISACSX_DATA); + spin_unlock_irqrestore(&adapter->hw_lock, flags); + DBG(0x1000, __FUNCTION__ " port %#x, value %#x", + offset, val); + + return val; +} + +static void fcpci2_write_isac(struct isac *isac, unsigned char offset, + unsigned char value) +{ + struct fritz_adapter *adapter = isac->priv; + unsigned long flags; + + DBG(0x1000, __FUNCTION__ " port %#x, value %#x", + offset, value); + spin_lock_irqsave(&adapter->hw_lock, flags); + outl(offset, adapter->io + AVM_ISACSX_INDEX); + outl(value, adapter->io + AVM_ISACSX_DATA); + spin_unlock_irqrestore(&adapter->hw_lock, flags); +} + +static void fcpci2_read_isac_fifo(struct isac *isac, unsigned char * data, + int size) +{ + struct fritz_adapter *adapter = isac->priv; + int i; + unsigned long flags; + + spin_lock_irqsave(&adapter->hw_lock, flags); + outl(0, adapter->io + AVM_ISACSX_INDEX); + for (i = 0; i < size; i++) + data[i] = inl(adapter->io + AVM_ISACSX_DATA); + spin_unlock_irqrestore(&adapter->hw_lock, flags); +} + +static void fcpci2_write_isac_fifo(struct isac *isac, unsigned char * data, + int size) +{ + struct fritz_adapter *adapter = isac->priv; + int i; + unsigned long flags; + + spin_lock_irqsave(&adapter->hw_lock, flags); + outl(0, adapter->io + AVM_ISACSX_INDEX); + for (i = 0; i < size; i++) + outl(data[i], adapter->io + AVM_ISACSX_DATA); + spin_unlock_irqrestore(&adapter->hw_lock, flags); +} + +static u32 fcpci2_read_hdlc_status(struct fritz_adapter *adapter, int nr) +{ + int offset = nr ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1; + + return inl(adapter->io + offset); +} + +static void fcpci2_write_ctrl(struct fritz_bcs *bcs, int which) +{ + struct fritz_adapter *adapter = bcs->adapter; + int offset = bcs->channel ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1; + + DBG(0x40, "hdlc %c wr%x ctrl %x", + 'A' + bcs->channel, which, bcs->ctrl.ctrl); + + outl(bcs->ctrl.ctrl, adapter->io + offset); +} + +// ---------------------------------------------------------------------- +// Fritz!PnP (ISAC access as for Fritz!PCI) + +static u32 fcpnp_read_hdlc_status(struct fritz_adapter *adapter, int nr) +{ + unsigned char idx = nr ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1; + u32 val; + unsigned long flags; + + spin_lock_irqsave(&adapter->hw_lock, flags); + outb(idx, adapter->io + AVM_INDEX); + val = inb(adapter->io + AVM_DATA + HDLC_STATUS); + if (val & HDLC_INT_RPR) + val |= inb(adapter->io + AVM_DATA + HDLC_STATUS + 1) << 8; + spin_unlock_irqrestore(&adapter->hw_lock, flags); + return val; +} + +static void __fcpnp_write_ctrl(struct fritz_bcs *bcs, int which) +{ + struct fritz_adapter *adapter = bcs->adapter; + unsigned char idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1; + + DBG(0x40, "hdlc %c wr%x ctrl %x", + 'A' + bcs->channel, which, bcs->ctrl.ctrl); + + outb(idx, adapter->io + AVM_INDEX); + if (which & 4) + outb(bcs->ctrl.sr.mode, + adapter->io + AVM_DATA + HDLC_STATUS + 2); + if (which & 2) + outb(bcs->ctrl.sr.xml, + adapter->io + AVM_DATA + HDLC_STATUS + 1); + if (which & 1) + outb(bcs->ctrl.sr.cmd, + adapter->io + AVM_DATA + HDLC_STATUS + 0); +} + +static void fcpnp_write_ctrl(struct fritz_bcs *bcs, int which) +{ + struct fritz_adapter *adapter = bcs->adapter; + unsigned long flags; + + spin_lock_irqsave(&adapter->hw_lock, flags); + __fcpnp_write_ctrl(bcs, which); + spin_unlock_irqrestore(&adapter->hw_lock, flags); +} + +// ---------------------------------------------------------------------- + +static inline void B_L1L2(struct fritz_bcs *bcs, int pr, void *arg) +{ + struct hisax_if *ifc = (struct hisax_if *) &bcs->b_if; + + DBG(2, "pr %#x", pr); + ifc->l1l2(ifc, pr, arg); +} + +static void hdlc_fill_fifo(struct fritz_bcs *bcs) +{ + struct fritz_adapter *adapter = bcs->adapter; + struct sk_buff *skb = bcs->tx_skb; + int count; + int fifo_size = 32; + unsigned long flags; + unsigned char *p; + + DBG(0x40, "hdlc_fill_fifo"); + + if (!skb) + BUG(); + + if (skb->len == 0) + BUG(); + + bcs->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->ctrl.sr.cmd |= HDLC_CMD_XME; + } + DBG(0x40, "hdlc_fill_fifo %d/%d", count, bcs->tx_skb->len); + p = bcs->tx_skb->data; + skb_pull(bcs->tx_skb, count); + bcs->tx_cnt += count; + bcs->ctrl.sr.xml = ((count == fifo_size) ? 0 : count); + + switch (adapter->type) { + case AVM_FRITZ_PCI: + spin_lock_irqsave(&adapter->hw_lock, flags); + // sets the correct AVM_INDEX, too + __fcpci_write_ctrl(bcs, 3); + outsl(adapter->io + AVM_DATA + HDLC_FIFO, + p, (count + 3) / 4); + spin_unlock_irqrestore(&adapter->hw_lock, flags); + break; + case AVM_FRITZ_PCIV2: + fcpci2_write_ctrl(bcs, 3); + outsl(adapter->io + + (bcs->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1), + p, (count + 3) / 4); + break; + case AVM_FRITZ_PNP: + spin_lock_irqsave(&adapter->hw_lock, flags); + // sets the correct AVM_INDEX, too + __fcpnp_write_ctrl(bcs, 3); + outsb(adapter->io + AVM_DATA, p, count); + spin_unlock_irqrestore(&adapter->hw_lock, flags); + break; + } +} + +static inline void hdlc_empty_fifo(struct fritz_bcs *bcs, int count) +{ + struct fritz_adapter *adapter = bcs->adapter; + unsigned char *p; + unsigned char idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1; + + DBG(0x10, "hdlc_empty_fifo %d", count); + if (bcs->rcvidx + count > HSCX_BUFMAX) { + DBG(0x10, "hdlc_empty_fifo: incoming packet too large"); + return; + } + p = bcs->rcvbuf + bcs->rcvidx; + bcs->rcvidx += count; + switch (adapter->type) { + case AVM_FRITZ_PCI: + spin_lock(&adapter->hw_lock); + outl(idx, adapter->io + AVM_INDEX); + insl(adapter->io + AVM_DATA + HDLC_FIFO, + p, (count + 3) / 4); + spin_unlock(&adapter->hw_lock); + break; + case AVM_FRITZ_PCIV2: + insl(adapter->io + + (bcs->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1), + p, (count + 3) / 4); + break; + case AVM_FRITZ_PNP: + spin_lock(&adapter->hw_lock); + outb(idx, adapter->io + AVM_INDEX); + insb(adapter->io + AVM_DATA, p, count); + spin_unlock(&adapter->hw_lock); + break; + } +} + +static inline void hdlc_rpr_irq(struct fritz_bcs *bcs, u32 stat) +{ + struct fritz_adapter *adapter = bcs->adapter; + struct sk_buff *skb; + int len; + + if (stat & HDLC_STAT_RDO) { + DBG(0x10, "RDO"); + bcs->ctrl.sr.xml = 0; + bcs->ctrl.sr.cmd |= HDLC_CMD_RRS; + adapter->write_ctrl(bcs, 1); + bcs->ctrl.sr.cmd &= ~HDLC_CMD_RRS; + adapter->write_ctrl(bcs, 1); + bcs->rcvidx = 0; + return; + } + + len = (stat & HDLC_STAT_RML_MASK) >> 8; + if (len == 0) + 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)) { + skb = dev_alloc_skb(bcs->rcvidx); + if (!skb) { + printk(KERN_WARNING "HDLC: receive out of memory\n"); + } else { + memcpy(skb_put(skb, bcs->rcvidx), bcs->rcvbuf, + bcs->rcvidx); + DBG_SKB(1, skb); + B_L1L2(bcs, PH_DATA | INDICATION, skb); + } + bcs->rcvidx = 0; + } else { + DBG(0x10, "ch%d invalid frame %#x", + bcs->channel, stat); + bcs->rcvidx = 0; + } + } +} + +static inline void hdlc_xdu_irq(struct fritz_bcs *bcs) +{ + struct fritz_adapter *adapter = bcs->adapter; + + /* Here we lost an TX interrupt, so + * restart transmitting the whole frame. + */ + bcs->ctrl.sr.xml = 0; + bcs->ctrl.sr.cmd |= HDLC_CMD_XRS; + adapter->write_ctrl(bcs, 1); + bcs->ctrl.sr.cmd &= ~HDLC_CMD_XRS; + adapter->write_ctrl(bcs, 1); + + if (!bcs->tx_skb) { + DBG(0x10, "XDU without skb"); + return; + } + skb_push(bcs->tx_skb, bcs->tx_cnt); + bcs->tx_cnt = 0; +} + +static inline void hdlc_xpr_irq(struct fritz_bcs *bcs) +{ + struct sk_buff *skb; + + skb = bcs->tx_skb; + if (!skb) + return; + + if (skb->len) { + hdlc_fill_fifo(bcs); + return; + } + bcs->tx_cnt = 0; + bcs->tx_skb = NULL; + B_L1L2(bcs, PH_DATA | CONFIRM, (void *) skb->truesize); + dev_kfree_skb_irq(skb); +} + +static void hdlc_irq(struct fritz_bcs *bcs, u32 stat) +{ + DBG(0x10, "ch%d stat %#x", bcs->channel, stat); + if (stat & HDLC_INT_RPR) { + DBG(0x10, "RPR"); + hdlc_rpr_irq(bcs, stat); + } + if (stat & HDLC_INT_XDU) { + DBG(0x10, "XDU"); + hdlc_xdu_irq(bcs); + } + if (stat & HDLC_INT_XPR) { + DBG(0x10, "XPR"); + hdlc_xpr_irq(bcs); + } +} + +static inline void hdlc_interrupt(struct fritz_adapter *adapter) +{ + int nr; + u32 stat; + + for (nr = 0; nr < 2; nr++) { + stat = adapter->read_hdlc_status(adapter, nr); + DBG(0x10, "HDLC %c stat %#x", 'A' + nr, stat); + if (stat & HDLC_INT_MASK) + hdlc_irq(&adapter->bcs[nr], stat); + } +} + +static void modehdlc(struct fritz_bcs *bcs, int mode) +{ + struct fritz_adapter *adapter = bcs->adapter; + + DBG(0x40, "hdlc %c mode %d --> %d", + 'A' + bcs->channel, bcs->mode, mode); + + if (bcs->mode == mode) + return; + + bcs->ctrl.ctrl = 0; + bcs->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; + switch (mode) { + case L1_MODE_NULL: + bcs->ctrl.sr.mode = HDLC_MODE_TRANS; + adapter->write_ctrl(bcs, 5); + break; + case L1_MODE_TRANS: + bcs->ctrl.sr.mode = HDLC_MODE_TRANS; + adapter->write_ctrl(bcs, 5); + bcs->ctrl.sr.cmd = HDLC_CMD_XRS; + adapter->write_ctrl(bcs, 1); + bcs->ctrl.sr.cmd = 0; + break; + case L1_MODE_HDLC: + bcs->ctrl.sr.mode = HDLC_MODE_ITF_FLG; + adapter->write_ctrl(bcs, 5); + bcs->ctrl.sr.cmd = HDLC_CMD_XRS; + adapter->write_ctrl(bcs, 1); + bcs->ctrl.sr.cmd = 0; + break; + } + bcs->mode = mode; +} + +static void fritz_b_l2l1(struct hisax_if *ifc, int pr, void *arg) +{ + struct fritz_bcs *bcs = ifc->priv; + struct sk_buff *skb = arg; + int mode; + + DBG(0x10, "pr %#x", pr); + + switch (pr) { + case PH_DATA | REQUEST: + if (bcs->tx_skb) + BUG(); + + bcs->tx_skb = skb; + DBG_SKB(1, skb); + hdlc_fill_fifo(bcs); + break; + case PH_ACTIVATE | REQUEST: + mode = (int) arg; + DBG(4,"B%d,PH_ACTIVATE_REQUEST %d", bcs->channel + 1, mode); + modehdlc(bcs, mode); + B_L1L2(bcs, PH_ACTIVATE | INDICATION, NULL); + break; + case PH_DEACTIVATE | REQUEST: + DBG(4,"B%d,PH_DEACTIVATE_REQUEST", bcs->channel + 1); + modehdlc(bcs, L1_MODE_NULL); + B_L1L2(bcs, PH_DEACTIVATE | INDICATION, NULL); + break; + } +} + +// ---------------------------------------------------------------------- + +static void fcpci2_irq(int intno, void *dev, struct pt_regs *regs) +{ + struct fritz_adapter *adapter = dev; + unsigned char val; + + val = inb(adapter->io + AVM_STATUS0); + if (!(val & AVM_STATUS0_IRQ_MASK)) + /* hopefully a shared IRQ reqest */ + return; + DBG(2, "STATUS0 %#x", val); + if (val & AVM_STATUS0_IRQ_ISAC) + isacsx_interrupt(&adapter->isac); + + if (val & AVM_STATUS0_IRQ_HDLC) + hdlc_interrupt(adapter); +} + +static void fcpci_irq(int intno, void *dev, struct pt_regs *regs) +{ + struct fritz_adapter *adapter = dev; + unsigned char sval; + + sval = inb(adapter->io + 2); + if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) + /* possibly a shared IRQ reqest */ + return; + DBG(2, "sval %#x", sval); + if (!(sval & AVM_STATUS0_IRQ_ISAC)) + isac_interrupt(&adapter->isac); + + if (!(sval & AVM_STATUS0_IRQ_HDLC)) + hdlc_interrupt(adapter); +} + +// ---------------------------------------------------------------------- + +static inline void fcpci2_init(struct fritz_adapter *adapter) +{ + outb(AVM_STATUS0_RES_TIMER, adapter->io + AVM_STATUS0); + outb(AVM_STATUS0_ENA_IRQ, adapter->io + AVM_STATUS0); + +} + +static inline void fcpci_init(struct fritz_adapter *adapter) +{ + outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | + AVM_STATUS0_ENA_IRQ, adapter->io + AVM_STATUS0); +} + +// ---------------------------------------------------------------------- + +static int __devinit fcpcipnp_setup(struct fritz_adapter *adapter) +{ + u32 val = 0; + struct pci_dev *pdev = adapter->pci_dev; + int retval; + + DBG(1,""); + + isac_init(&adapter->isac); // FIXME is this okay now + + retval = -EBUSY; + if (!request_region(adapter->io, 32, "hisax_fcpcipnp")) + goto err; + + switch (adapter->type) { + case AVM_FRITZ_PCIV2: + retval = request_irq(pdev->irq, fcpci2_irq, SA_SHIRQ, + "hisax_fcpcipnp", adapter); + break; + case AVM_FRITZ_PCI: + retval = request_irq(pdev->irq, fcpci_irq, SA_SHIRQ, + "hisax_fcpcipnp", adapter); + break; + case AVM_FRITZ_PNP: + retval = request_irq(pdev->irq, fcpci_irq, 0, + "hisax_fcpcipnp", adapter); + break; + } + if (retval) + goto err_region; + + switch (adapter->type) { + case AVM_FRITZ_PCIV2: + case AVM_FRITZ_PCI: + val = inl(adapter->io); + break; + case AVM_FRITZ_PNP: + val = inb(adapter->io); + val |= inb(adapter->io + 1) << 8; + break; + } + + DBG(1, "stat %#x Class %X Rev %d", + val, val & 0xff, (val>>8) & 0xff); + + spin_lock_init(&adapter->hw_lock); + adapter->isac.priv = adapter; + switch (adapter->type) { + case AVM_FRITZ_PCIV2: + adapter->isac.read_isac = &fcpci2_read_isac;; + adapter->isac.write_isac = &fcpci2_write_isac; + adapter->isac.read_isac_fifo = &fcpci2_read_isac_fifo; + adapter->isac.write_isac_fifo = &fcpci2_write_isac_fifo; + + adapter->read_hdlc_status = &fcpci2_read_hdlc_status; + adapter->write_ctrl = &fcpci2_write_ctrl; + break; + case AVM_FRITZ_PCI: + adapter->isac.read_isac = &fcpci_read_isac;; + adapter->isac.write_isac = &fcpci_write_isac; + adapter->isac.read_isac_fifo = &fcpci_read_isac_fifo; + adapter->isac.write_isac_fifo = &fcpci_write_isac_fifo; + + adapter->read_hdlc_status = &fcpci_read_hdlc_status; + adapter->write_ctrl = &fcpci_write_ctrl; + break; + case AVM_FRITZ_PNP: + adapter->isac.read_isac = &fcpci_read_isac;; + adapter->isac.write_isac = &fcpci_write_isac; + adapter->isac.read_isac_fifo = &fcpci_read_isac_fifo; + adapter->isac.write_isac_fifo = &fcpci_write_isac_fifo; + + adapter->read_hdlc_status = &fcpnp_read_hdlc_status; + adapter->write_ctrl = &fcpnp_write_ctrl; + break; + } + + // Reset + outb(0, adapter->io + AVM_STATUS0); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(50 * HZ / 1000); // 50 msec + outb(AVM_STATUS0_RESET, adapter->io + AVM_STATUS0); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(50 * HZ / 1000); // 50 msec + outb(0, adapter->io + AVM_STATUS0); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(10 * HZ / 1000); // 10 msec + + switch (adapter->type) { + case AVM_FRITZ_PCIV2: + fcpci2_init(adapter); + isacsx_setup(&adapter->isac); + break; + case AVM_FRITZ_PCI: + case AVM_FRITZ_PNP: + fcpci_init(adapter); + isac_setup(&adapter->isac); + break; + } + val = adapter->read_hdlc_status(adapter, 0); + DBG(0x20, "HDLC A STA %x", val); + val = adapter->read_hdlc_status(adapter, 1); + DBG(0x20, "HDLC B STA %x", val); + + adapter->bcs[0].mode = -1; + adapter->bcs[1].mode = -1; + modehdlc(&adapter->bcs[0], L1_MODE_NULL); + modehdlc(&adapter->bcs[1], L1_MODE_NULL); + + return 0; + + err_region: + release_region(adapter->io, 32); + err: + return retval; +} + +static void __devexit fcpcipnp_release(struct fritz_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pci_dev; + + DBG(1,""); + + outb(0, adapter->io + AVM_STATUS0); + free_irq(pdev->irq, adapter); + release_region(adapter->io, 32); + + switch (adapter->type) { + case AVM_FRITZ_PCI: + case AVM_FRITZ_PCIV2: + pci_disable_device(pdev); + break; + case AVM_FRITZ_PNP: + pdev->deactivate(pdev); + break; + } +} + +// ---------------------------------------------------------------------- + +static struct fritz_adapter * __devinit +new_adapter(struct pci_dev *pdev) +{ + struct fritz_adapter *adapter; + struct hisax_b_if *b_if[2]; + int i; + + adapter = kmalloc(sizeof(struct fritz_adapter), GFP_KERNEL); + if (!adapter) + return NULL; + + memset(adapter, 0, sizeof(struct fritz_adapter)); + + adapter->pci_dev = pdev; + + adapter->isac.hisax_d_if.ifc.priv = &adapter->isac; + adapter->isac.hisax_d_if.ifc.l2l1 = isac_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 = fritz_b_l2l1; + } + list_add(&adapter->list, &adapter_list); + + pci_set_drvdata(pdev, adapter); + + for (i = 0; i < 2; i++) + b_if[i] = &adapter->bcs[i].b_if; + + hisax_register(&adapter->isac.hisax_d_if, b_if, "fcpcipnp", protocol); + + return adapter; +} + +static void delete_adapter(struct fritz_adapter *adapter) +{ + hisax_unregister(&adapter->isac.hisax_d_if); + list_del(&adapter->list); + kfree(adapter); +} + +static int __devinit fcpci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct fritz_adapter *adapter; + int retval; + + printk(KERN_INFO "hisax_fcpcipnp: found adapter %s at %s\n", + (char *) ent->driver_data, pdev->slot_name); + + retval = -ENOMEM; + adapter = new_adapter(pdev); + if (!adapter) + goto err; + + if (pdev->device == 0x0e00) + adapter->type = AVM_FRITZ_PCIV2; + else + adapter->type = AVM_FRITZ_PCI; + + retval = pci_enable_device(pdev); + if (retval) + goto err_free; + adapter->io = pci_resource_start(pdev, 1); + + retval = fcpcipnp_setup(adapter); + if (retval) + goto err_free; + + return 0; + + err_free: + delete_adapter(adapter); + err: + return retval; +} + +static int __devinit fcpnp_probe(struct pci_dev *pdev, + const struct isapnp_device_id *ent) +{ + struct fritz_adapter *adapter; + int retval; + + printk(KERN_INFO "hisax_fcpcipnp: found adapter %s\n", + (char *) ent->driver_data); + + retval = -ENOMEM; + adapter = new_adapter(pdev); + if (!adapter) + goto err; + + adapter->type = AVM_FRITZ_PNP; + + pdev->prepare(pdev); + pdev->deactivate(pdev); // why? + pdev->activate(pdev); + adapter->io = pdev->resource[0].start; + pdev->irq = pdev->irq_resource[0].start; + + retval = fcpcipnp_setup(adapter); + if (retval) + goto err_free; + + return 0; + + err_free: + delete_adapter(adapter); + err: + return retval; +} + +static void __devexit fcpcipnp_remove(struct pci_dev *pdev) +{ + struct fritz_adapter *adapter = pci_get_drvdata(pdev); + + fcpcipnp_release(adapter); + delete_adapter(adapter); +} + +static struct pci_driver fcpci_driver = { + name: "fcpci", + probe: fcpci_probe, + remove: fcpcipnp_remove, + id_table: fcpci_ids, +}; + +static struct isapnp_driver fcpnp_driver = { + name: "fcpnp", + probe: fcpnp_probe, + remove: fcpcipnp_remove, + id_table: fcpnp_ids, +}; + +static LIST_HEAD(isapnp_drivers); + +static int __init hisax_fcpci_init(void) +{ + int retval, pci_nr_found; + + printk(KERN_INFO "hisax_fcpcipnp: Fritz!PCI/PnP ISDN driver v0.0.1\n"); + + retval = pci_register_driver(&fcpci_driver); + if (retval < 0) + goto out; + pci_nr_found = retval; + + retval = isapnp_register_driver(&fcpnp_driver); + if (retval < 0) + goto out_unregister_pci; + +#if !defined(CONFIG_HOTPLUG) && defined(MODULE) + if (pci_nr_found + retval == 0) { + retval = -ENODEV; + goto out_unregister_isapnp; +#endif + return 0; + +#if !defined(CONFIG_HOTPLUG) && defined(MODULE) + out_unregister_isapnp: + isapnp_unregister_driver(&fcpnp_driver); +#endif + out_unregister_pci: + pci_unregister_driver(&fcpci_driver); + out: + return retval; +} + +static void __exit hisax_fcpci_exit(void) +{ + isapnp_unregister_driver(&fcpnp_driver); + pci_unregister_driver(&fcpci_driver); +} + +module_init(hisax_fcpci_init); +module_exit(hisax_fcpci_exit); + +#include "hisax_isapnp.c" diff --git a/drivers/isdn/hisax/hisax_fcpcipnp.h b/drivers/isdn/hisax/hisax_fcpcipnp.h new file mode 100644 index 00000000..b8f71e14 --- /dev/null +++ b/drivers/isdn/hisax/hisax_fcpcipnp.h @@ -0,0 +1,59 @@ +#include "hisax_if.h" +#include "hisax_isac.h" +#include + +#define HSCX_BUFMAX 4096 + +enum { + AVM_FRITZ_PCI, + AVM_FRITZ_PNP, + AVM_FRITZ_PCIV2, +}; + +struct hdlc_stat_reg { +#ifdef __BIG_ENDIAN + u_char fill __attribute__((packed)); + u_char mode __attribute__((packed)); + u_char xml __attribute__((packed)); + u_char cmd __attribute__((packed)); +#else + u_char cmd __attribute__((packed)); + u_char xml __attribute__((packed)); + u_char mode __attribute__((packed)); + u_char fill __attribute__((packed)); +#endif +}; + +struct fritz_bcs { + struct hisax_b_if b_if; + struct fritz_adapter *adapter; + int mode; + int channel; + + union { + u_int ctrl; + struct hdlc_stat_reg sr; + } ctrl; + u_int stat; + int rcvidx; + u_char rcvbuf[HSCX_BUFMAX]; /* B-Channel receive Buffer */ + + int tx_cnt; /* B-Channel transmit counter */ + struct sk_buff *tx_skb; /* B-Channel transmit Buffer */ +}; + +struct fritz_adapter { + struct list_head list; + struct pci_dev *pci_dev; + + int type; + spinlock_t hw_lock; + unsigned int io; + struct isac isac; + + struct fritz_bcs bcs[2]; + + u32 (*read_hdlc_status) (struct fritz_adapter *adapter, int nr); + void (*write_ctrl) (struct fritz_bcs *bcs, int which); +}; + diff --git a/drivers/isdn/hisax/hisax_if.h b/drivers/isdn/hisax/hisax_if.h index a6b46fd4..5dfdb682 100644 --- a/drivers/isdn/hisax/hisax_if.h +++ b/drivers/isdn/hisax/hisax_if.h @@ -1,6 +1,8 @@ #ifndef __HISAX_IF_H__ #define __HISAX_IF_H__ +#include + #define REQUEST 0 #define CONFIRM 1 #define INDICATION 2 @@ -10,6 +12,7 @@ #define PH_DEACTIVATE 0x0110 #define PH_DATA 0x0120 #define PH_PULL 0x0130 +#define PH_DATA_E 0x0140 #define L1_MODE_NULL 0 #define L1_MODE_TRANS 1 @@ -39,6 +42,8 @@ struct hisax_d_if { // private to hisax struct IsdnCardState *cs; struct hisax_b_if *b_if[2]; + struct sk_buff_head erq; + long ph_state; }; int hisax_register(struct hisax_d_if *hisax_if, struct hisax_b_if *b_if[], diff --git a/drivers/isdn/hisax/hisax_isac.c b/drivers/isdn/hisax/hisax_isac.c new file mode 100644 index 00000000..adafdc95 --- /dev/null +++ b/drivers/isdn/hisax/hisax_isac.c @@ -0,0 +1,891 @@ +/* + * Driver for ISAC-S and ISAC-SX + * ISDN Subscriber Access Controller for Terminals + * + * Author Kai Germaschewski + * Copyright 2001 by Kai Germaschewski + * 2001 by Karsten Keil + * + * based upon Karsten Keil's original isac.c driver + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * Thanks to Wizard Computersysteme GmbH, Bremervoerde and + * SoHaNet Technology GmbH, Berlin + * for supporting the development of this driver + */ + +/* TODO: + * specifically handle level vs edge triggered? + */ + +#include +#include +#include +#include "hisax_isac.h" + +// debugging cruft + +#define __debug_variable debug +#include "hisax_debug.h" + +#ifdef CONFIG_HISAX_DEBUG +static int debug = 1; +MODULE_PARM(debug, "i"); + +static char *ISACVer[] __devinitdata = { + "2086/2186 V1.1", + "2085 B1", + "2085 B2", + "2085 V2.3" +}; +#endif + +#define DBG_WARN 0x0001 +#define DBG_IRQ 0x0002 +#define DBG_L1M 0x0004 +#define DBG_PR 0x0008 +#define DBG_RFIFO 0x0100 +#define DBG_RPACKET 0x0200 +#define DBG_XFIFO 0x1000 +#define DBG_XPACKET 0x2000 + +// we need to distinguish ISAC-S and ISAC-SX +#define TYPE_ISAC 0x00 +#define TYPE_ISACSX 0x01 + +// registers etc. +#define ISAC_MASK 0x20 +#define ISAC_ISTA 0x20 +#define ISAC_ISTA_EXI 0x01 +#define ISAC_ISTA_SIN 0x02 +#define ISAC_ISTA_CISQ 0x04 +#define ISAC_ISTA_XPR 0x10 +#define ISAC_ISTA_RSC 0x20 +#define ISAC_ISTA_RPF 0x40 +#define ISAC_ISTA_RME 0x80 + +#define ISAC_STAR 0x21 +#define ISAC_CMDR 0x21 +#define ISAC_CMDR_XRES 0x01 +#define ISAC_CMDR_XME 0x02 +#define ISAC_CMDR_XTF 0x08 +#define ISAC_CMDR_RRES 0x40 +#define ISAC_CMDR_RMC 0x80 + +#define ISAC_EXIR 0x24 +#define ISAC_EXIR_MOS 0x04 +#define ISAC_EXIR_XDU 0x40 +#define ISAC_EXIR_XMR 0x80 + +#define ISAC_ADF2 0x39 +#define ISAC_SPCR 0x30 +#define ISAC_ADF1 0x38 + +#define ISAC_CIR0 0x31 +#define ISAC_CIX0 0x31 +#define ISAC_CIR0_CIC0 0x02 +#define ISAC_CIR0_CIC1 0x01 + +#define ISAC_CIR1 0x33 +#define ISAC_CIX1 0x33 +#define ISAC_STCR 0x37 +#define ISAC_MODE 0x22 + +#define ISAC_RSTA 0x27 +#define ISAC_RSTA_RDO 0x40 +#define ISAC_RSTA_CRC 0x20 +#define ISAC_RSTA_RAB 0x10 + +#define ISAC_RBCL 0x25 +#define ISAC_RBCH 0x2A +#define ISAC_TIMR 0x23 +#define ISAC_SQXR 0x3b +#define ISAC_MOSR 0x3a +#define ISAC_MOCR 0x3a +#define ISAC_MOR0 0x32 +#define ISAC_MOX0 0x32 +#define ISAC_MOR1 0x34 +#define ISAC_MOX1 0x34 + +#define ISAC_RBCH_XAC 0x80 + +#define ISAC_CMD_TIM 0x0 +#define ISAC_CMD_RES 0x1 +#define ISAC_CMD_SSP 0x2 +#define ISAC_CMD_SCP 0x3 +#define ISAC_CMD_AR8 0x8 +#define ISAC_CMD_AR10 0x9 +#define ISAC_CMD_ARL 0xa +#define ISAC_CMD_DI 0xf + +#define ISACSX_MASK 0x60 +#define ISACSX_ISTA 0x60 +#define ISACSX_ISTA_ICD 0x01 +#define ISACSX_ISTA_CIC 0x10 + +#define ISACSX_MASKD 0x20 +#define ISACSX_ISTAD 0x20 +#define ISACSX_ISTAD_XDU 0x04 +#define ISACSX_ISTAD_XMR 0x08 +#define ISACSX_ISTAD_XPR 0x10 +#define ISACSX_ISTAD_RFO 0x20 +#define ISACSX_ISTAD_RPF 0x40 +#define ISACSX_ISTAD_RME 0x80 + +#define ISACSX_CMDRD 0x21 +#define ISACSX_CMDRD_XRES 0x01 +#define ISACSX_CMDRD_XME 0x02 +#define ISACSX_CMDRD_XTF 0x08 +#define ISACSX_CMDRD_RRES 0x40 +#define ISACSX_CMDRD_RMC 0x80 + +#define ISACSX_MODED 0x22 + +#define ISACSX_RBCLD 0x26 + +#define ISACSX_RSTAD 0x28 +#define ISACSX_RSTAD_RAB 0x10 +#define ISACSX_RSTAD_CRC 0x20 +#define ISACSX_RSTAD_RDO 0x40 +#define ISACSX_RSTAD_VFR 0x80 + +#define ISACSX_CIR0 0x2e +#define ISACSX_CIR0_CIC0 0x08 +#define ISACSX_CIX0 0x2e + +#define ISACSX_TR_CONF0 0x30 + +#define ISACSX_TR_CONF2 0x32 + +static struct Fsm l1fsm; + +enum { + ST_L1_RESET, + ST_L1_F3_PDOWN, + ST_L1_F3_PUP, + ST_L1_F3_PEND_DEACT, + 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_RESET", + "ST_L1_F3_PDOWN", + "ST_L1_F3_PUP", + "ST_L1_F3_PEND_DEACT", + "ST_L1_F4", + "ST_L1_F5", + "ST_L1_F6", + "ST_L1_F7", + "ST_L1_F8", +}; + +enum { + EV_PH_DR, // 0000 + EV_PH_RES, // 0001 + EV_PH_TMA, // 0010 + EV_PH_SLD, // 0011 + EV_PH_RSY, // 0100 + EV_PH_DR6, // 0101 + EV_PH_EI, // 0110 + EV_PH_PU, // 0111 + EV_PH_AR, // 1000 + EV_PH_9, // 1001 + EV_PH_ARL, // 1010 + EV_PH_CVR, // 1011 + EV_PH_AI8, // 1100 + EV_PH_AI10, // 1101 + EV_PH_AIL, // 1110 + EV_PH_DC, // 1111 + EV_PH_ACTIVATE_REQ, + EV_PH_DEACTIVATE_REQ, + EV_TIMER3, +}; + +#define L1_EVENT_COUNT (EV_TIMER3 + 1) + +static char *strL1Event[] = +{ + "EV_PH_DR", // 0000 + "EV_PH_RES", // 0001 + "EV_PH_TMA", // 0010 + "EV_PH_SLD", // 0011 + "EV_PH_RSY", // 0100 + "EV_PH_DR6", // 0101 + "EV_PH_EI", // 0110 + "EV_PH_PU", // 0111 + "EV_PH_AR", // 1000 + "EV_PH_9", // 1001 + "EV_PH_ARL", // 1010 + "EV_PH_CVR", // 1011 + "EV_PH_AI8", // 1100 + "EV_PH_AI10", // 1101 + "EV_PH_AIL", // 1110 + "EV_PH_DC", // 1111 + "EV_PH_ACTIVATE_REQ", + "EV_PH_DEACTIVATE_REQ", + "EV_TIMER3", +}; + +static inline void D_L1L2(struct isac *isac, int pr, void *arg) +{ + struct hisax_if *ifc = (struct hisax_if *) &isac->hisax_d_if; + + DBG(DBG_PR, "pr %#x", pr); + ifc->l1l2(ifc, pr, arg); +} + +static void ph_command(struct isac *isac, unsigned int command) +{ + DBG(DBG_L1M, "ph_command %#x", command); + switch (isac->type) { + case TYPE_ISAC: + isac->write_isac(isac, ISAC_CIX0, (command << 2) | 3); + break; + case TYPE_ISACSX: + isac->write_isac(isac, ISACSX_CIX0, (command << 4) | (7 << 1)); + break; + } +} + +// ---------------------------------------------------------------------- + +static void l1_di(struct FsmInst *fi, int event, void *arg) +{ + struct isac *isac = fi->userdata; + + FsmChangeState(fi, ST_L1_RESET); + ph_command(isac, ISAC_CMD_DI); +} + +static void l1_di_deact_ind(struct FsmInst *fi, int event, void *arg) +{ + struct isac *isac = fi->userdata; + + FsmChangeState(fi, ST_L1_RESET); + D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL); + ph_command(isac, ISAC_CMD_DI); +} + +static void l1_go_f3pdown(struct FsmInst *fi, int event, void *arg) +{ + FsmChangeState(fi, ST_L1_F3_PDOWN); +} + +static void l1_go_f3pend_deact_ind(struct FsmInst *fi, int event, void *arg) +{ + struct isac *isac = fi->userdata; + + FsmChangeState(fi, ST_L1_F3_PEND_DEACT); + D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL); + ph_command(isac, ISAC_CMD_DI); +} + +static void l1_go_f3pend(struct FsmInst *fi, int event, void *arg) +{ + struct isac *isac = fi->userdata; + + FsmChangeState(fi, ST_L1_F3_PEND_DEACT); + ph_command(isac, ISAC_CMD_DI); +} + +static void l1_go_f4(struct FsmInst *fi, int event, void *arg) +{ + FsmChangeState(fi, ST_L1_F4); +} + +static void l1_go_f5(struct FsmInst *fi, int event, void *arg) +{ + FsmChangeState(fi, ST_L1_F5); +} + +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 isac *isac = fi->userdata; + + FsmChangeState(fi, ST_L1_F6); + D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL); +} + +static void l1_go_f7_act_ind(struct FsmInst *fi, int event, void *arg) +{ + struct isac *isac = fi->userdata; + + FsmDelTimer(&isac->timer, 0); + FsmChangeState(fi, ST_L1_F7); + ph_command(isac, ISAC_CMD_AR8); + D_L1L2(isac, 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 isac *isac = fi->userdata; + + FsmChangeState(fi, ST_L1_F8); + D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL); +} + +static void l1_ar8(struct FsmInst *fi, int event, void *arg) +{ + struct isac *isac = fi->userdata; + + FsmRestartTimer(&isac->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2); + ph_command(isac, ISAC_CMD_AR8); +} + +static void l1_timer3(struct FsmInst *fi, int event, void *arg) +{ + struct isac *isac = fi->userdata; + + ph_command(isac, ISAC_CMD_DI); + D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL); +} + +// state machines according to data sheet PSB 2186 / 3186 + +static struct FsmNode L1FnList[] __initdata = +{ + {ST_L1_RESET, EV_PH_RES, l1_di}, + {ST_L1_RESET, EV_PH_EI, l1_di}, + {ST_L1_RESET, EV_PH_DC, l1_go_f3pdown}, + {ST_L1_RESET, EV_PH_AR, l1_go_f6}, + {ST_L1_RESET, EV_PH_AI8, l1_go_f7_act_ind}, + + {ST_L1_F3_PDOWN, EV_PH_RES, l1_di}, + {ST_L1_F3_PDOWN, EV_PH_EI, l1_di}, + {ST_L1_F3_PDOWN, EV_PH_AR, l1_go_f6}, + {ST_L1_F3_PDOWN, EV_PH_RSY, l1_go_f5}, + {ST_L1_F3_PDOWN, EV_PH_PU, l1_go_f4}, + {ST_L1_F3_PDOWN, EV_PH_AI8, l1_go_f7_act_ind}, + {ST_L1_F3_PDOWN, EV_PH_ACTIVATE_REQ, l1_ar8}, + {ST_L1_F3_PDOWN, EV_TIMER3, l1_timer3}, + + {ST_L1_F3_PEND_DEACT, EV_PH_RES, l1_di}, + {ST_L1_F3_PEND_DEACT, EV_PH_EI, l1_di}, + {ST_L1_F3_PEND_DEACT, EV_PH_DC, l1_go_f3pdown}, + {ST_L1_F3_PEND_DEACT, EV_PH_RSY, l1_go_f5}, + {ST_L1_F3_PEND_DEACT, EV_PH_AR, l1_go_f6}, + {ST_L1_F3_PEND_DEACT, EV_PH_AI8, l1_go_f7_act_ind}, + + {ST_L1_F4, EV_PH_RES, l1_di}, + {ST_L1_F4, EV_PH_EI, l1_di}, + {ST_L1_F4, EV_PH_RSY, l1_go_f5}, + {ST_L1_F4, EV_PH_AI8, l1_go_f7_act_ind}, + {ST_L1_F4, EV_TIMER3, l1_timer3}, + {ST_L1_F4, EV_PH_DC, l1_go_f3pdown}, + + {ST_L1_F5, EV_PH_RES, l1_di}, + {ST_L1_F5, EV_PH_EI, l1_di}, + {ST_L1_F5, EV_PH_AR, l1_go_f6}, + {ST_L1_F5, EV_PH_AI8, l1_go_f7_act_ind}, + {ST_L1_F5, EV_TIMER3, l1_timer3}, + {ST_L1_F5, EV_PH_DR, l1_go_f3pend}, + {ST_L1_F5, EV_PH_DC, l1_go_f3pdown}, + + {ST_L1_F6, EV_PH_RES, l1_di}, + {ST_L1_F6, EV_PH_EI, l1_di}, + {ST_L1_F6, EV_PH_RSY, l1_go_f8}, + {ST_L1_F6, EV_PH_AI8, l1_go_f7_act_ind}, + {ST_L1_F6, EV_PH_DR6, l1_go_f3pend}, + {ST_L1_F6, EV_TIMER3, l1_timer3}, + {ST_L1_F6, EV_PH_DC, l1_go_f3pdown}, + + {ST_L1_F7, EV_PH_RES, l1_di_deact_ind}, + {ST_L1_F7, EV_PH_EI, l1_di_deact_ind}, + {ST_L1_F7, EV_PH_AR, l1_go_f6_deact_ind}, + {ST_L1_F7, EV_PH_RSY, l1_go_f8_deact_ind}, + {ST_L1_F7, EV_PH_DR, l1_go_f3pend_deact_ind}, + + {ST_L1_F8, EV_PH_RES, l1_di}, + {ST_L1_F8, EV_PH_EI, l1_di}, + {ST_L1_F8, EV_PH_AR, l1_go_f6}, + {ST_L1_F8, EV_PH_DR, l1_go_f3pend}, + {ST_L1_F8, EV_PH_AI8, l1_go_f7_act_ind}, + {ST_L1_F8, EV_TIMER3, l1_timer3}, + {ST_L1_F8, EV_PH_DC, l1_go_f3pdown}, +}; + +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); +} + +static void __devinit isac_version(struct isac *cs) +{ + int val; + + val = cs->read_isac(cs, ISAC_RBCH); + DBG(1, "ISAC version (%x): %s", val, ISACVer[(val >> 5) & 3]); +} + +static void isac_empty_fifo(struct isac *isac, int count) +{ + // this also works for isacsx, since + // CMDR(D) register works the same + u_char *ptr; + + DBG(DBG_IRQ, "count %d", count); + + if ((isac->rcvidx + count) >= MAX_DFRAME_LEN_L1) { + DBG(DBG_WARN, "overrun %d", isac->rcvidx + count); + isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC); + isac->rcvidx = 0; + return; + } + ptr = isac->rcvbuf + isac->rcvidx; + isac->rcvidx += count; + isac->read_isac_fifo(isac, ptr, count); + isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC); + DBG_PACKET(DBG_RFIFO, ptr, count); +} + +static void isac_fill_fifo(struct isac *isac) +{ + // this also works for isacsx, since + // CMDR(D) register works the same + + int count; + unsigned char cmd; + u_char *ptr; + + if (!isac->tx_skb) + BUG(); + + count = isac->tx_skb->len; + if (count <= 0) + BUG(); + + DBG(DBG_IRQ, "count %d", count); + + if (count > 0x20) { + count = 0x20; + cmd = ISAC_CMDR_XTF; + } else { + cmd = ISAC_CMDR_XTF | ISAC_CMDR_XME; + } + + ptr = isac->tx_skb->data; + skb_pull(isac->tx_skb, count); + isac->tx_cnt += count; + DBG_PACKET(DBG_XFIFO, ptr, count); + isac->write_isac_fifo(isac, ptr, count); + isac->write_isac(isac, ISAC_CMDR, cmd); +} + +static void isac_retransmit(struct isac *isac) +{ + if (!isac->tx_skb) { + DBG(DBG_WARN, "no skb"); + return; + } + skb_push(isac->tx_skb, isac->tx_cnt); + isac->tx_cnt = 0; +} + + +static inline void isac_cisq_interrupt(struct isac *isac) +{ + unsigned char val; + + val = isac->read_isac(isac, ISAC_CIR0); + DBG(DBG_IRQ, "CIR0 %#x", val); + if (val & ISAC_CIR0_CIC0) { + DBG(DBG_IRQ, "CODR0 %#x", (val >> 2) & 0xf); + FsmEvent(&isac->l1m, (val >> 2) & 0xf, NULL); + } + if (val & ISAC_CIR0_CIC1) { + val = isac->read_isac(isac, ISAC_CIR1); + DBG(DBG_WARN, "ISAC CIR1 %#x", val ); + } +} + +static inline void isac_rme_interrupt(struct isac *isac) +{ + unsigned char val; + int count; + struct sk_buff *skb; + + val = isac->read_isac(isac, ISAC_RSTA); + if ((val & (ISAC_RSTA_RDO | ISAC_RSTA_CRC | ISAC_RSTA_RAB) ) + != ISAC_RSTA_CRC) { + DBG(DBG_WARN, "RSTA %#x, dropped", val); + isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC); + goto out; + } + + count = isac->read_isac(isac, ISAC_RBCL) & 0x1f; + DBG(DBG_IRQ, "RBCL %#x", count); + if (count == 0) + count = 0x20; + + isac_empty_fifo(isac, count); + count = isac->rcvidx; + if (count < 1) { + DBG(DBG_WARN, "count %d < 1", count); + goto out; + } + + skb = alloc_skb(count, GFP_ATOMIC); + if (!skb) { + DBG(DBG_WARN, "no memory, dropping\n"); + goto out; + } + memcpy(skb_put(skb, count), isac->rcvbuf, count); + DBG_SKB(DBG_RPACKET, skb); + D_L1L2(isac, PH_DATA | INDICATION, skb); + out: + isac->rcvidx = 0; +} + +static inline void isac_xpr_interrupt(struct isac *isac) +{ + if (!isac->tx_skb) + return; + + if (isac->tx_skb->len > 0) { + isac_fill_fifo(isac); + return; + } + dev_kfree_skb_irq(isac->tx_skb); + isac->tx_cnt = 0; + isac->tx_skb = NULL; + D_L1L2(isac, PH_DATA | CONFIRM, NULL); +} + +static inline void isac_exi_interrupt(struct isac *isac) +{ + unsigned char val; + + val = isac->read_isac(isac, ISAC_EXIR); + DBG(2, "EXIR %#x", val); + + if (val & ISAC_EXIR_XMR) { + DBG(DBG_WARN, "ISAC XMR"); + isac_retransmit(isac); + } + if (val & ISAC_EXIR_XDU) { + DBG(DBG_WARN, "ISAC XDU"); + isac_retransmit(isac); + } + if (val & ISAC_EXIR_MOS) { /* MOS */ + DBG(DBG_WARN, "MOS"); + val = isac->read_isac(isac, ISAC_MOSR); + DBG(2, "ISAC MOSR %#x", val); + } +} + +void isac_interrupt(struct isac *isac) +{ + unsigned char val; + + val = isac->read_isac(isac, ISAC_ISTA); + DBG(DBG_IRQ, "ISTA %#x", val); + + if (val & ISAC_ISTA_EXI) { + DBG(DBG_IRQ, "EXI"); + isac_exi_interrupt(isac); + } + if (val & ISAC_ISTA_XPR) { + DBG(DBG_IRQ, "XPR"); + isac_xpr_interrupt(isac); + } + if (val & ISAC_ISTA_RME) { + DBG(DBG_IRQ, "RME"); + isac_rme_interrupt(isac); + } + if (val & ISAC_ISTA_RPF) { + DBG(DBG_IRQ, "RPF"); + isac_empty_fifo(isac, 0x20); + } + if (val & ISAC_ISTA_CISQ) { + DBG(DBG_IRQ, "CISQ"); + isac_cisq_interrupt(isac); + } + if (val & ISAC_ISTA_RSC) { + DBG(DBG_WARN, "RSC"); + } + if (val & ISAC_ISTA_SIN) { + DBG(DBG_WARN, "SIN"); + } +} + +// ====================================================================== + +static inline void isacsx_cic_interrupt(struct isac *isac) +{ + unsigned char val; + + val = isac->read_isac(isac, ISACSX_CIR0); + DBG(DBG_IRQ, "CIR0 %#x", val); + if (val & ISACSX_CIR0_CIC0) { + DBG(DBG_IRQ, "CODR0 %#x", val >> 4); + FsmEvent(&isac->l1m, val >> 4, NULL); + } +} + +static inline void isacsx_rme_interrupt(struct isac *isac) +{ + int count; + struct sk_buff *skb; + unsigned char val; + + val = isac->read_isac(isac, ISACSX_RSTAD); + if ((val & (ISACSX_RSTAD_VFR | + ISACSX_RSTAD_RDO | + ISACSX_RSTAD_CRC | + ISACSX_RSTAD_RAB)) + != (ISACSX_RSTAD_VFR | ISACSX_RSTAD_CRC)) { + DBG(DBG_WARN, "RSTAD %#x, dropped", val); + isac->write_isac(isac, ISACSX_CMDRD, ISACSX_CMDRD_RMC); + goto out; + } + + count = isac->read_isac(isac, ISACSX_RBCLD) & 0x1f; + DBG(DBG_IRQ, "RBCLD %#x", count); + if (count == 0) + count = 0x20; + + isac_empty_fifo(isac, count); + // strip trailing status byte + count = isac->rcvidx - 1; + if (count < 1) { + DBG(DBG_WARN, "count %d < 1", count); + goto out; + } + + skb = dev_alloc_skb(count); + if (!skb) { + DBG(DBG_WARN, "no memory, dropping"); + goto out; + } + memcpy(skb_put(skb, count), isac->rcvbuf, count); + DBG_SKB(DBG_RPACKET, skb); + D_L1L2(isac, PH_DATA | INDICATION, skb); + out: + isac->rcvidx = 0; +} + +static inline void isacsx_xpr_interrupt(struct isac *isac) +{ + if (!isac->tx_skb) + return; + + if (isac->tx_skb->len > 0) { + isac_fill_fifo(isac); + return; + } + dev_kfree_skb_irq(isac->tx_skb); + isac->tx_skb = NULL; + isac->tx_cnt = 0; + D_L1L2(isac, PH_DATA | CONFIRM, NULL); +} + +static inline void isacsx_icd_interrupt(struct isac *isac) +{ + unsigned char val; + + val = isac->read_isac(isac, ISACSX_ISTAD); + DBG(DBG_IRQ, "ISTAD %#x", val); + if (val & ISACSX_ISTAD_XDU) { + DBG(DBG_WARN, "ISTAD XDU"); + isac_retransmit(isac); + } + if (val & ISACSX_ISTAD_XMR) { + DBG(DBG_WARN, "ISTAD XMR"); + isac_retransmit(isac); + } + if (val & ISACSX_ISTAD_XPR) { + DBG(DBG_IRQ, "ISTAD XPR"); + isacsx_xpr_interrupt(isac); + } + if (val & ISACSX_ISTAD_RFO) { + DBG(DBG_WARN, "ISTAD RFO"); + isac->write_isac(isac, ISACSX_CMDRD, ISACSX_CMDRD_RMC); + } + if (val & ISACSX_ISTAD_RME) { + DBG(DBG_IRQ, "ISTAD RME"); + isacsx_rme_interrupt(isac); + } + if (val & ISACSX_ISTAD_RPF) { + DBG(DBG_IRQ, "ISTAD RPF"); + isac_empty_fifo(isac, 0x20); + } +} + +void isacsx_interrupt(struct isac *isac) +{ + unsigned char val; + + val = isac->read_isac(isac, ISACSX_ISTA); + DBG(DBG_IRQ, "ISTA %#x", val); + + if (val & ISACSX_ISTA_ICD) + isacsx_icd_interrupt(isac); + if (val & ISACSX_ISTA_CIC) + isacsx_cic_interrupt(isac); +} + +void __devinit isac_init(struct isac *isac) +{ + isac->tx_skb = NULL; + isac->l1m.fsm = &l1fsm; + isac->l1m.state = ST_L1_RESET; +#ifdef CONFIG_HISAX_DEBUG + isac->l1m.debug = 1; +#else + isac->l1m.debug = 0; +#endif + isac->l1m.userdata = isac; + isac->l1m.printdebug = l1m_debug; + FsmInitTimer(&isac->l1m, &isac->timer); +} + +void __devinit isac_setup(struct isac *isac) +{ + int val, eval; + + isac->type = TYPE_ISAC; + isac_version(isac); + + ph_command(isac, ISAC_CMD_RES); + + isac->write_isac(isac, ISAC_MASK, 0xff); + isac->mocr = 0xaa; + if (test_bit(HW_IOM1, &isac->flags)) { + /* IOM 1 Mode */ + isac->write_isac(isac, ISAC_ADF2, 0x0); + isac->write_isac(isac, ISAC_SPCR, 0xa); + isac->write_isac(isac, ISAC_ADF1, 0x2); + isac->write_isac(isac, ISAC_STCR, 0x70); + isac->write_isac(isac, ISAC_MODE, 0xc9); + } else { + /* IOM 2 Mode */ + if (!isac->adf2) + isac->adf2 = 0x80; + isac->write_isac(isac, ISAC_ADF2, isac->adf2); + isac->write_isac(isac, ISAC_SQXR, 0x2f); + isac->write_isac(isac, ISAC_SPCR, 0x00); + isac->write_isac(isac, ISAC_STCR, 0x70); + isac->write_isac(isac, ISAC_MODE, 0xc9); + isac->write_isac(isac, ISAC_TIMR, 0x00); + isac->write_isac(isac, ISAC_ADF1, 0x00); + } + val = isac->read_isac(isac, ISAC_STAR); + DBG(2, "ISAC STAR %x", val); + val = isac->read_isac(isac, ISAC_MODE); + DBG(2, "ISAC MODE %x", val); + val = isac->read_isac(isac, ISAC_ADF2); + DBG(2, "ISAC ADF2 %x", val); + val = isac->read_isac(isac, ISAC_ISTA); + DBG(2, "ISAC ISTA %x", val); + if (val & 0x01) { + eval = isac->read_isac(isac, ISAC_EXIR); + DBG(2, "ISAC EXIR %x", eval); + } + val = isac->read_isac(isac, ISAC_CIR0); + DBG(2, "ISAC CIR0 %x", val); + FsmEvent(&isac->l1m, (val >> 2) & 0xf, NULL); + + isac->write_isac(isac, ISAC_MASK, 0x0); + /* RESET Receiver and Transmitter */ + isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_XRES | ISAC_CMDR_RRES); +} + +void isacsx_setup(struct isac *isac) +{ + isac->type = TYPE_ISACSX; + // clear LDD + isac->write_isac(isac, ISACSX_TR_CONF0, 0x00); + // enable transmitter + isac->write_isac(isac, ISACSX_TR_CONF2, 0x00); + // transparent mode 0, RAC, stop/go + isac->write_isac(isac, ISACSX_MODED, 0xc9); + // all HDLC IRQ unmasked + isac->write_isac(isac, ISACSX_MASKD, 0x03); + // unmask ICD, CID IRQs + isac->write_isac(isac, ISACSX_MASK, + ~(ISACSX_ISTA_ICD | ISACSX_ISTA_CIC)); +} + +void isac_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg) +{ + struct isac *isac = hisax_d_if->priv; + struct sk_buff *skb = arg; + + DBG(DBG_PR, "pr %#x", pr); + + switch (pr) { + case PH_ACTIVATE | REQUEST: + FsmEvent(&isac->l1m, EV_PH_ACTIVATE_REQ, NULL); + break; + case PH_DEACTIVATE | REQUEST: + FsmEvent(&isac->l1m, EV_PH_DEACTIVATE_REQ, NULL); + break; + case PH_DATA | REQUEST: + DBG(DBG_PR, "PH_DATA REQUEST len %d", skb->len); + DBG_SKB(DBG_XPACKET, skb); + if (isac->l1m.state != ST_L1_F7) { + DBG(1, "L1 wrong state %d\n", isac->l1m.state); + dev_kfree_skb(skb); + break; + } + if (isac->tx_skb) + BUG(); + + isac->tx_skb = skb; + isac_fill_fifo(isac); + break; + } +} + +static int __init hisax_isac_init(void) +{ + printk(KERN_INFO "hisax_isac: ISAC-S/ISAC-SX ISDN driver v0.1.0\n"); + + l1fsm.state_count = L1_STATE_COUNT; + l1fsm.event_count = L1_EVENT_COUNT; + l1fsm.strState = strL1State; + l1fsm.strEvent = strL1Event; + return FsmNew(&l1fsm, L1FnList, ARRAY_SIZE(L1FnList)); +} + +static void __exit hisax_isac_exit(void) +{ + FsmFree(&l1fsm); +} + +EXPORT_SYMBOL(isac_init); +EXPORT_SYMBOL(isac_d_l2l1); + +EXPORT_SYMBOL(isacsx_setup); +EXPORT_SYMBOL(isacsx_interrupt); + +EXPORT_SYMBOL(isac_setup); +EXPORT_SYMBOL(isac_interrupt); + +module_init(hisax_isac_init); +module_exit(hisax_isac_exit); diff --git a/drivers/isdn/hisax/hisax_isac.h b/drivers/isdn/hisax/hisax_isac.h new file mode 100644 index 00000000..730067e2 --- /dev/null +++ b/drivers/isdn/hisax/hisax_isac.h @@ -0,0 +1,45 @@ +#ifndef __HISAX_ISAC_H__ +#define __HISAX_ISAC_H__ + +#include +#include "fsm.h" +#include "hisax_if.h" + +#define TIMER3_VALUE 7000 +#define MAX_DFRAME_LEN_L1 300 + +#define HW_IOM1 0 + +struct isac { + void *priv; + + u_long flags; + struct hisax_d_if hisax_d_if; + struct FsmInst l1m; + struct FsmTimer timer; + u_char mocr; + u_char adf2; + int type; + + u_char rcvbuf[MAX_DFRAME_LEN_L1]; + int rcvidx; + + struct sk_buff *tx_skb; + int tx_cnt; + + u_char (*read_isac) (struct isac *, u_char); + void (*write_isac) (struct isac *, u_char, u_char); + void (*read_isac_fifo) (struct isac *, u_char *, int); + void (*write_isac_fifo)(struct isac *, u_char *, int); +}; + +void isac_init(struct isac *isac); +void isac_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg); + +void isac_setup(struct isac *isac); +void isac_interrupt(struct isac *isac); + +void isacsx_setup(struct isac *isac); +void isacsx_interrupt(struct isac *isac); + +#endif diff --git a/drivers/isdn/hisax/isdnl1.h b/drivers/isdn/hisax/isdnl1.h index c68044dc..d106ce7e 100644 --- a/drivers/isdn/hisax/isdnl1.h +++ b/drivers/isdn/hisax/isdnl1.h @@ -14,6 +14,7 @@ #define D_RX_MON1 5 #define D_TX_MON0 6 #define D_TX_MON1 7 +#define E_RCVBUFREADY 8 #define B_RCVBUFREADY 0 #define B_XMTBUFREADY 1 diff --git a/drivers/isdn/hisax/isdnl2.c b/drivers/isdn/hisax/isdnl2.c index 15917479..143ad9bd 100644 --- a/drivers/isdn/hisax/isdnl2.c +++ b/drivers/isdn/hisax/isdnl2.c @@ -1414,8 +1414,8 @@ l2_st5_tei_remove(struct FsmInst *fi, int event, void *arg) freewin(st); st->l2.tei = -1; stop_t200(st, 17); - st5_dl_release_l2l3(st); FsmChangeState(fi, ST_L2_1); + st5_dl_release_l2l3(st); } static void diff --git a/drivers/isdn/hisax/st5481-debug.h b/drivers/isdn/hisax/st5481-debug.h index a2a1aeac..557348ef 100644 --- a/drivers/isdn/hisax/st5481-debug.h +++ b/drivers/isdn/hisax/st5481-debug.h @@ -24,50 +24,10 @@ if (level & ST5481_DEBUG) \ printk(KERN_DEBUG __FUNCTION__ ": " format "\n" , ## arg) \ -static inline const char * -ST5481_IND_string(int evt) -{ - static char s[16]; - - switch(evt) { - case ST5481_IND_DP: return "DP"; - case ST5481_IND_RSY: return "RSY"; - case ST5481_IND_AP: return "AP"; - case ST5481_IND_AI8: return "AI8"; - case ST5481_IND_AI10: return "AI10"; - case ST5481_IND_AIL: return "AIL"; - case ST5481_IND_DI: return "DI"; - } - - sprintf(s,"0x%x",evt); - return s; -} - -static inline const char * -ST5481_CMD_string(int evt) -{ - static char s[16]; - - switch (evt) { - case ST5481_CMD_DR: return "DR"; - case ST5481_CMD_RES: return "RES"; - case ST5481_CMD_TM1: return "TM1"; - case ST5481_CMD_TM2: return "TM2"; - case ST5481_CMD_PUP: return "PUP"; - case ST5481_CMD_AR8: return "AR8"; - case ST5481_CMD_AR10: return "AR10"; - case ST5481_CMD_ARL: return "ARL"; - case ST5481_CMD_PDN: return "PDN"; - }; - - sprintf(s,"0x%x",evt); - return s; -} - static inline void dump_packet(const char *name,const u_char *data,int pkt_len) { -#define DUMP_HDR_SIZE 20 +#define DUMP_HDR_SIZE 200 #define DUMP_TLR_SIZE 8 if (pkt_len) { int i,len1,len2; diff --git a/drivers/isdn/hisax/st5481.h b/drivers/isdn/hisax/st5481.h index 563f53dd..333de24c 100644 --- a/drivers/isdn/hisax/st5481.h +++ b/drivers/isdn/hisax/st5481.h @@ -1,14 +1,27 @@ -#ifndef _ST5481__H_ -#define _ST5481__H_ +/* + * Driver for ST5481 USB ISDN modem + * + * Author Frode Isaksen + * Copyright 2001 by Frode Isaksen + * 2001 by Kai Germaschewski + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#ifndef _ST5481_H_ +#define _ST5481_H_ + +// USB IDs, the Product Id is in the range 0x4810-0x481F #define ST_VENDOR_ID 0x0483 -#define ST5481_PRODUCT_ID 0x4810 /* The Product Id is in the range 0x4810-0x481F */ +#define ST5481_PRODUCT_ID 0x4810 #define ST5481_PRODUCT_ID_MASK 0xFFF0 -/* - ST5481 endpoints when using alternative setting 3 (2B+D). - To get the endpoint address, OR with 0x80 for IN endpoints. -*/ +// ST5481 endpoints when using alternative setting 3 (2B+D). +// To get the endpoint address, OR with 0x80 for IN endpoints. + #define EP_CTRL 0x00U /* Control endpoint */ #define EP_INT 0x01U /* Interrupt endpoint */ #define EP_B1_OUT 0x02U /* B1 channel out */ @@ -18,27 +31,27 @@ #define EP_D_OUT 0x06U /* D channel out */ #define EP_D_IN 0x07U /* D channel in */ -/* - Number of isochronous packets. With 20 packets we get - 50 interrupts/sec for each endpoint. -*/ +// Number of isochronous packets. With 20 packets we get +// 50 interrupts/sec for each endpoint. + #define NUM_ISO_PACKETS_D 20 #define NUM_ISO_PACKETS_B 20 -/* - Size of each isochronous packet. -*/ +// Size of each isochronous packet. +// In outgoing direction we need to match ISDN data rates: +// D: 2 bytes / msec -> 16 kbit / s +// B: 16 bytes / msec -> 64 kbit / s #define SIZE_ISO_PACKETS_D_IN 16 #define SIZE_ISO_PACKETS_D_OUT 2 #define SIZE_ISO_PACKETS_B_IN 32 #define SIZE_ISO_PACKETS_B_OUT 8 +// If we overrun/underrun, we send one packet with +/- 2 bytes #define B_FLOW_ADJUST 2 -/* - Registers that are written using vendor specific device request - on endpoint 0. -*/ +// Registers that are written using vendor specific device request +// on endpoint 0. + #define LBA 0x02 /* S loopback */ #define SET_DEFAULT 0x06 /* Soft reset */ #define LBB 0x1D /* S maintenance loopback */ @@ -74,16 +87,14 @@ #define TXCI 0x56 /* CI command to be transmitted */ -/* - Format of the interrupt packet received on endpoint 1: +// Format of the interrupt packet received on endpoint 1: +// +// +--------+--------+--------+--------+--------+--------+ +// !MPINT !FFINT_D !FFINT_B1!FFINT_B2!CCIST !GPIO_INT! +// +--------+--------+--------+--------+--------+--------+ - +--------+--------+--------+--------+--------+--------+ - !MPINT !FFINT_D !FFINT_B1!FFINT_B2!CCIST !GPIO_INT! - +--------+--------+--------+--------+--------+--------+ +// Offsets in the interrupt packet -*/ - -/* Offsets in the interrupt packet */ #define MPINT 0 #define FFINT_D 1 #define FFINT_B1 2 @@ -92,7 +103,7 @@ #define GPIO_INT 5 #define INT_PKT_SIZE 6 -/* MPINT */ +// MPINT #define LSD_INT 0x80 /* S line activity detected */ #define RXCI_INT 0x40 /* Indicate primitive arrived */ #define DEN_INT 0x20 /* Signal enabling data out of D Tx fifo */ @@ -102,7 +113,7 @@ #define DRXON_INT 0x02 /* Reception channel active */ #define GPCHG_INT 0x01 /* GPIO pin value changed */ -/* FFINT_x */ +// FFINT_x #define IN_OVERRUN 0x80 /* In fifo overrun */ #define OUT_UNDERRUN 0x40 /* Out fifo underrun */ #define IN_UP 0x20 /* In fifo thresholdh up-crossed */ @@ -115,17 +126,8 @@ #define ANY_REC_INT (IN_OVERRUN+IN_UP+IN_DOWN+IN_COUNTER_ZEROED) #define ANY_XMIT_INT (OUT_UNDERRUN+OUT_UP+OUT_DOWN+OUT_COUNTER_ZEROED) -/* Level 1 indications that are found at offset 4 (CCIST) - in the interrupt packet */ -#define ST5481_IND_DP 0x0 /* Deactivation Pending */ -#define ST5481_IND_RSY 0x4 /* ReSYnchronizing */ -#define ST5481_IND_AP 0x8 /* Activation Pending */ -#define ST5481_IND_AI8 0xC /* Activation Indication class 8 */ -#define ST5481_IND_AI10 0xD /* Activation Indication class 10 */ -#define ST5481_IND_AIL 0xE /* Activation Indication Loopback */ -#define ST5481_IND_DI 0xF /* Deactivation Indication */ -/* Level 1 commands that are sent using the TXCI device request */ +// Level 1 commands that are sent using the TXCI device request #define ST5481_CMD_DR 0x0 /* Deactivation Request */ #define ST5481_CMD_RES 0x1 /* state machine RESet */ #define ST5481_CMD_TM1 0x2 /* Test Mode 1 */ @@ -136,15 +138,14 @@ #define ST5481_CMD_ARL 0xA /* Activation Request Loopback */ #define ST5481_CMD_PDN 0xF /* Power DoWn */ - -/* Turn on/off the LEDs using the GPIO device request. - To use the B LEDs, number_of_leds must be set to 4 */ +// Turn on/off the LEDs using the GPIO device request. +// To use the B LEDs, number_of_leds must be set to 4 #define B1_LED 0x10U #define B2_LED 0x20U #define GREEN_LED 0x40U #define RED_LED 0x80U -/* D channel out states */ +// D channel out states enum { ST_DOUT_NONE, @@ -163,7 +164,7 @@ enum { #define DOUT_STATE_COUNT (ST_DOUT_WAIT_FOR_RESET + 1) -/* D channel out events */ +// D channel out events enum { EV_DOUT_START_XMIT, EV_DOUT_COMPLETE, @@ -172,12 +173,48 @@ enum { EV_DOUT_STOPPED, EV_DOUT_COLL, EV_DOUT_UNDERRUN, - DXMIT_NOT_BUSY, }; -#define DOUT_EVENT_COUNT (DXMIT_NOT_BUSY + 1) +#define DOUT_EVENT_COUNT (EV_DOUT_UNDERRUN + 1) -#define MIN(a,b) ((a)<(b) ? (a):(b)) +// ---------------------------------------------------------------------- + +enum { + ST_L1_F3, + ST_L1_F4, + ST_L1_F6, + ST_L1_F7, + ST_L1_F8, +}; + +#define L1_STATE_COUNT (ST_L1_F8+1) + +// The first 16 entries match the Level 1 indications that +// are found at offset 4 (CCIST) in the interrupt packet + +enum { + EV_IND_DP, // 0000 Deactivation Pending + EV_IND_1, // 0001 + EV_IND_2, // 0010 + EV_IND_3, // 0011 + EV_IND_RSY, // 0100 ReSYnchronizing + EV_IND_5, // 0101 + EV_IND_6, // 0110 + EV_IND_7, // 0111 + EV_IND_AP, // 1000 Activation Pending + EV_IND_9, // 1001 + EV_IND_10, // 1010 + EV_IND_11, // 1011 + EV_IND_AI8, // 1100 Activation Indication class 8 + EV_IND_AI10,// 1101 Activation Indication class 10 + EV_IND_AIL, // 1110 Activation Indication Loopback + EV_IND_DI, // 1111 Deactivation Indication + EV_PH_ACTIVATE_REQ, + EV_PH_DEACTIVATE_REQ, + EV_TIMER3, +}; + +#define L1_EVENT_COUNT (EV_TIMER3 + 1) #define ERR(format, arg...) \ printk(KERN_ERR __FILE__ ": " __FUNCTION__ ": " format "\n" , ## arg) @@ -188,7 +225,6 @@ printk(KERN_WARNING __FILE__ ": " __FUNCTION__ ": " format "\n" , ## arg) #define INFO(format, arg...) \ printk(KERN_INFO __FILE__ ": " __FUNCTION__ ": " format "\n" , ## arg) -#include "st5481-debug.h" #include "st5481_hdlc.h" #include "fsm.h" #include "hisax_if.h" @@ -265,21 +301,6 @@ static inline int fifo_remove(struct fifo *fifo) return index; } -// ---------------------------------------------------------------------- - -/* FIFO of received interrupt events */ - -struct evt { - int pr; - void *arg; -}; - -#define MAX_EVT_FIFO 16 -struct evt_fifo { - struct fifo f; - struct evt data[MAX_EVT_FIFO]; -}; - /* ====================================================================== * control pipe */ @@ -308,7 +329,7 @@ struct st5481_ctrl { }; struct st5481_intr { - struct evt_fifo evt_fifo; + // struct evt_fifo evt_fifo; struct urb *urb; }; @@ -369,9 +390,7 @@ struct st5481_adapter { unsigned int led_counter; unsigned long event; - struct tq_struct tqueue; - int ph_state; struct FsmInst l1m; struct FsmTimer timer; @@ -414,8 +433,6 @@ void st5481_release_b(struct st5481_bcs *bcs); void st5481_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg); /* D Channel */ -#define D_L1STATECHANGE 2 -#define D_OUT_EVENT 10 int st5481_setup_d(struct st5481_adapter *adapter); void st5481_release_d(struct st5481_adapter *adapter); @@ -423,9 +440,6 @@ void st5481_b_l2l1(struct hisax_if *b_if, int pr, void *arg); int st5481_d_init(void); void st5481_d_exit(void); -void st5481_sched_event(struct st5481_adapter *adapter, int event); -void st5481_sched_d_out_event(struct st5481_adapter *adapter, - int event, void *arg); /* USB */ void st5481_ph_command(struct st5481_adapter *adapter, unsigned int command); int st5481_setup_isocpipes(struct urb* urb[2], struct usb_device *dev, @@ -448,4 +462,72 @@ void st5481_release_usb(struct st5481_adapter *adapter); void st5481_start(struct st5481_adapter *adapter); void st5481_stop(struct st5481_adapter *adapter); +// ---------------------------------------------------------------------- +// debugging macros + +#define __debug_variable st5481_debug +#include "hisax_debug.h" + +#ifdef CONFIG_HISAX_DEBUG + +extern int st5481_debug; + +#define DBG_ISO_PACKET(level,urb) \ + if (level & __debug_variable) dump_iso_packet(__FUNCTION__,urb) + +static void __attribute__((unused)) +dump_iso_packet(const char *name,urb_t *urb) +{ + int i,j; + int len,ofs; + u_char *data; + + printk(KERN_DEBUG "%s: packets=%d,errors=%d\n", + name,urb->number_of_packets,urb->error_count); + for (i = 0; i < urb->number_of_packets; ++i) { + if (urb->pipe & USB_DIR_IN) { + len = urb->iso_frame_desc[i].actual_length; + } else { + len = urb->iso_frame_desc[i].length; + } + ofs = urb->iso_frame_desc[i].offset; + printk(KERN_DEBUG "len=%.2d,ofs=%.3d ",len,ofs); + if (len) { + data = urb->transfer_buffer+ofs; + for (j=0; j < len; j++) { + printk ("%.2x", data[j]); + } + } + printk("\n"); + } +} + +static inline const char *ST5481_CMD_string(int evt) +{ + static char s[16]; + + switch (evt) { + case ST5481_CMD_DR: return "DR"; + case ST5481_CMD_RES: return "RES"; + case ST5481_CMD_TM1: return "TM1"; + case ST5481_CMD_TM2: return "TM2"; + case ST5481_CMD_PUP: return "PUP"; + case ST5481_CMD_AR8: return "AR8"; + case ST5481_CMD_AR10: return "AR10"; + case ST5481_CMD_ARL: return "ARL"; + case ST5481_CMD_PDN: return "PDN"; + }; + + sprintf(s,"0x%x",evt); + return s; +} + +#else + +#define DBG_ISO_PACKET(level,urb) do {} while (0) + +#endif + + + #endif diff --git a/drivers/isdn/hisax/st5481_b.c b/drivers/isdn/hisax/st5481_b.c index ca1bfde4..17b2a21f 100644 --- a/drivers/isdn/hisax/st5481_b.c +++ b/drivers/isdn/hisax/st5481_b.c @@ -1,3 +1,15 @@ +/* + * Driver for ST5481 USB ISDN modem + * + * Author Frode Isaksen + * Copyright 2001 by Frode Isaksen + * 2001 by Kai Germaschewski + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + #include #include #include @@ -6,9 +18,9 @@ #include #include "st5481.h" -static inline void B_L1L2(struct hisax_b_if *b_if, int pr, void *arg) +static inline void B_L1L2(struct st5481_bcs *bcs, int pr, void *arg) { - struct hisax_if *ifc = (struct hisax_if *) b_if; + struct hisax_if *ifc = (struct hisax_if *) &bcs->b_if; ifc->l1l2(ifc, pr, arg); } @@ -50,13 +62,15 @@ static void usb_b_out(struct st5481_bcs *bcs,int buf_nr) len = 0; while (len < buf_size) { if ((skb = b_out->tx_skb)) { - DUMP_SKB(0x100, skb); + DBG_SKB(0x100, skb); DBG(4,"B%d,len=%d",bcs->channel+1,skb->len); if (bcs->mode == L1_MODE_TRANS) { - bytes_sent = MIN(buf_size - len, skb->len); + bytes_sent = buf_size - len; + if (skb->len < bytes_sent) + bytes_sent = skb->len; - memcpy(urb->transfer_buffer+len, skb->data, buf_size); + memcpy(urb->transfer_buffer+len, skb->data, bytes_sent); len += bytes_sent; } else { @@ -70,8 +84,7 @@ static void usb_b_out(struct st5481_bcs *bcs,int buf_nr) if (!skb->len) { // Frame sent b_out->tx_skb = NULL; - B_L1L2(&bcs->b_if, PH_DATA | CONFIRM, - (void *) skb->truesize); + B_L1L2(bcs, PH_DATA | CONFIRM, (void *) skb->truesize); dev_kfree_skb_any(skb); /* if (!(bcs->tx_skb = skb_dequeue(&bcs->sq))) { */ @@ -102,7 +115,7 @@ static void usb_b_out(struct st5481_bcs *bcs,int buf_nr) urb->number_of_packets = i; urb->dev = adapter->usb_dev; - DUMP_ISO_PACKET(0x200,urb); + DBG_ISO_PACKET(0x200,urb); SUBMIT_URB(urb); } @@ -324,7 +337,6 @@ void __devexit st5481_release_b(struct st5481_bcs *bcs) */ void st5481_b_l2l1(struct hisax_if *ifc, int pr, void *arg) { - struct hisax_b_if *b_if = (struct hisax_b_if *) ifc; struct st5481_bcs *bcs = ifc->priv; struct sk_buff *skb = arg; int mode; @@ -342,12 +354,12 @@ void st5481_b_l2l1(struct hisax_if *ifc, int pr, void *arg) mode = (int) arg; DBG(4,"B%d,PH_ACTIVATE_REQUEST %d", bcs->channel + 1, mode); st5481B_mode(bcs, mode); - B_L1L2(b_if, PH_ACTIVATE | INDICATION, NULL); + B_L1L2(bcs, PH_ACTIVATE | INDICATION, NULL); break; case PH_DEACTIVATE | REQUEST: DBG(4,"B%d,PH_DEACTIVATE_REQUEST", bcs->channel + 1); st5481B_mode(bcs, L1_MODE_NULL); - B_L1L2(b_if, PH_DEACTIVATE | INDICATION, NULL); + B_L1L2(bcs, PH_DEACTIVATE | INDICATION, NULL); break; default: WARN("pr %#x\n", pr); diff --git a/drivers/isdn/hisax/st5481_d.c b/drivers/isdn/hisax/st5481_d.c index e73c98d5..78c4e496 100644 --- a/drivers/isdn/hisax/st5481_d.c +++ b/drivers/isdn/hisax/st5481_d.c @@ -1,3 +1,15 @@ +/* + * Driver for ST5481 USB ISDN modem + * + * Author Frode Isaksen + * Copyright 2001 by Frode Isaksen + * 2001 by Kai Germaschewski + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + #include #include #include @@ -8,79 +20,9 @@ static void ph_connect(struct st5481_adapter *adapter); static void ph_disconnect(struct st5481_adapter *adapter); -static void st5481_d_out_event(struct st5481_adapter *adapter); - -/* - * delay processing to bh - */ -void st5481_sched_event(struct st5481_adapter *adapter, int event) -{ - test_and_set_bit(event, &adapter->event); - queue_task(&adapter->tqueue, &tq_immediate); - mark_bh(IMMEDIATE_BH); -} - -/* - * Schedule the D_OUT_EVENT. - * The actual event is stored in a FIFO. - */ -void st5481_sched_d_out_event(struct st5481_adapter *adapter, - int event, void *arg) -{ - struct st5481_intr *intr = &adapter->intr; - int w_index; - - // DBG(2,"event=%s",D_EVENT_string(event)); - - w_index = fifo_add(&intr->evt_fifo.f); - if (w_index < 0) { - WARN("D_OUT event FIFO full"); - return; - } - intr->evt_fifo.data[w_index].pr = event; - intr->evt_fifo.data[w_index].arg = arg; - - // Schedule to tell that an event has been added - st5481_sched_event(adapter, D_OUT_EVENT); -} - -static void st5481_new_ph(struct st5481_adapter *adapter); - -static void st5481_bh(struct st5481_adapter *adapter) -{ - -#if 0 - struct PStack *stptr; - - if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) { - stptr = cs->stlist; - while (stptr != NULL) { - stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL); - stptr = stptr->next; - } - } -#endif - - if (test_and_clear_bit(D_L1STATECHANGE, &adapter->event)) { - st5481_new_ph(adapter); - } - if (test_and_clear_bit(D_OUT_EVENT, &adapter->event)) { - st5481_d_out_event(adapter); - } -} static struct Fsm l1fsm; -enum { - ST_L1_F3, - ST_L1_F4, - ST_L1_F6, - ST_L1_F7, - ST_L1_F8, -}; - -#define L1_STATE_COUNT (ST_L1_F8+1) - static char *strL1State[] = { "ST_L1_F3", @@ -90,36 +32,32 @@ static char *strL1State[] = "ST_L1_F8", }; -enum { - EV_PH_ACTIVATE_REQ, - EV_PH_DEACTIVATE_REQ, - EV_IND_DI, - EV_IND_DP, - EV_IND_RSY, - EV_IND_AP, - EV_IND_AI8, - EV_IND_AI10, - EV_TIMER3, -}; - -#define L1_EVENT_COUNT (EV_TIMER3 + 1) - static char *strL1Event[] = { - "EV_PH_ACTIVATE_REQ", - "EV_PH_DEACTIVATE_REQ", - "EV_IND_DI", - "EV_IND_DP", - "EV_IND_RSY", - "EV_IND_AP", + "EV_IND_DP", + "EV_IND_1", + "EV_IND_2", + "EV_IND_3", + "EV_IND_RSY", + "EV_IND_5", + "EV_IND_6", + "EV_IND_7", + "EV_IND_AP", + "EV_IND_9", + "EV_IND_10", + "EV_IND_11", "EV_IND_AI8", "EV_IND_AI10", + "EV_IND_AIL", + "EV_IND_DI", + "EV_PH_ACTIVATE_REQ", + "EV_PH_DEACTIVATE_REQ", "EV_TIMER3", }; -static inline void D_L1L2(struct hisax_d_if *d_if, int pr, void *arg) +static inline void D_L1L2(struct st5481_adapter *adapter, int pr, void *arg) { - struct hisax_if *ifc = (struct hisax_if *) d_if; + struct hisax_if *ifc = (struct hisax_if *) &adapter->hisax_d_if; ifc->l1l2(ifc, pr, arg); } @@ -133,7 +71,7 @@ l1_go_f3(struct FsmInst *fi, int event, void *arg) ph_disconnect(adapter); FsmChangeState(fi, ST_L1_F3); - D_L1L2(&adapter->hisax_d_if, PH_DEACTIVATE | INDICATION, NULL); + D_L1L2(adapter, PH_DEACTIVATE | INDICATION, NULL); } static void @@ -155,7 +93,7 @@ l1_go_f7(struct FsmInst *fi, int event, void *arg) FsmDelTimer(&adapter->timer, 0); ph_connect(adapter); FsmChangeState(fi, ST_L1_F7); - D_L1L2(&adapter->hisax_d_if, PH_ACTIVATE | INDICATION, NULL); + D_L1L2(adapter, PH_ACTIVATE | INDICATION, NULL); } static void @@ -176,7 +114,7 @@ l1_timer3(struct FsmInst *fi, int event, void *arg) st5481_ph_command(adapter, ST5481_CMD_DR); FsmChangeState(fi, ST_L1_F3); - D_L1L2(&adapter->hisax_d_if, PH_DEACTIVATE | INDICATION, NULL); + D_L1L2(adapter, PH_DEACTIVATE | INDICATION, NULL); } static void @@ -238,39 +176,11 @@ static void l1m_debug(struct FsmInst *fi, char *fmt, ...) va_start(args, fmt); vsprintf(buf, fmt, args); + printk("buf %s\n", buf); DBG(8, "%s", buf); va_end(args); } -static void st5481_new_ph(struct st5481_adapter *adapter) -{ - DBG(8,"state=%s", ST5481_IND_string(adapter->ph_state)); - - switch (adapter->ph_state) { - case ST5481_IND_DI: - FsmEvent(&adapter->l1m, EV_IND_DI, NULL); - break; - case ST5481_IND_DP: - FsmEvent(&adapter->l1m, EV_IND_DP, NULL); - break; - case ST5481_IND_RSY: - FsmEvent(&adapter->l1m, EV_IND_RSY, NULL); - break; - case ST5481_IND_AP: - FsmEvent(&adapter->l1m, EV_IND_AP, NULL); - break; - case ST5481_IND_AI8: - FsmEvent(&adapter->l1m, EV_IND_AI8, NULL); - break; - case ST5481_IND_AI10: - FsmEvent(&adapter->l1m, EV_IND_AI10, NULL); - break; - default: - WARN("unknown st5481.ph_state %x", adapter->ph_state); - break; - } -} - /* ====================================================================== * D-Channel out */ @@ -360,7 +270,6 @@ static char *strDoutEvent[] = "EV_DOUT_STOPPED", "EV_DOUT_COLL", "EV_DOUT_UNDERRUN", - "DXMIT_NOT_BUSY", }; static void dout_debug(struct FsmInst *fi, char *fmt, ...) @@ -378,7 +287,7 @@ static void dout_stop_event(void *context) { struct st5481_adapter *adapter = context; - st5481_sched_d_out_event(adapter, EV_DOUT_STOPPED, NULL); + FsmEvent(&adapter->d_out.fsm, EV_DOUT_STOPPED, NULL); } /* @@ -391,6 +300,7 @@ static void usb_d_out(struct st5481_adapter *adapter, int buf_nr) unsigned int num_packets, packet_offset; int len, buf_size, bytes_sent; struct sk_buff *skb; + iso_packet_descriptor_t *desc; if (d_out->fsm.state != ST_DOUT_NORMAL) return; @@ -422,27 +332,32 @@ static void usb_d_out(struct st5481_adapter *adapter, int buf_nr) } if (skb && !skb->len) { d_out->tx_skb = NULL; - D_L1L2(&adapter->hisax_d_if, PH_DATA | CONFIRM, NULL); + D_L1L2(adapter, PH_DATA | CONFIRM, NULL); dev_kfree_skb_any(skb); } // Prepare the URB urb->transfer_buffer_length = len; - for (num_packets = 0, packet_offset = 0; packet_offset < len; - num_packets++, packet_offset += SIZE_ISO_PACKETS_D_OUT) { - urb->iso_frame_desc[num_packets].offset = packet_offset; - urb->iso_frame_desc[num_packets].length = - MIN(SIZE_ISO_PACKETS_D_OUT, len - packet_offset); + num_packets = 0; + packet_offset = 0; + while (packet_offset < len) { + desc = &urb->iso_frame_desc[num_packets]; + desc->offset = packet_offset; + desc->length = SIZE_ISO_PACKETS_D_OUT; + if (len - packet_offset < desc->length) + desc->length = len - packet_offset; + num_packets++; + packet_offset += desc->length; } urb->number_of_packets = num_packets; // Prepare the URB urb->dev = adapter->usb_dev; - // Need to transmit the next buffer 8ms after the DEN_EVENT + // Need to transmit the next buffer 2ms after the DEN_EVENT urb->transfer_flags = 0; urb->start_frame = usb_get_current_frame_number(adapter->usb_dev)+2; - DUMP_ISO_PACKET(0x20,urb); + DBG_ISO_PACKET(0x20,urb); if (usb_submit_urb(urb) < 0) { // There is another URB queued up @@ -455,27 +370,7 @@ static void fifo_reseted(void *context) { struct st5481_adapter *adapter = context; - st5481_sched_d_out_event(adapter, EV_DOUT_RESETED, NULL); -} - -/* - * Remove the event from the FIFO and call the OUT D - * state machine. - */ -static void st5481_d_out_event(struct st5481_adapter *adapter) -{ - struct st5481_intr *intr = &adapter->intr; - struct st5481_d_out *d_out = &adapter->d_out; - int r_index; - int event; - void *arg; - - while ((r_index = fifo_remove(&intr->evt_fifo.f)) >= 0) { - event = intr->evt_fifo.data[r_index].pr; - arg = intr->evt_fifo.data[r_index].arg; - FsmEvent(&d_out->fsm, event, arg); - } - + FsmEvent(&adapter->d_out.fsm, EV_DOUT_RESETED, NULL); } static void usb_d_out_complete(struct urb *urb) @@ -502,11 +397,7 @@ static void usb_d_out_complete(struct urb *urb) } } - if (d_out->fsm.state == ST_DOUT_NORMAL) - // submit next urb directly from irq context - usb_d_out(adapter, buf_nr); - else - st5481_sched_d_out_event(adapter, EV_DOUT_COMPLETE, NULL); + FsmEvent(&adapter->d_out.fsm, EV_DOUT_COMPLETE, (void *) buf_nr); } /* ====================================================================== */ @@ -533,7 +424,7 @@ static void dout_start_xmit(struct FsmInst *fsm, int event, void *arg) } urb = d_out->urb[buf_nr]; - DUMP_SKB(0x10, skb); + DBG_SKB(0x10, skb); len = hdlc_encode(&d_out->hdlc_state, skb->data, skb->len, &bytes_sent, urb->transfer_buffer, 16); @@ -546,7 +437,7 @@ static void dout_start_xmit(struct FsmInst *fsm, int event, void *arg) if (skb->len == 0) { d_out->tx_skb = NULL; - D_L1L2(&adapter->hisax_d_if, PH_DATA | CONFIRM, NULL); + D_L1L2(adapter, PH_DATA | CONFIRM, NULL); dev_kfree_skb_any(skb); } @@ -561,7 +452,7 @@ static void dout_start_xmit(struct FsmInst *fsm, int event, void *arg) urb->dev = adapter->usb_dev; urb->transfer_flags = USB_ISO_ASAP; - DUMP_ISO_PACKET(0x20,urb); + DBG_ISO_PACKET(0x20,urb); SUBMIT_URB(urb); } @@ -651,6 +542,14 @@ static void dout_reseted(struct FsmInst *fsm, int event, void *arg) FsmEvent(&d_out->fsm, EV_DOUT_START_XMIT, NULL); } +static void dout_complete(struct FsmInst *fsm, int event, void *arg) +{ + struct st5481_adapter *adapter = fsm->userdata; + int buf_nr = (int) arg; + + usb_d_out(adapter, buf_nr); +} + static void dout_ignore(struct FsmInst *fsm, int event, void *arg) { } @@ -670,9 +569,7 @@ static struct FsmNode DoutFnList[] __initdata = {ST_DOUT_LONG_WAIT_DEN, EV_DOUT_UNDERRUN, dout_underrun}, {ST_DOUT_NORMAL, EV_DOUT_UNDERRUN, dout_underrun}, -// {ST_DOUT_NORMAL, DV_DOUT_COMPLETE, ...}, -// refilling the fifo is directly handled from irq context, not through -// this state machine + {ST_DOUT_NORMAL, EV_DOUT_COMPLETE, dout_complete}, {ST_DOUT_WAIT_FOR_UNDERRUN, EV_DOUT_UNDERRUN, dout_underrun}, {ST_DOUT_WAIT_FOR_UNDERRUN, EV_DOUT_COMPLETE, dout_ignore}, @@ -725,8 +622,8 @@ static void ph_connect(struct st5481_adapter *adapter) FsmChangeState(&d_out->fsm, ST_DOUT_NONE); - st5481_usb_device_ctrl_msg(adapter, FFMSK_D, OUT_UNDERRUN, NULL, NULL); - st5481_usb_device_ctrl_msg(adapter, FFMSK_D, 0xff, NULL, NULL); + // st5481_usb_device_ctrl_msg(adapter, FFMSK_D, OUT_UNDERRUN, NULL, NULL); + st5481_usb_device_ctrl_msg(adapter, FFMSK_D, 0xfc, NULL, NULL); st5481_in_mode(d_in, L1_MODE_HDLC); #if LOOPBACK @@ -808,7 +705,6 @@ int __devinit st5481_setup_d(struct st5481_adapter *adapter) if (retval) goto err_d_out; - INIT_TQUEUE(&adapter->tqueue, (void *)(void *)st5481_bh, adapter); adapter->l1m.fsm = &l1fsm; adapter->l1m.state = ST_L1_F3; adapter->l1m.debug = 1; @@ -870,7 +766,9 @@ int __init st5481_d_init(void) return retval; } -void __exit st5481_d_exit(void) +// can't be __exit +void st5481_d_exit(void) { FsmFree(&l1fsm); + FsmFree(&dout_fsm); } diff --git a/drivers/isdn/hisax/st5481_hdlc.c b/drivers/isdn/hisax/st5481_hdlc.c index 296e515b..2ea5136b 100644 --- a/drivers/isdn/hisax/st5481_hdlc.c +++ b/drivers/isdn/hisax/st5481_hdlc.c @@ -1,7 +1,18 @@ +/* + * Driver for ST5481 USB ISDN modem + * + * Author Frode Isaksen + * Copyright 2001 by Frode Isaksen + * 2001 by Kai Germaschewski + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + #include "st5481_hdlc.h" - -static const unsigned short int crc16_tab[]={ +static const unsigned short int crc16_tab[] = { 0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf, 0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7, 0x1081,0x0108,0x3393,0x221a,0x56a5,0x472c,0x75b7,0x643e, @@ -236,7 +247,7 @@ int hdlc_decode(struct hdlc_vars *hdlc, const unsigned char *src, } else { switch(hdlc->hdlc_bits1){ case 5: - break; + break; case 6: if(hdlc->data_received){ if (hdlc->dstpos < 2) { @@ -350,260 +361,260 @@ int hdlc_decode(struct hdlc_vars *hdlc, const unsigned char *src, dsize - destination buffer size returns - number of encoded bytes in the destination buffer */ -int -hdlc_encode(struct hdlc_vars *hdlc, const unsigned char *src, unsigned short slen, int *count, - unsigned char *dst, int dsize) +int hdlc_encode(struct hdlc_vars *hdlc, const unsigned char *src, + unsigned short slen, int *count, + unsigned char *dst, int dsize) { - static const unsigned char xfast_flag_value[]={ - 0x7e,0x3f,0x9f,0xcf,0xe7,0xf3,0xf9,0xfc,0x7e - }; + static const unsigned char xfast_flag_value[] = { + 0x7e,0x3f,0x9f,0xcf,0xe7,0xf3,0xf9,0xfc,0x7e + }; - int len = 0; + int len = 0; - *count = slen; + *count = slen; - while (dsize > 0) { - if(hdlc->bit_shift==0){ - if(slen && !hdlc->do_closing){ - hdlc->shift_reg = *src++; - slen--; - if (slen == 0) - hdlc->do_closing = 1; /* closing sequence, CRC + flag(s) */ - hdlc->bit_shift = 8; - } else { - if(hdlc->state == HDLC_SEND_DATA){ - if(hdlc->data_received){ - hdlc->state = HDLC_SEND_CRC1; - hdlc->crc ^= 0xffff; - hdlc->bit_shift = 8; - hdlc->shift_reg = hdlc->crc & 0xff; - } else if(!hdlc->do_adapt56){ - hdlc->state = HDLC_SEND_FAST_FLAG; - } else { - hdlc->state = HDLC_SENDFLAG_B0; - } - } - - } - } - - switch(hdlc->state){ - case STOPPED: - while (dsize--) - *dst++ = 0xff; - - return dsize; - case HDLC_SEND_FAST_FLAG: - hdlc->do_closing = 0; - if(slen == 0){ - *dst++ = hdlc->ffvalue; - len++; - dsize--; - break; - } - if(hdlc->bit_shift==8){ - hdlc->cbin = hdlc->ffvalue>>(8-hdlc->data_bits); - hdlc->state = HDLC_SEND_DATA; - hdlc->crc = 0xffff; - hdlc->hdlc_bits1 = 0; - hdlc->data_received = 1; - } - break; - case HDLC_SENDFLAG_B0: - hdlc->do_closing = 0; - hdlc->cbin <<= 1; - hdlc->data_bits++; - hdlc->hdlc_bits1 = 0; - hdlc->state = HDLC_SENDFLAG_B1A6; - break; - case HDLC_SENDFLAG_B1A6: - hdlc->cbin <<= 1; - hdlc->data_bits++; - hdlc->cbin++; - if(++hdlc->hdlc_bits1 == 6) - hdlc->state = HDLC_SENDFLAG_B7; - break; - case HDLC_SENDFLAG_B7: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if(slen == 0){ - hdlc->state = HDLC_SENDFLAG_B0; - break; - } - if(hdlc->bit_shift==8){ - hdlc->state = HDLC_SEND_DATA; - hdlc->crc = 0xffff; - hdlc->hdlc_bits1 = 0; - hdlc->data_received = 1; - } - break; - case HDLC_SEND_FIRST_FLAG: - hdlc->data_received = 1; - if(hdlc->data_bits==8){ - hdlc->state = HDLC_SEND_DATA; - hdlc->crc = 0xffff; - hdlc->hdlc_bits1 = 0; - break; - } - hdlc->cbin <<= 1; - hdlc->data_bits++; - if(hdlc->shift_reg & 0x01) - hdlc->cbin++; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - if(hdlc->bit_shift==0){ - hdlc->state = HDLC_SEND_DATA; - hdlc->crc = 0xffff; - hdlc->hdlc_bits1 = 0; - } - break; - case HDLC_SEND_DATA: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if(hdlc->hdlc_bits1 == 5){ - hdlc->hdlc_bits1 = 0; - break; - } - if(hdlc->bit_shift==8){ - unsigned cval; - - cval = (hdlc->crc^hdlc->shift_reg) & 0xff; - hdlc->crc = (hdlc->crc>>8)^crc16_tab[cval]; - } - if(hdlc->shift_reg & 0x01){ - hdlc->hdlc_bits1++; - hdlc->cbin++; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } else { - hdlc->hdlc_bits1 = 0; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } - break; - case HDLC_SEND_CRC1: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if(hdlc->hdlc_bits1 == 5){ - hdlc->hdlc_bits1 = 0; - break; - } - if(hdlc->shift_reg & 0x01){ - hdlc->hdlc_bits1++; - hdlc->cbin++; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } else { - hdlc->hdlc_bits1 = 0; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } - if(hdlc->bit_shift==0){ - hdlc->shift_reg = (hdlc->crc >> 8); - hdlc->state = HDLC_SEND_CRC2; - hdlc->bit_shift = 8; - } - break; - case HDLC_SEND_CRC2: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if(hdlc->hdlc_bits1 == 5){ - hdlc->hdlc_bits1 = 0; - break; - } - if(hdlc->shift_reg & 0x01){ - hdlc->hdlc_bits1++; - hdlc->cbin++; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } else { - hdlc->hdlc_bits1 = 0; - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - } - if(hdlc->bit_shift==0){ - hdlc->shift_reg = 0x7e; - hdlc->state = HDLC_SEND_CLOSING_FLAG; - hdlc->bit_shift = 8; - } - break; - case HDLC_SEND_CLOSING_FLAG: - hdlc->cbin <<= 1; - hdlc->data_bits++; - if(hdlc->hdlc_bits1 == 5){ - hdlc->hdlc_bits1 = 0; - break; - } - if(hdlc->shift_reg & 0x01){ - hdlc->cbin++; - } - hdlc->shift_reg >>= 1; - hdlc->bit_shift--; - if(hdlc->bit_shift==0){ - hdlc->ffvalue = xfast_flag_value[hdlc->data_bits]; - if(hdlc->dchannel){ - hdlc->ffvalue = 0x7e; - hdlc->state = HDLC_SEND_IDLE1; - hdlc->bit_shift = 8-hdlc->data_bits; - if(hdlc->bit_shift==0) - hdlc->state = HDLC_SEND_FAST_IDLE; + while (dsize > 0) { + if(hdlc->bit_shift==0){ + if(slen && !hdlc->do_closing){ + hdlc->shift_reg = *src++; + slen--; + if (slen == 0) + hdlc->do_closing = 1; /* closing sequence, CRC + flag(s) */ + hdlc->bit_shift = 8; } else { - if(!hdlc->do_adapt56){ - hdlc->state = HDLC_SEND_FAST_FLAG; - hdlc->data_received = 0; - } else { - hdlc->state = HDLC_SENDFLAG_B0; - hdlc->data_received = 0; + if(hdlc->state == HDLC_SEND_DATA){ + if(hdlc->data_received){ + hdlc->state = HDLC_SEND_CRC1; + hdlc->crc ^= 0xffff; + hdlc->bit_shift = 8; + hdlc->shift_reg = hdlc->crc & 0xff; + } else if(!hdlc->do_adapt56){ + hdlc->state = HDLC_SEND_FAST_FLAG; + } else { + hdlc->state = HDLC_SENDFLAG_B0; + } } - // Finished with this frame, send flags - if (dsize > 1) dsize = 1; + } } - break; - case HDLC_SEND_IDLE1: - hdlc->do_closing = 0; - hdlc->cbin <<= 1; - hdlc->cbin++; - hdlc->data_bits++; - hdlc->bit_shift--; - if(hdlc->bit_shift==0){ - hdlc->state = HDLC_SEND_FAST_IDLE; - hdlc->bit_shift = 0; - } - break; - case HDLC_SEND_FAST_IDLE: - hdlc->do_closing = 0; - hdlc->cbin = 0xff; - hdlc->data_bits = 8; - if(hdlc->bit_shift == 8){ - hdlc->cbin = 0x7e; - hdlc->state = HDLC_SEND_FIRST_FLAG; - } else { - *dst++ = hdlc->cbin; - hdlc->bit_shift = hdlc->data_bits = 0; - len++; - dsize = 0; - } - break; - default: - break; - } - if(hdlc->do_adapt56){ - if(hdlc->data_bits==7){ + + switch(hdlc->state){ + case STOPPED: + while (dsize--) + *dst++ = 0xff; + + return dsize; + case HDLC_SEND_FAST_FLAG: + hdlc->do_closing = 0; + if(slen == 0){ + *dst++ = hdlc->ffvalue; + len++; + dsize--; + break; + } + if(hdlc->bit_shift==8){ + hdlc->cbin = hdlc->ffvalue>>(8-hdlc->data_bits); + hdlc->state = HDLC_SEND_DATA; + hdlc->crc = 0xffff; + hdlc->hdlc_bits1 = 0; + hdlc->data_received = 1; + } + break; + case HDLC_SENDFLAG_B0: + hdlc->do_closing = 0; + hdlc->cbin <<= 1; + hdlc->data_bits++; + hdlc->hdlc_bits1 = 0; + hdlc->state = HDLC_SENDFLAG_B1A6; + break; + case HDLC_SENDFLAG_B1A6: + hdlc->cbin <<= 1; + hdlc->data_bits++; + hdlc->cbin++; + if(++hdlc->hdlc_bits1 == 6) + hdlc->state = HDLC_SENDFLAG_B7; + break; + case HDLC_SENDFLAG_B7: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if(slen == 0){ + hdlc->state = HDLC_SENDFLAG_B0; + break; + } + if(hdlc->bit_shift==8){ + hdlc->state = HDLC_SEND_DATA; + hdlc->crc = 0xffff; + hdlc->hdlc_bits1 = 0; + hdlc->data_received = 1; + } + break; + case HDLC_SEND_FIRST_FLAG: + hdlc->data_received = 1; + if(hdlc->data_bits==8){ + hdlc->state = HDLC_SEND_DATA; + hdlc->crc = 0xffff; + hdlc->hdlc_bits1 = 0; + break; + } + hdlc->cbin <<= 1; + hdlc->data_bits++; + if(hdlc->shift_reg & 0x01) + hdlc->cbin++; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + if(hdlc->bit_shift==0){ + hdlc->state = HDLC_SEND_DATA; + hdlc->crc = 0xffff; + hdlc->hdlc_bits1 = 0; + } + break; + case HDLC_SEND_DATA: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if(hdlc->hdlc_bits1 == 5){ + hdlc->hdlc_bits1 = 0; + break; + } + if(hdlc->bit_shift==8){ + unsigned cval; + + cval = (hdlc->crc^hdlc->shift_reg) & 0xff; + hdlc->crc = (hdlc->crc>>8)^crc16_tab[cval]; + } + if(hdlc->shift_reg & 0x01){ + hdlc->hdlc_bits1++; + hdlc->cbin++; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } else { + hdlc->hdlc_bits1 = 0; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } + break; + case HDLC_SEND_CRC1: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if(hdlc->hdlc_bits1 == 5){ + hdlc->hdlc_bits1 = 0; + break; + } + if(hdlc->shift_reg & 0x01){ + hdlc->hdlc_bits1++; + hdlc->cbin++; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } else { + hdlc->hdlc_bits1 = 0; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } + if(hdlc->bit_shift==0){ + hdlc->shift_reg = (hdlc->crc >> 8); + hdlc->state = HDLC_SEND_CRC2; + hdlc->bit_shift = 8; + } + break; + case HDLC_SEND_CRC2: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if(hdlc->hdlc_bits1 == 5){ + hdlc->hdlc_bits1 = 0; + break; + } + if(hdlc->shift_reg & 0x01){ + hdlc->hdlc_bits1++; + hdlc->cbin++; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } else { + hdlc->hdlc_bits1 = 0; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } + if(hdlc->bit_shift==0){ + hdlc->shift_reg = 0x7e; + hdlc->state = HDLC_SEND_CLOSING_FLAG; + hdlc->bit_shift = 8; + } + break; + case HDLC_SEND_CLOSING_FLAG: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if(hdlc->hdlc_bits1 == 5){ + hdlc->hdlc_bits1 = 0; + break; + } + if(hdlc->shift_reg & 0x01){ + hdlc->cbin++; + } + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + if(hdlc->bit_shift==0){ + hdlc->ffvalue = xfast_flag_value[hdlc->data_bits]; + if(hdlc->dchannel){ + hdlc->ffvalue = 0x7e; + hdlc->state = HDLC_SEND_IDLE1; + hdlc->bit_shift = 8-hdlc->data_bits; + if(hdlc->bit_shift==0) + hdlc->state = HDLC_SEND_FAST_IDLE; + } else { + if(!hdlc->do_adapt56){ + hdlc->state = HDLC_SEND_FAST_FLAG; + hdlc->data_received = 0; + } else { + hdlc->state = HDLC_SENDFLAG_B0; + hdlc->data_received = 0; + } + // Finished with this frame, send flags + if (dsize > 1) dsize = 1; + } + } + break; + case HDLC_SEND_IDLE1: + hdlc->do_closing = 0; hdlc->cbin <<= 1; hdlc->cbin++; hdlc->data_bits++; + hdlc->bit_shift--; + if(hdlc->bit_shift==0){ + hdlc->state = HDLC_SEND_FAST_IDLE; + hdlc->bit_shift = 0; + } + break; + case HDLC_SEND_FAST_IDLE: + hdlc->do_closing = 0; + hdlc->cbin = 0xff; + hdlc->data_bits = 8; + if(hdlc->bit_shift == 8){ + hdlc->cbin = 0x7e; + hdlc->state = HDLC_SEND_FIRST_FLAG; + } else { + *dst++ = hdlc->cbin; + hdlc->bit_shift = hdlc->data_bits = 0; + len++; + dsize = 0; + } + break; + default: + break; + } + if(hdlc->do_adapt56){ + if(hdlc->data_bits==7){ + hdlc->cbin <<= 1; + hdlc->cbin++; + hdlc->data_bits++; + } + } + if(hdlc->data_bits==8){ + *dst++ = hdlc->cbin; + hdlc->data_bits = 0; + len++; + dsize--; } } - if(hdlc->data_bits==8){ - *dst++ = hdlc->cbin; - hdlc->data_bits = 0; - len++; - dsize--; - } - } - *count -= slen; + *count -= slen; - return len; + return len; } diff --git a/drivers/isdn/hisax/st5481_hdlc.h b/drivers/isdn/hisax/st5481_hdlc.h index fe2a766e..495432f0 100644 --- a/drivers/isdn/hisax/st5481_hdlc.h +++ b/drivers/isdn/hisax/st5481_hdlc.h @@ -1,5 +1,17 @@ -#ifndef __ST5481_HDLC__ -#define __ST5481_HDLC__ +/* + * Driver for ST5481 USB ISDN modem + * + * Author Frode Isaksen + * Copyright 2001 by Frode Isaksen + * 2001 by Kai Germaschewski + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + +#ifndef __ST5481_HDLC_H__ +#define __ST5481_HDLC_H__ struct hdlc_vars { int bit_shift; diff --git a/drivers/isdn/hisax/st5481_init.c b/drivers/isdn/hisax/st5481_init.c index 0d85bd91..31899950 100644 --- a/drivers/isdn/hisax/st5481_init.c +++ b/drivers/isdn/hisax/st5481_init.c @@ -1,9 +1,12 @@ -/* - * +/* * Driver for ST5481 USB ISDN modem * - * Author Frode Isaksen (fisaksen@bewan.com) - * + * Author Frode Isaksen + * Copyright 2001 by Frode Isaksen + * 2001 by Kai Germaschewski + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. * */ @@ -11,29 +14,26 @@ * TODO: * * b layer1 delay? - * d out fsm * hdlc as module * hotplug / unregister issues * mod_inc/dec_use_count * unify parts of d/b channel usb handling * file header - * PH_PAUSE? - * evt queue w/o arg? + * avoid copy to isoc buffer? + * improve usb delay? + * merge l1 state machines? + * clean up debug */ -static const char *st5481_revision = "$Revision$"; - #include #include #include #include #include -#include -#include #include "st5481.h" MODULE_AUTHOR("Frode Isaksen "); -MODULE_DESCRIPTION("ST5481 USB ISDN modem"); +MODULE_DESCRIPTION("ST5481 USB ISDN modem driver"); static int protocol = 2; /* EURO-ISDN Default */ MODULE_PARM(protocol, "i"); @@ -41,6 +41,12 @@ MODULE_PARM(protocol, "i"); static int number_of_leds = 2; /* 2 LEDs on the adpater default */ MODULE_PARM(number_of_leds, "i"); +#ifdef CONFIG_HISAX_DEBUG +static int debug = 0x1; +MODULE_PARM(debug, "i"); +int st5481_debug; +#endif + static LIST_HEAD(adapter_list); /* ====================================================================== @@ -59,6 +65,7 @@ static void * __devinit probe_st5481(struct usb_device *dev, struct hisax_b_if *b_if[2]; int retval, i; + MOD_INC_USE_COUNT; printk(KERN_INFO "st541: found adapter VendorId %04x, ProductId %04x, LEDs %d\n", dev->descriptor.idVendor, dev->descriptor.idProduct, number_of_leds); @@ -113,7 +120,7 @@ static void * __devinit probe_st5481(struct usb_device *dev, err_usb: st5481_release_usb(adapter); err: - WARN("retval %d\n", retval); + MOD_DEC_USE_COUNT; return NULL; } @@ -140,6 +147,7 @@ static void __devexit disconnect_st5481(struct usb_device *dev, void *arg) hisax_unregister(&adapter->hisax_d_if); kfree(adapter); + MOD_DEC_USE_COUNT; } /* @@ -177,10 +185,11 @@ static int __init st5481_usb_init(void) { int retval; - DBG(1,""); +#ifdef CONFIG_HISAX_DEBUG + st5481_debug = debug; +#endif - printk(KERN_INFO "st5481: ST5481 USB ISDN driver %s\n", - st5481_revision); + printk(KERN_INFO "hiax_st5481: ST5481 USB ISDN driver v0.1.0\n"); retval = st5481_d_init(); if (retval < 0) @@ -190,7 +199,6 @@ static int __init st5481_usb_init(void) if (retval < 0) goto out_d_exit; - // create_proc_read_entry("driver/st5481", 0, 0, proc_read_proc, NULL); return 0; out_d_exit: @@ -199,13 +207,10 @@ static int __init st5481_usb_init(void) return retval; } -static void __exit st5481_usb_cleanup(void) +static void __exit st5481_usb_exit(void) { - DBG(1,""); - usb_deregister(&st5481_usb_driver); - // remove_proc_entry("driver/st5481", NULL); } module_init(st5481_usb_init); -module_exit(st5481_usb_cleanup); +module_exit(st5481_usb_exit); diff --git a/drivers/isdn/hisax/st5481_usb.c b/drivers/isdn/hisax/st5481_usb.c index 40df3edd..158f2a2a 100644 --- a/drivers/isdn/hisax/st5481_usb.c +++ b/drivers/isdn/hisax/st5481_usb.c @@ -1,3 +1,15 @@ +/* + * Driver for ST5481 USB ISDN modem + * + * Author Frode Isaksen + * Copyright 2001 by Frode Isaksen + * 2001 by Kai Germaschewski + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + */ + #include #include #include @@ -103,7 +115,7 @@ void st5481_usb_pipe_reset(struct st5481_adapter *adapter, void st5481_ph_command(struct st5481_adapter *adapter, unsigned int command) { - DBG(8,"command=%s",ST5481_CMD_string(command)); + DBG(8,"command=%s", ST5481_CMD_string(command)); st5481_usb_device_ctrl_msg(adapter, TXCI, command, NULL, NULL); } @@ -183,40 +195,32 @@ static void usb_int_complete(struct urb *urb) } } - DUMP_PACKET(1, data, INT_PKT_SIZE); + DBG_PACKET(1, data, INT_PKT_SIZE); if (urb->actual_length == 0) { return; } irqbyte = data[MPINT]; - if (irqbyte & DEN_INT) { - st5481_sched_d_out_event(adapter, EV_DOUT_DEN, NULL); - } + if (irqbyte & DEN_INT) + FsmEvent(&adapter->d_out.fsm, EV_DOUT_DEN, NULL); - if (irqbyte & DCOLL_INT) { - st5481_sched_d_out_event(adapter, EV_DOUT_COLL, NULL); - } + if (irqbyte & DCOLL_INT) + FsmEvent(&adapter->d_out.fsm, EV_DOUT_COLL, NULL); irqbyte = data[FFINT_D]; - if (irqbyte & OUT_UNDERRUN) { -// printk("OUT_UNDERRUN\n"); - st5481_sched_d_out_event(adapter, EV_DOUT_UNDERRUN, NULL); - } - if (irqbyte & OUT_DOWN) { -// printk("OUT_DOWN\n"); - } + if (irqbyte & OUT_UNDERRUN) + FsmEvent(&adapter->d_out.fsm, EV_DOUT_UNDERRUN, NULL); + + if (irqbyte & OUT_DOWN) +;// printk("OUT_DOWN\n"); irqbyte = data[MPINT]; - if (irqbyte & RXCI_INT) { - DBG(8,"CI %s",ST5481_IND_string(data[CCIST] & 0x0f)); - adapter->ph_state = data[CCIST] & 0x0f; - st5481_sched_event(adapter, D_L1STATECHANGE); - } + if (irqbyte & RXCI_INT) + FsmEvent(&adapter->l1m, data[CCIST] & 0x0f, NULL); - for (j = 0; j < 2; j++) { + for (j = 0; j < 2; j++) adapter->bcs[j].b_out.flow_event |= data[FFINT_B1 + j]; - } urb->actual_length = 0; } @@ -298,8 +302,6 @@ int __devinit st5481_setup_usb(struct st5481_adapter *adapter) usb_int_complete, adapter, endpoint->bInterval); - fifo_init(&intr->evt_fifo.f, ARRAY_SIZE(intr->evt_fifo.data)); - return 0; } @@ -478,7 +480,7 @@ static void usb_in_complete(struct urb *urb) } } - DUMP_ISO_PACKET(0x80,urb); + DBG_ISO_PACKET(0x80,urb); len = st5481_isoc_flatten(urb); ptr = urb->transfer_buffer; @@ -497,7 +499,7 @@ static void usb_in_complete(struct urb *urb) if (status > 0) { // Good frame received DBG(4,"count=%d",status); - DUMP_PACKET(0x400, in->rcvbuf, status); + DBG_PACKET(0x400, in->rcvbuf, status); if (!(skb = dev_alloc_skb(status))) { WARN("receive out of memory\n"); break; @@ -602,8 +604,6 @@ static void st5481_start_rcv(void *context) DBG(4,""); - // Start receiving from B channel - in->urb[0]->dev = adapter->usb_dev; SUBMIT_URB(in->urb[0]); @@ -627,9 +627,15 @@ void st5481_in_mode(struct st5481_in *in, int mode) in->mode == L1_MODE_HDLC_56K); st5481_usb_pipe_reset(in->adapter, in->ep, NULL, NULL); +#if 0 st5481_usb_device_ctrl_msg(in->adapter, in->counter, in->packet_size, st5481_start_rcv, in); +#endif + st5481_usb_device_ctrl_msg(in->adapter, in->counter, + in->packet_size, + NULL, NULL); + st5481_start_rcv(in); } else { st5481_usb_device_ctrl_msg(in->adapter, in->counter, 0, NULL, NULL);