diff options
Diffstat (limited to 'drivers/isdn/hardware/mISDN/hfcpci.c')
-rw-r--r-- | drivers/isdn/hardware/mISDN/hfcpci.c | 239 |
1 files changed, 185 insertions, 54 deletions
diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index cd8302af40e..917bf41a293 100644 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -23,6 +23,25 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + * Module options: + * + * debug: + * NOTE: only one poll value must be given for all cards + * See hfc_pci.h for debug flags. + * + * poll: + * NOTE: only one poll value must be given for all cards + * Give the number of samples for each fifo process. + * By default 128 is used. Decrease to reduce delay, increase to + * reduce cpu load. If unsure, don't mess with it! + * A value of 128 will use controller's interrupt. Other values will + * use kernel timer, because the controller will not allow lower values + * than 128. + * Also note that the value depends on the kernel timer frequency. + * If kernel uses a frequency of 1000 Hz, steps of 8 samples are possible. + * If the kernel uses 100 Hz, steps of 80 samples are possible. + * If the kernel uses 300 Hz, steps of about 26 samples are possible. + * */ #include <linux/module.h> @@ -34,16 +53,16 @@ static const char *hfcpci_revision = "2.0"; -#define MAX_CARDS 8 static int HFC_cnt; static uint debug; +static uint poll, tics; +struct timer_list hfc_tl; +u32 hfc_jiffies; MODULE_AUTHOR("Karsten Keil"); MODULE_LICENSE("GPL"); module_param(debug, uint, 0); - -static LIST_HEAD(HFClist); -static DEFINE_RWLOCK(HFClock); +module_param(poll, uint, S_IRUGO | S_IWUSR); enum { HFC_CCD_2BD0, @@ -114,7 +133,6 @@ struct hfcPCI_hw { struct hfc_pci { - struct list_head list; u_char subtype; u_char chanlimit; u_char initdone; @@ -520,9 +538,9 @@ receive_dmsg(struct hfc_pci *hc) } /* - * check for transparent receive data and read max one threshold size if avail + * check for transparent receive data and read max one 'poll' size if avail */ -static int +static void hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *bz, u_char *bdata) { __le16 *z1r, *z2r; @@ -534,17 +552,19 @@ hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *bz, u_char *bdata) fcnt = le16_to_cpu(*z1r) - le16_to_cpu(*z2r); if (!fcnt) - return 0; /* no data avail */ + return; /* no data avail */ if (fcnt <= 0) fcnt += B_FIFO_SIZE; /* bytes actually buffered */ - if (fcnt > HFCPCI_BTRANS_THRESHOLD) - fcnt = HFCPCI_BTRANS_THRESHOLD; /* limit size */ - new_z2 = le16_to_cpu(*z2r) + fcnt; /* new position in fifo */ if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) new_z2 -= B_FIFO_SIZE; /* buffer wrap */ + if (fcnt > MAX_DATA_SIZE) { /* flush, if oversized */ + *z2r = cpu_to_le16(new_z2); /* new position */ + return; + } + bch->rx_skb = mI_alloc_skb(fcnt, GFP_ATOMIC); if (bch->rx_skb) { ptr = skb_put(bch->rx_skb, fcnt); @@ -569,7 +589,6 @@ hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *bz, u_char *bdata) printk(KERN_WARNING "HFCPCI: receive out of memory\n"); *z2r = cpu_to_le16(new_z2); /* new position */ - return 1; } /* @@ -580,12 +599,11 @@ main_rec_hfcpci(struct bchannel *bch) { struct hfc_pci *hc = bch->hw; int rcnt, real_fifo; - int receive, count = 5; + int receive = 0, count = 5; struct bzfifo *bz; u_char *bdata; struct zt *zp; - if ((bch->nr & 2) && (!hc->hw.bswapped)) { bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2; bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b2; @@ -625,9 +643,10 @@ Begin: receive = 1; else receive = 0; - } else if (test_bit(FLG_TRANSPARENT, &bch->Flags)) - receive = hfcpci_empty_fifo_trans(bch, bz, bdata); - else + } else if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { + hfcpci_empty_fifo_trans(bch, bz, bdata); + return; + } else receive = 0; if (count && receive) goto Begin; @@ -751,11 +770,41 @@ hfcpci_fill_fifo(struct bchannel *bch) /* fcnt contains available bytes in fifo */ fcnt = B_FIFO_SIZE - fcnt; /* remaining bytes to send (bytes in fifo) */ + + /* "fill fifo if empty" feature */ + if (test_bit(FLG_FILLEMPTY, &bch->Flags) && !fcnt) { + /* printk(KERN_DEBUG "%s: buffer empty, so we have " + "underrun\n", __func__); */ + /* fill buffer, to prevent future underrun */ + count = HFCPCI_FILLEMPTY; + new_z1 = le16_to_cpu(*z1t) + count; + /* new buffer Position */ + if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z1 -= B_FIFO_SIZE; /* buffer wrap */ + dst = bdata + (le16_to_cpu(*z1t) - B_SUB_VAL); + maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(*z1t); + /* end of fifo */ + if (bch->debug & DEBUG_HW_BFIFO) + printk(KERN_DEBUG "hfcpci_FFt fillempty " + "fcnt(%d) maxl(%d) nz1(%x) dst(%p)\n", + fcnt, maxlen, new_z1, dst); + fcnt += count; + if (maxlen > count) + maxlen = count; /* limit size */ + memset(dst, 0x2a, maxlen); /* first copy */ + count -= maxlen; /* remaining bytes */ + if (count) { + dst = bdata; /* start of buffer */ + memset(dst, 0x2a, count); + } + *z1t = cpu_to_le16(new_z1); /* now send data */ + } + next_t_frame: count = bch->tx_skb->len - bch->tx_idx; - /* maximum fill shall be HFCPCI_BTRANS_MAX */ - if (count > HFCPCI_BTRANS_MAX - fcnt) - count = HFCPCI_BTRANS_MAX - fcnt; + /* maximum fill shall be poll*2 */ + if (count > (poll << 1) - fcnt) + count = (poll << 1) - fcnt; if (count <= 0) return; /* data is suitable for fifo */ @@ -1135,37 +1184,37 @@ hfcpci_int(int intno, void *dev_id) val &= ~0x80; Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER); } - if (val & 0x08) { + if (val & 0x08) { /* B1 rx */ bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); if (bch) main_rec_hfcpci(bch); else if (hc->dch.debug) printk(KERN_DEBUG "hfcpci spurious 0x08 IRQ\n"); } - if (val & 0x10) { + if (val & 0x10) { /* B2 rx */ bch = Sel_BCS(hc, 2); if (bch) main_rec_hfcpci(bch); else if (hc->dch.debug) printk(KERN_DEBUG "hfcpci spurious 0x10 IRQ\n"); } - if (val & 0x01) { + if (val & 0x01) { /* B1 tx */ bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); if (bch) tx_birq(bch); else if (hc->dch.debug) printk(KERN_DEBUG "hfcpci spurious 0x01 IRQ\n"); } - if (val & 0x02) { + if (val & 0x02) { /* B2 tx */ bch = Sel_BCS(hc, 2); if (bch) tx_birq(bch); else if (hc->dch.debug) printk(KERN_DEBUG "hfcpci spurious 0x02 IRQ\n"); } - if (val & 0x20) + if (val & 0x20) /* D rx */ receive_dmsg(hc); - if (val & 0x04) { /* dframe transmitted */ + if (val & 0x04) { /* D tx */ if (test_and_clear_bit(FLG_BUSY_TIMER, &hc->dch.Flags)) del_timer(&hc->dch.timer); tx_dirq(&hc->dch); @@ -1283,14 +1332,16 @@ mode_hfcpci(struct bchannel *bch, int bc, int protocol) } if (fifo2 & 2) { hc->hw.fifo_en |= HFCPCI_FIFOEN_B2; - hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS + - HFCPCI_INTS_B2REC); + if (!tics) + hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS + + HFCPCI_INTS_B2REC); hc->hw.ctmt |= 2; hc->hw.conn &= ~0x18; } else { hc->hw.fifo_en |= HFCPCI_FIFOEN_B1; - hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS + - HFCPCI_INTS_B1REC); + if (!tics) + hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS + + HFCPCI_INTS_B1REC); hc->hw.ctmt |= 1; hc->hw.conn &= ~0x03; } @@ -1398,7 +1449,8 @@ set_hfcpci_rxtest(struct bchannel *bch, int protocol, int chan) if (chan & 2) { hc->hw.sctrl_r |= SCTRL_B2_ENA; hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX; - hc->hw.int_m1 |= HFCPCI_INTS_B2REC; + if (!tics) + hc->hw.int_m1 |= HFCPCI_INTS_B2REC; hc->hw.ctmt |= 2; hc->hw.conn &= ~0x18; #ifdef REVERSE_BITORDER @@ -1407,7 +1459,8 @@ set_hfcpci_rxtest(struct bchannel *bch, int protocol, int chan) } else { hc->hw.sctrl_r |= SCTRL_B1_ENA; hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX; - hc->hw.int_m1 |= HFCPCI_INTS_B1REC; + if (!tics) + hc->hw.int_m1 |= HFCPCI_INTS_B1REC; hc->hw.ctmt |= 1; hc->hw.conn &= ~0x03; #ifdef REVERSE_BITORDER @@ -1481,11 +1534,17 @@ deactivate_bchannel(struct bchannel *bch) static int channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) { - int ret = 0; + int ret = 0; switch (cq->op) { case MISDN_CTRL_GETOP: - cq->op = 0; + cq->op = MISDN_CTRL_FILL_EMPTY; + break; + case MISDN_CTRL_FILL_EMPTY: /* fill fifo, if empty */ + test_and_set_bit(FLG_FILLEMPTY, &bch->Flags); + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: FILL_EMPTY request (nr=%d " + "off=%d)\n", __func__, bch->nr, !!cq->p1); break; default: printk(KERN_WARNING "%s: unknown Op %x\n", __func__, cq->op); @@ -1859,6 +1918,10 @@ open_dchannel(struct hfc_pci *hc, struct mISDNchannel *ch, hc->dch.dev.id, __builtin_return_address(0)); if (rq->protocol == ISDN_P_NONE) return -EINVAL; + if (rq->adr.channel == 1) { + /* TODO: E-Channel */ + return -EINVAL; + } if (!hc->initdone) { if (rq->protocol == ISDN_P_TE_S0) { err = create_l1(&hc->dch, hfc_l1callback); @@ -1874,6 +1937,11 @@ open_dchannel(struct hfc_pci *hc, struct mISDNchannel *ch, if (rq->protocol != ch->protocol) { if (hc->hw.protocol == ISDN_P_TE_S0) l1_event(hc->dch.l1, CLOSE_CHANNEL); + if (rq->protocol == ISDN_P_TE_S0) { + err = create_l1(&hc->dch, hfc_l1callback); + if (err) + return err; + } hc->hw.protocol = rq->protocol; ch->protocol = rq->protocol; hfcpci_setmode(hc); @@ -1903,6 +1971,7 @@ open_bchannel(struct hfc_pci *hc, struct channel_req *rq) bch = &hc->bch[rq->adr.channel - 1]; if (test_and_set_bit(FLG_OPEN, &bch->Flags)) return -EBUSY; /* b-channel can be only open once */ + test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags); bch->ch.protocol = rq->protocol; rq->ch = &bch->ch; /* TODO: E-channel */ if (!try_module_get(THIS_MODULE)) @@ -1928,7 +1997,8 @@ hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) switch (cmd) { case OPEN_CHANNEL: rq = arg; - if (rq->adr.channel == 0) + if ((rq->protocol == ISDN_P_TE_S0) || + (rq->protocol == ISDN_P_NT_S0)) err = open_dchannel(hc, ch, rq); else err = open_bchannel(hc, rq); @@ -2027,7 +2097,6 @@ release_card(struct hfc_pci *hc) { mISDN_freebchannel(&hc->bch[1]); mISDN_freebchannel(&hc->bch[0]); mISDN_freedchannel(&hc->dch); - list_del(&hc->list); pci_set_drvdata(hc->pdev, NULL); kfree(hc); } @@ -2037,12 +2106,8 @@ setup_card(struct hfc_pci *card) { int err = -EINVAL; u_int i; - u_long flags; char name[MISDN_MAX_IDLEN]; - if (HFC_cnt >= MAX_CARDS) - return -EINVAL; /* maybe better value */ - card->dch.debug = debug; spin_lock_init(&card->lock); mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, ph_state); @@ -2068,13 +2133,10 @@ setup_card(struct hfc_pci *card) if (err) goto error; snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-pci.%d", HFC_cnt + 1); - err = mISDN_register_device(&card->dch.dev, name); + err = mISDN_register_device(&card->dch.dev, &card->pdev->dev, name); if (err) goto error; HFC_cnt++; - write_lock_irqsave(&HFClock, flags); - list_add_tail(&card->list, &HFClist); - write_unlock_irqrestore(&HFClock, flags); printk(KERN_INFO "HFC %d cards installed\n", HFC_cnt); return 0; error: @@ -2210,15 +2272,12 @@ static void __devexit hfc_remove_pci(struct pci_dev *pdev) { struct hfc_pci *card = pci_get_drvdata(pdev); - u_long flags; - if (card) { - write_lock_irqsave(&HFClock, flags); + if (card) release_card(card); - write_unlock_irqrestore(&HFClock, flags); - } else + else if (debug) - printk(KERN_WARNING "%s: drvdata allready removed\n", + printk(KERN_WARNING "%s: drvdata already removed\n", __func__); } @@ -2230,25 +2289,97 @@ static struct pci_driver hfc_driver = { .id_table = hfc_ids, }; +static int +_hfcpci_softirq(struct device *dev, void *arg) +{ + struct hfc_pci *hc = dev_get_drvdata(dev); + struct bchannel *bch; + if (hc == NULL) + return 0; + + if (hc->hw.int_m2 & HFCPCI_IRQ_ENABLE) { + spin_lock(&hc->lock); + bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); + if (bch && bch->state == ISDN_P_B_RAW) { /* B1 rx&tx */ + main_rec_hfcpci(bch); + tx_birq(bch); + } + bch = Sel_BCS(hc, hc->hw.bswapped ? 1 : 2); + if (bch && bch->state == ISDN_P_B_RAW) { /* B2 rx&tx */ + main_rec_hfcpci(bch); + tx_birq(bch); + } + spin_unlock(&hc->lock); + } + return 0; +} + +static void +hfcpci_softirq(void *arg) +{ + (void) driver_for_each_device(&hfc_driver.driver, NULL, arg, + _hfcpci_softirq); + + /* if next event would be in the past ... */ + if ((s32)(hfc_jiffies + tics - jiffies) <= 0) + hfc_jiffies = jiffies + 1; + else + hfc_jiffies += tics; + hfc_tl.expires = hfc_jiffies; + add_timer(&hfc_tl); +} + static int __init HFC_init(void) { int err; + if (!poll) + poll = HFCPCI_BTRANS_THRESHOLD; + + if (poll != HFCPCI_BTRANS_THRESHOLD) { + tics = (poll * HZ) / 8000; + if (tics < 1) + tics = 1; + poll = (tics * 8000) / HZ; + if (poll > 256 || poll < 8) { + printk(KERN_ERR "%s: Wrong poll value %d not in range " + "of 8..256.\n", __func__, poll); + err = -EINVAL; + return err; + } + } + if (poll != HFCPCI_BTRANS_THRESHOLD) { + printk(KERN_INFO "%s: Using alternative poll value of %d\n", + __func__, poll); + hfc_tl.function = (void *)hfcpci_softirq; + hfc_tl.data = 0; + init_timer(&hfc_tl); + hfc_tl.expires = jiffies + tics; + hfc_jiffies = hfc_tl.expires; + add_timer(&hfc_tl); + } else + tics = 0; /* indicate the use of controller's timer */ + err = pci_register_driver(&hfc_driver); + if (err) { + if (timer_pending(&hfc_tl)) + del_timer(&hfc_tl); + } + return err; } static void __exit HFC_cleanup(void) { - struct hfc_pci *card, *next; + if (timer_pending(&hfc_tl)) + del_timer(&hfc_tl); - list_for_each_entry_safe(card, next, &HFClist, list) { - release_card(card); - } pci_unregister_driver(&hfc_driver); } module_init(HFC_init); module_exit(HFC_cleanup); + +MODULE_DEVICE_TABLE(pci, hfc_ids); |