From 69e656cc16511719a89d83373c48172d3f39bc5f Mon Sep 17 00:00:00 2001 From: Karsten Keil Date: Wed, 7 Jan 2009 00:00:59 +0100 Subject: mISDN: Cleanup hfc multiport driver Remove references to external zaptel modules and some cosmetic cleanups. Signed-off-by: Karsten Keil --- drivers/isdn/hardware/mISDN/hfc_multi.h | 4 -- drivers/isdn/hardware/mISDN/hfcmulti.c | 109 +++++++------------------------- 2 files changed, 24 insertions(+), 89 deletions(-) diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h index 7bbf7300593..6d74951a179 100644 --- a/drivers/isdn/hardware/mISDN/hfc_multi.h +++ b/drivers/isdn/hardware/mISDN/hfc_multi.h @@ -2,10 +2,6 @@ * see notice in hfc_multi.c */ -extern void ztdummy_extern_interrupt(void); -extern void ztdummy_register_interrupt(void); -extern int ztdummy_unregister_interrupt(void); - #define DEBUG_HFCMULTI_FIFO 0x00010000 #define DEBUG_HFCMULTI_CRC 0x00020000 #define DEBUG_HFCMULTI_INIT 0x00040000 diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c index c63e2f49da8..ff5ec3cbeb7 100644 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -140,7 +140,7 @@ * #define HFC_REGISTER_DEBUG */ -static const char *hfcmulti_revision = "2.02"; +#define HFC_MULTI_VERSION "2.03" #include #include @@ -165,10 +165,6 @@ static LIST_HEAD(HFClist); static spinlock_t HFClock; /* global hfc list lock */ static void ph_state_change(struct dchannel *); -static void (*hfc_interrupt)(void); -static void (*register_interrupt)(void); -static int (*unregister_interrupt)(void); -static int interrupt_registered; static struct hfc_multi *syncmaster; static int plxsd_master; /* if we have a master card (yet) */ @@ -209,6 +205,7 @@ static int HFC_cnt, Port_cnt, PCM_cnt = 99; MODULE_AUTHOR("Andreas Eversberg"); MODULE_LICENSE("GPL"); +MODULE_VERSION(HFC_MULTI_VERSION); module_param(debug, uint, S_IRUGO | S_IWUSR); module_param(poll, uint, S_IRUGO | S_IWUSR); module_param(timer, uint, S_IRUGO | S_IWUSR); @@ -1419,19 +1416,6 @@ controller_fail: HFC_outb(hc, R_TI_WD, poll_timer); hc->hw.r_irqmsk_misc |= V_TI_IRQMSK; - /* - * set up 125us interrupt, only if function pointer is available - * and module parameter timer is set - */ - if (timer && hfc_interrupt && register_interrupt) { - /* only one chip should use this interrupt */ - timer = 0; - interrupt_registered = 1; - hc->hw.r_irqmsk_misc |= V_PROC_IRQMSK; - /* deactivate other interrupts in ztdummy */ - register_interrupt(); - } - /* set E1 state machine IRQ */ if (hc->type == 1) hc->hw.r_irqmsk_misc |= V_STA_IRQMSK; @@ -2583,7 +2567,6 @@ hfcmulti_interrupt(int intno, void *dev_id) static int iq1 = 0, iq2 = 0, iq3 = 0, iq4 = 0, iq5 = 0, iq6 = 0, iqcnt = 0; #endif - static int count; struct hfc_multi *hc = dev_id; struct dchannel *dch; u_char r_irq_statech, status, r_irq_misc, r_irq_oview; @@ -2698,16 +2681,11 @@ hfcmulti_interrupt(int intno, void *dev_id) /* -> DTMF IRQ */ hfcmulti_dtmf(hc); } - /* TODO: REPLACE !!!! 125 us Interrupts are not acceptable */ if (r_irq_misc & V_IRQ_PROC) { - /* IRQ every 125us */ - count++; - /* generate 1kHz signal */ - if (count == 8) { - if (hfc_interrupt) - hfc_interrupt(); - count = 0; - } + static int irq_proc_cnt; + if (!irq_proc_cnt++) + printk(KERN_WARNING "%s: got V_IRQ_PROC -" + " this should not happen\n", __func__); } } @@ -4945,9 +4923,7 @@ hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent) switch (m->dip_type) { case DIP_4S: /* - * get DIP Setting for beroNet 1S/2S/4S cards - * check if Port Jumper config matches - * module param 'protocol' + * Get DIP setting for beroNet 1S/2S/4S cards * DIP Setting: (collect GPIO 13/14/15 (R_GPIO_IN1) + * GPI 19/23 (R_GPI_IN2)) */ @@ -4966,9 +4942,8 @@ hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent) break; case DIP_8S: /* - * get DIP Setting for beroNet 8S0+ cards - * - * enable PCI auxbridge function + * Get DIP Setting for beroNet 8S0+ cards + * Enable PCI auxbridge function */ HFC_outb(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK); /* prepare access to auxport */ @@ -5137,8 +5112,7 @@ static struct pci_device_id hfmultipci_ids[] __devinitdata = { { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, 0, 0, H(14)}, /* old Eval */ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, - PCI_SUBDEVICE_ID_CCD_IOB8STR, 0, 0, H(15)}, - /* IOB8ST Recording */ + PCI_SUBDEVICE_ID_CCD_IOB8STR, 0, 0, H(15)}, /* IOB8ST Recording */ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, PCI_SUBDEVICE_ID_CCD_IOB8ST, 0, 0, H(16)}, /* IOB8ST */ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, @@ -5188,18 +5162,16 @@ hfcmulti_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct hm_map *m = (struct hm_map *)ent->driver_data; int ret; - if (m == NULL) { - if (ent->vendor == PCI_VENDOR_ID_CCD) - if (ent->device == PCI_DEVICE_ID_CCD_HFC4S || - ent->device == PCI_DEVICE_ID_CCD_HFC8S || - ent->device == PCI_DEVICE_ID_CCD_HFCE1) - printk(KERN_ERR - "unknown HFC multiport controller " - "(vendor:%x device:%x subvendor:%x " - "subdevice:%x) Please contact the " - "driver maintainer for support.\n", - ent->vendor, ent->device, - ent->subvendor, ent->subdevice); + if (m == NULL && ent->vendor == PCI_VENDOR_ID_CCD && ( + ent->device == PCI_DEVICE_ID_CCD_HFC4S || + ent->device == PCI_DEVICE_ID_CCD_HFC8S || + ent->device == PCI_DEVICE_ID_CCD_HFCE1)) { + printk(KERN_ERR + "Unknown HFC multiport controller (vendor:%x device:%x " + "subvendor:%x subdevice:%x)\n", ent->vendor, ent->device, + ent->subvendor, ent->subdevice); + printk(KERN_ERR + "Please contact the driver maintainer for support.\n"); return -ENODEV; } ret = hfcmulti_init(pdev, ent); @@ -5222,22 +5194,9 @@ HFCmulti_cleanup(void) { struct hfc_multi *card, *next; - /* unload interrupt function symbol */ - if (hfc_interrupt) - symbol_put(ztdummy_extern_interrupt); - if (register_interrupt) - symbol_put(ztdummy_register_interrupt); - if (unregister_interrupt) { - if (interrupt_registered) { - interrupt_registered = 0; - unregister_interrupt(); - } - symbol_put(ztdummy_unregister_interrupt); - } - + /* get rid of all devices of this driver */ list_for_each_entry_safe(card, next, &HFClist, list) release_card(card); - /* get rid of all devices of this driver */ pci_unregister_driver(&hfcmultipci_driver); } @@ -5246,8 +5205,10 @@ HFCmulti_init(void) { int err; + printk(KERN_INFO "mISDN: HFC-multi driver %s\n", HFC_MULTI_VERSION); + #ifdef IRQ_DEBUG - printk(KERN_ERR "%s: IRQ_DEBUG IS ENABLED!\n", __func__); + printk(KERN_DEBUG "%s: IRQ_DEBUG IS ENABLED!\n", __func__); #endif spin_lock_init(&HFClock); @@ -5256,22 +5217,11 @@ HFCmulti_init(void) if (debug & DEBUG_HFCMULTI_INIT) printk(KERN_DEBUG "%s: init entered\n", __func__); - hfc_interrupt = symbol_get(ztdummy_extern_interrupt); - register_interrupt = symbol_get(ztdummy_register_interrupt); - unregister_interrupt = symbol_get(ztdummy_unregister_interrupt); - printk(KERN_INFO "mISDN: HFC-multi driver %s\n", - hfcmulti_revision); - switch (poll) { case 0: poll_timer = 6; poll = 128; break; - /* - * wenn dieses break nochmal verschwindet, - * gibt es heisse ohren :-) - * "without the break you will get hot ears ???" - */ case 8: poll_timer = 2; break; @@ -5301,17 +5251,6 @@ HFCmulti_init(void) err = pci_register_driver(&hfcmultipci_driver); if (err < 0) { printk(KERN_ERR "error registering pci driver: %x\n", err); - if (hfc_interrupt) - symbol_put(ztdummy_extern_interrupt); - if (register_interrupt) - symbol_put(ztdummy_register_interrupt); - if (unregister_interrupt) { - if (interrupt_registered) { - interrupt_registered = 0; - unregister_interrupt(); - } - symbol_put(ztdummy_unregister_interrupt); - } return err; } return 0; -- cgit v1.2.3 From 8dd2f36f317569665e454268a2677cfba3e848f1 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 2 Aug 2008 22:51:52 +0200 Subject: mISDN: Add feature via MISDN_CTRL_FILL_EMPTY to fill fifo if empty This prevents underrun of fifo when filled and in case of an underrun it prevents subsequent underruns due to jitter. Improve dsp, so buffers are kept filled with a certain delay, so moderate jitter will not cause underrun all the time -> the audio quality is highly improved. tones are not interrupted by gaps anymore, except when CPU is stalling or in high load. Signed-off-by: Andreas Eversberg Signed-off-by: Karsten Keil --- drivers/isdn/hardware/mISDN/hfc_multi.h | 3 ++ drivers/isdn/hardware/mISDN/hfc_pci.h | 1 + drivers/isdn/hardware/mISDN/hfcmulti.c | 45 ++++++++++++++++++++------ drivers/isdn/hardware/mISDN/hfcpci.c | 41 ++++++++++++++++++++++-- drivers/isdn/mISDN/dsp.h | 1 + drivers/isdn/mISDN/dsp_cmx.c | 56 ++++++++++++++++++++++----------- drivers/isdn/mISDN/dsp_core.c | 37 ++++++++++++++++++++-- include/linux/mISDNhw.h | 25 ++++++++------- include/linux/mISDNif.h | 1 + 9 files changed, 165 insertions(+), 45 deletions(-) diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h index 6d74951a179..4aa6a8b41f5 100644 --- a/drivers/isdn/hardware/mISDN/hfc_multi.h +++ b/drivers/isdn/hardware/mISDN/hfc_multi.h @@ -9,6 +9,7 @@ #define DEBUG_HFCMULTI_MODE 0x00100000 #define DEBUG_HFCMULTI_MSG 0x00200000 #define DEBUG_HFCMULTI_STATE 0x00400000 +#define DEBUG_HFCMULTI_FILL 0x00800000 #define DEBUG_HFCMULTI_SYNC 0x01000000 #define DEBUG_HFCMULTI_DTMF 0x02000000 #define DEBUG_HFCMULTI_LOCK 0x80000000 @@ -166,6 +167,8 @@ struct hfc_multi { u_long chip; /* chip configuration */ int masterclk; /* port that provides master clock -1=off */ + unsigned char silence;/* silence byte */ + unsigned char silence_data[128];/* silence block */ int dtmf; /* flag that dtmf is currently in process */ int Flen; /* F-buffer size */ int Zlen; /* Z-buffer size (must be int for calculation)*/ diff --git a/drivers/isdn/hardware/mISDN/hfc_pci.h b/drivers/isdn/hardware/mISDN/hfc_pci.h index 5783d22a18f..fd9241ab180 100644 --- a/drivers/isdn/hardware/mISDN/hfc_pci.h +++ b/drivers/isdn/hardware/mISDN/hfc_pci.h @@ -27,6 +27,7 @@ */ #define HFCPCI_BTRANS_THRESHOLD 128 #define HFCPCI_BTRANS_MAX 256 +#define HFCPCI_FILLEMPTY 64 #define HFCPCI_BTRANS_THRESMASK 0x00 /* defines for PCI config */ diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c index ff5ec3cbeb7..3fc2e9d9534 100644 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -180,7 +180,6 @@ static int nt_t1_count[] = { 3840, 1920, 960, 480, 240, 120, 60, 30 }; #define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ #define CLKDEL_NT 0x6c /* CLKDEL in NT mode (0x60 MUST be included!) */ -static u_char silence = 0xff; /* silence by LAW */ #define DIP_4S 0x1 /* DIP Switches for Beronet 1S/2S/4S cards */ #define DIP_8S 0x2 /* DIP Switches for Beronet 8S+ cards */ @@ -1975,6 +1974,17 @@ next_frame: return; /* no data */ } + /* "fill fifo if empty" feature */ + if (bch && test_bit(FLG_FILLEMPTY, &bch->Flags) + && !test_bit(FLG_HDLC, &bch->Flags) && z2 == z1) { + if (debug & DEBUG_HFCMULTI_FILL) + printk(KERN_DEBUG "%s: buffer empty, so we have " + "underrun\n", __func__); + /* fill buffer, to prevent future underrun */ + hc->write_fifo(hc, hc->silence_data, poll >> 1); + Zspace -= (poll >> 1); + } + /* if audio data and connected slot */ if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && (!*txpending) && slot_tx >= 0) { @@ -2011,7 +2021,6 @@ next_frame: __func__, hc->id + 1, ch, Zspace, z1, z2, ii-i, len-i, temp ? "HDLC":"TRANS"); - /* Have to prep the audio data */ hc->write_fifo(hc, d, ii - i); *idxp = ii; @@ -2050,7 +2059,7 @@ next_frame: * no more data at all. this prevents sending an undefined value. */ if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags)) - HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence); + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence); } @@ -2932,7 +2941,7 @@ mode_hfcmulti(struct hfc_multi *hc, int ch, int protocol, int slot_tx, HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); HFC_wait(hc); /* tx silence */ - HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence); + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence); HFC_outb(hc, R_SLOT, (((ch / 4) * 8) + ((ch % 4) * 4)) << 1); HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1)); @@ -2947,7 +2956,7 @@ mode_hfcmulti(struct hfc_multi *hc, int ch, int protocol, int slot_tx, HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); HFC_wait(hc); /* tx silence */ - HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence); + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence); /* enable RX fifo */ HFC_outb(hc, R_FIFO, (ch<<1)|1); HFC_wait(hc); @@ -3439,7 +3448,7 @@ channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) switch (cq->op) { case MISDN_CTRL_GETOP: cq->op = MISDN_CTRL_HFC_OP | MISDN_CTRL_HW_FEATURES_OP - | MISDN_CTRL_RX_OFF; + | MISDN_CTRL_RX_OFF | MISDN_CTRL_FILL_EMPTY; break; case MISDN_CTRL_RX_OFF: /* turn off / on rx stream */ hc->chan[bch->slot].rx_off = !!cq->p1; @@ -3454,6 +3463,12 @@ channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) printk(KERN_DEBUG "%s: RX_OFF request (nr=%d off=%d)\n", __func__, bch->nr, hc->chan[bch->slot].rx_off); break; + case MISDN_CTRL_FILL_EMPTY: /* fill fifo, if empty */ + test_and_set_bit(FLG_FILLEMPTY, &bch->Flags); + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: FILL_EMPTY request (nr=%d " + "off=%d)\n", __func__, bch->nr, !!cq->p1); + break; case MISDN_CTRL_HW_FEATURES: /* fill features structure */ if (debug & DEBUG_HFCMULTI_MSG) printk(KERN_DEBUG "%s: HW_FEATURE request\n", @@ -3970,6 +3985,7 @@ open_bchannel(struct hfc_multi *hc, struct dchannel *dch, } 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; hc->chan[ch].rx_off = 0; rq->ch = &bch->ch; @@ -4806,6 +4822,7 @@ hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent) struct hfc_multi *hc; u_long flags; u_char dips = 0, pmj = 0; /* dip settings, port mode Jumpers */ + int i; if (HFC_cnt >= MAX_CARDS) { printk(KERN_ERR "too many cards (max=%d).\n", @@ -4839,11 +4856,11 @@ hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent) hc->id = HFC_cnt; hc->pcm = pcm[HFC_cnt]; hc->io_mode = iomode[HFC_cnt]; - if (dslot[HFC_cnt] < 0) { + if (dslot[HFC_cnt] < 0 && hc->type == 1) { hc->dslot = 0; printk(KERN_INFO "HFC-E1 card has disabled D-channel, but " "31 B-channels\n"); - } if (dslot[HFC_cnt] > 0 && dslot[HFC_cnt] < 32) { + } if (dslot[HFC_cnt] > 0 && dslot[HFC_cnt] < 32 && hc->type == 1) { hc->dslot = dslot[HFC_cnt]; printk(KERN_INFO "HFC-E1 card has alternating D-channel on " "time slot %d\n", dslot[HFC_cnt]); @@ -4854,9 +4871,17 @@ hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent) hc->masterclk = -1; if (type[HFC_cnt] & 0x100) { test_and_set_bit(HFC_CHIP_ULAW, &hc->chip); - silence = 0xff; /* ulaw silence */ + hc->silence = 0xff; /* ulaw silence */ } else - silence = 0x2a; /* alaw silence */ + hc->silence = 0x2a; /* alaw silence */ + if ((poll >> 1) > sizeof(hc->silence_data)) { + printk(KERN_ERR "HFCMULTI error: silence_data too small, " + "please fix\n"); + return -EINVAL; + } + for (i = 0; i < (poll >> 1); i++) + hc->silence_data[i] = hc->silence; + if (!(type[HFC_cnt] & 0x200)) test_and_set_bit(HFC_CHIP_DTMF, &hc->chip); diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index cd8302af40e..80c356e5dbe 100644 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -751,6 +751,36 @@ 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 */ @@ -1481,11 +1511,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); @@ -1903,6 +1939,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)) diff --git a/drivers/isdn/mISDN/dsp.h b/drivers/isdn/mISDN/dsp.h index 6c3fed6b8d4..ded94806933 100644 --- a/drivers/isdn/mISDN/dsp.h +++ b/drivers/isdn/mISDN/dsp.h @@ -198,6 +198,7 @@ struct dsp { /* hardware stuff */ struct dsp_features features; int features_rx_off; /* set if rx_off is featured */ + int features_fill_empty; /* set if fill_empty is featured */ int pcm_slot_rx; /* current PCM slot (or -1) */ int pcm_bank_rx; int pcm_slot_tx; diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c index c884511e2d4..fc8ea41ae6a 100644 --- a/drivers/isdn/mISDN/dsp_cmx.c +++ b/drivers/isdn/mISDN/dsp_cmx.c @@ -1168,11 +1168,18 @@ dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb) dsp->rx_init = 0; if (dsp->features.unordered) { dsp->rx_R = (hh->id & CMX_BUFF_MASK); - dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) - & CMX_BUFF_MASK; + if (dsp->cmx_delay) + dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) + & CMX_BUFF_MASK; + else + dsp->rx_W = (dsp->rx_R + (dsp_poll >> 1)) + & CMX_BUFF_MASK; } else { dsp->rx_R = 0; - dsp->rx_W = dsp->cmx_delay; + if (dsp->cmx_delay) + dsp->rx_W = dsp->cmx_delay; + else + dsp->rx_W = dsp_poll >> 1; } } /* if frame contains time code, write directly */ @@ -1190,14 +1197,20 @@ dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb) "cmx_receive(dsp=%lx): UNDERRUN (or overrun the " "maximum delay), adjusting read pointer! " "(inst %s)\n", (u_long)dsp, dsp->name); - /* flush buffer */ + /* flush rx buffer and set delay to dsp_poll / 2 */ if (dsp->features.unordered) { dsp->rx_R = (hh->id & CMX_BUFF_MASK); - dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) - & CMX_BUFF_MASK; + if (dsp->cmx_delay) + dsp->rx_W = (dsp->rx_R + dsp->cmx_delay) + & CMX_BUFF_MASK; + dsp->rx_W = (dsp->rx_R + (dsp_poll >> 1)) + & CMX_BUFF_MASK; } else { dsp->rx_R = 0; - dsp->rx_W = dsp->cmx_delay; + if (dsp->cmx_delay) + dsp->rx_W = dsp->cmx_delay; + else + dsp->rx_W = dsp_poll >> 1; } memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff)); } @@ -1360,8 +1373,11 @@ dsp_cmx_send_member(struct dsp *dsp, int len, s32 *c, int members) t = (t+1) & CMX_BUFF_MASK; r = (r+1) & CMX_BUFF_MASK; } - if (r != rr) + if (r != rr) { + printk(KERN_DEBUG "%s: buffer empty\n", + __func__); memset(d, dsp_silence, (rr-r)&CMX_BUFF_MASK); + } /* -> if echo is enabled */ } else { /* @@ -1704,9 +1720,10 @@ dsp_cmx_send(void *arg) } /* * remove rx_delay only if we have delay AND we - * have not preset cmx_delay + * have not preset cmx_delay AND + * the delay is greater dsp_poll */ - if (delay && !dsp->cmx_delay) { + if (delay > dsp_poll && !dsp->cmx_delay) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s lowest rx_delay of %d bytes for" @@ -1714,7 +1731,8 @@ dsp_cmx_send(void *arg) __func__, delay, dsp->name); r = dsp->rx_R; - rr = (r + delay) & CMX_BUFF_MASK; + rr = (r + delay - (dsp_poll >> 1)) + & CMX_BUFF_MASK; /* delete rx-data */ while (r != rr) { p[r] = dsp_silence; @@ -1736,7 +1754,7 @@ dsp_cmx_send(void *arg) * remove delay only if we have delay AND we * have enabled tx_dejitter */ - if (delay && dsp->tx_dejitter) { + if (delay > dsp_poll && dsp->tx_dejitter) { if (dsp_debug & DEBUG_DSP_CMX) printk(KERN_DEBUG "%s lowest tx_delay of %d bytes for" @@ -1744,7 +1762,8 @@ dsp_cmx_send(void *arg) __func__, delay, dsp->name); r = dsp->tx_R; - rr = (r + delay) & CMX_BUFF_MASK; + rr = (r + delay - (dsp_poll >> 1)) + & CMX_BUFF_MASK; /* delete tx-data */ while (r != rr) { q[r] = dsp_silence; @@ -1797,14 +1816,13 @@ dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb) ww = dsp->tx_R; p = dsp->tx_buff; d = skb->data; - space = ww-w; - if (space <= 0) - space += CMX_BUFF_SIZE; + space = (ww - w - 1) & CMX_BUFF_MASK; /* write-pointer should not overrun nor reach read pointer */ - if (space-1 < skb->len) + if (space < skb->len) { /* write to the space we have left */ - ww = (ww - 1) & CMX_BUFF_MASK; - else + ww = (ww - 1) & CMX_BUFF_MASK; /* end one byte prior tx_R */ + printk(KERN_DEBUG "%s: buffer overflow\n", __func__); + } else /* write until all byte are copied */ ww = (w + skb->len) & CMX_BUFF_MASK; dsp->tx_W = ww; diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c index 1dc21d80341..1d504ba954f 100644 --- a/drivers/isdn/mISDN/dsp_core.c +++ b/drivers/isdn/mISDN/dsp_core.c @@ -191,6 +191,8 @@ dsp_rx_off_member(struct dsp *dsp) struct mISDN_ctrl_req cq; int rx_off = 1; + memset(&cq, 0, sizeof(cq)); + if (!dsp->features_rx_off) return; @@ -249,6 +251,32 @@ dsp_rx_off(struct dsp *dsp) } } +/* enable "fill empty" feature */ +static void +dsp_fill_empty(struct dsp *dsp) +{ + struct mISDN_ctrl_req cq; + + memset(&cq, 0, sizeof(cq)); + + if (!dsp->ch.peer) { + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: no peer, no fill_empty\n", + __func__); + return; + } + cq.op = MISDN_CTRL_FILL_EMPTY; + cq.p1 = 1; + if (dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq)) { + printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n", + __func__); + return; + } + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_DEBUG "%s: %s set fill_empty = 1\n", + __func__, dsp->name); +} + static int dsp_control_req(struct dsp *dsp, struct mISDNhead *hh, struct sk_buff *skb) { @@ -593,8 +621,6 @@ get_features(struct mISDNchannel *ch) struct dsp *dsp = container_of(ch, struct dsp, ch); struct mISDN_ctrl_req cq; - if (dsp_options & DSP_OPT_NOHARDWARE) - return; if (!ch->peer) { if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: no peer, no features\n", @@ -610,6 +636,10 @@ get_features(struct mISDNchannel *ch) } if (cq.op & MISDN_CTRL_RX_OFF) dsp->features_rx_off = 1; + if (cq.op & MISDN_CTRL_FILL_EMPTY) + dsp->features_fill_empty = 1; + if (dsp_options & DSP_OPT_NOHARDWARE) + return; if ((cq.op & MISDN_CTRL_HW_FEATURES_OP)) { cq.op = MISDN_CTRL_HW_FEATURES; *((u_long *)&cq.p1) = (u_long)&dsp->features; @@ -865,6 +895,9 @@ dsp_function(struct mISDNchannel *ch, struct sk_buff *skb) if (dsp->dtmf.hardware || dsp->dtmf.software) dsp_dtmf_goertzel_init(dsp); get_features(ch); + /* enable fill_empty feature */ + if (dsp->features_fill_empty) + dsp_fill_empty(dsp); /* send ph_activate */ hh->prim = PH_ACTIVATE_REQ; if (ch->peer) diff --git a/include/linux/mISDNhw.h b/include/linux/mISDNhw.h index e794dfb8750..9384b92dfc6 100644 --- a/include/linux/mISDNhw.h +++ b/include/linux/mISDNhw.h @@ -57,20 +57,21 @@ #define FLG_L2DATA 14 /* channel use L2 DATA primitivs */ #define FLG_ORIGIN 15 /* channel is on origin site */ /* channel specific stuff */ +#define FLG_FILLEMPTY 16 /* fill fifo on first frame (empty) */ /* arcofi specific */ -#define FLG_ARCOFI_TIMER 16 -#define FLG_ARCOFI_ERROR 17 +#define FLG_ARCOFI_TIMER 17 +#define FLG_ARCOFI_ERROR 18 /* isar specific */ -#define FLG_INITIALIZED 16 -#define FLG_DLEETX 17 -#define FLG_LASTDLE 18 -#define FLG_FIRST 19 -#define FLG_LASTDATA 20 -#define FLG_NMD_DATA 21 -#define FLG_FTI_RUN 22 -#define FLG_LL_OK 23 -#define FLG_LL_CONN 24 -#define FLG_DTMFSEND 25 +#define FLG_INITIALIZED 17 +#define FLG_DLEETX 18 +#define FLG_LASTDLE 19 +#define FLG_FIRST 20 +#define FLG_LASTDATA 21 +#define FLG_NMD_DATA 22 +#define FLG_FTI_RUN 23 +#define FLG_LL_OK 24 +#define FLG_LL_CONN 25 +#define FLG_DTMFSEND 26 /* workq events */ #define FLG_RECVQUEUE 30 diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h index 8f2d60da04e..74c903cd7a0 100644 --- a/include/linux/mISDNif.h +++ b/include/linux/mISDNif.h @@ -312,6 +312,7 @@ clear_channelmap(u_int nr, u_char *map) #define MISDN_CTRL_SETPEER 0x0040 #define MISDN_CTRL_UNSETPEER 0x0080 #define MISDN_CTRL_RX_OFF 0x0100 +#define MISDN_CTRL_FILL_EMPTY 0x0200 #define MISDN_CTRL_HW_FEATURES_OP 0x2000 #define MISDN_CTRL_HW_FEATURES 0x2001 #define MISDN_CTRL_HFC_OP 0x4000 -- cgit v1.2.3 From 190f71d9d8516fea4db01ba86ddfad5738a92703 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 3 Aug 2008 10:36:53 +0200 Subject: mISDN: Add some debug option for clock problems Add a new debug bit for clock problems. Signed-off-by: Andreas Eversberg Signed-off-by: Karsten Keil --- drivers/isdn/mISDN/dsp.h | 1 + drivers/isdn/mISDN/dsp_cmx.c | 16 +++++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/isdn/mISDN/dsp.h b/drivers/isdn/mISDN/dsp.h index ded94806933..98a33c58f09 100644 --- a/drivers/isdn/mISDN/dsp.h +++ b/drivers/isdn/mISDN/dsp.h @@ -15,6 +15,7 @@ #define DEBUG_DSP_TONE 0x0020 #define DEBUG_DSP_BLOWFISH 0x0040 #define DEBUG_DSP_DELAY 0x0100 +#define DEBUG_DSP_CLOCK 0x0200 #define DEBUG_DSP_DTMFCOEFF 0x8000 /* heavy output */ /* options may be: diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c index fc8ea41ae6a..93fb819c520 100644 --- a/drivers/isdn/mISDN/dsp_cmx.c +++ b/drivers/isdn/mISDN/dsp_cmx.c @@ -1192,7 +1192,7 @@ dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb) * we set our new read pointer, and write silence to buffer */ if (((dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) { - if (dsp_debug & DEBUG_DSP_CMX) + if (dsp_debug & DEBUG_DSP_CLOCK) printk(KERN_DEBUG "cmx_receive(dsp=%lx): UNDERRUN (or overrun the " "maximum delay), adjusting read pointer! " @@ -1218,7 +1218,7 @@ dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb) if (dsp->cmx_delay) if (((dsp->rx_W - dsp->rx_R) & CMX_BUFF_MASK) >= (dsp->cmx_delay << 1)) { - if (dsp_debug & DEBUG_DSP_CMX) + if (dsp_debug & DEBUG_DSP_CLOCK) printk(KERN_DEBUG "cmx_receive(dsp=%lx): OVERRUN (because " "twice the delay is reached), adjusting " @@ -1374,8 +1374,9 @@ dsp_cmx_send_member(struct dsp *dsp, int len, s32 *c, int members) r = (r+1) & CMX_BUFF_MASK; } if (r != rr) { - printk(KERN_DEBUG "%s: buffer empty\n", - __func__); + if (dsp_debug & DEBUG_DSP_CLOCK) + printk(KERN_DEBUG "%s: RX empty\n", + __func__); memset(d, dsp_silence, (rr-r)&CMX_BUFF_MASK); } /* -> if echo is enabled */ @@ -1724,7 +1725,7 @@ dsp_cmx_send(void *arg) * the delay is greater dsp_poll */ if (delay > dsp_poll && !dsp->cmx_delay) { - if (dsp_debug & DEBUG_DSP_CMX) + if (dsp_debug & DEBUG_DSP_CLOCK) printk(KERN_DEBUG "%s lowest rx_delay of %d bytes for" " dsp %s are now removed.\n", @@ -1755,7 +1756,7 @@ dsp_cmx_send(void *arg) * have enabled tx_dejitter */ if (delay > dsp_poll && dsp->tx_dejitter) { - if (dsp_debug & DEBUG_DSP_CMX) + if (dsp_debug & DEBUG_DSP_CLOCK) printk(KERN_DEBUG "%s lowest tx_delay of %d bytes for" " dsp %s are now removed.\n", @@ -1821,7 +1822,8 @@ dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb) if (space < skb->len) { /* write to the space we have left */ ww = (ww - 1) & CMX_BUFF_MASK; /* end one byte prior tx_R */ - printk(KERN_DEBUG "%s: buffer overflow\n", __func__); + if (dsp_debug & DEBUG_DSP_CLOCK) + printk(KERN_DEBUG "%s: TX overflow\n", __func__); } else /* write until all byte are copied */ ww = (w + skb->len) & CMX_BUFF_MASK; -- cgit v1.2.3 From 11618496d03d9e1ad70ac68afbfa7df9b5fc19d1 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Wed, 6 Aug 2008 19:13:07 +0200 Subject: mISDN: Fix queue limit counting problem If received frames are not handled in time, purge them. Signed-off-by: Andreas Eversberg Signed-off-by: Karsten Keil --- drivers/isdn/mISDN/hwchannel.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/isdn/mISDN/hwchannel.c b/drivers/isdn/mISDN/hwchannel.c index 2596fba4e61..535ceacc05b 100644 --- a/drivers/isdn/mISDN/hwchannel.c +++ b/drivers/isdn/mISDN/hwchannel.c @@ -50,9 +50,6 @@ bchannel_bh(struct work_struct *ws) if (test_and_clear_bit(FLG_RECVQUEUE, &bch->Flags)) { while ((skb = skb_dequeue(&bch->rqueue))) { - if (bch->rcount >= 64) - printk(KERN_WARNING "B-channel %p receive " - "queue if full, but empties...\n", bch); bch->rcount--; if (likely(bch->ch.peer)) { err = bch->ch.recv(bch->ch.peer, skb); @@ -177,8 +174,10 @@ recv_Bchannel(struct bchannel *bch) hh->prim = PH_DATA_IND; hh->id = MISDN_ID_ANY; if (bch->rcount >= 64) { - dev_kfree_skb(bch->rx_skb); - bch->rx_skb = NULL; + printk(KERN_WARNING "B-channel %p receive queue overflow, " + "fushing!\n", bch); + skb_queue_purge(&bch->rqueue); + bch->rcount = 0; return; } bch->rcount++; @@ -200,8 +199,10 @@ void recv_Bchannel_skb(struct bchannel *bch, struct sk_buff *skb) { if (bch->rcount >= 64) { - dev_kfree_skb(skb); - return; + printk(KERN_WARNING "B-channel %p receive queue overflow, " + "fushing!\n", bch); + skb_queue_purge(&bch->rqueue); + bch->rcount = 0; } bch->rcount++; skb_queue_tail(&bch->rqueue, skb); @@ -245,8 +246,12 @@ confirm_Bsend(struct bchannel *bch) { struct sk_buff *skb; - if (bch->rcount >= 64) - return; + if (bch->rcount >= 64) { + printk(KERN_WARNING "B-channel %p receive queue overflow, " + "fushing!\n", bch); + skb_queue_purge(&bch->rqueue); + bch->rcount = 0; + } skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(bch->tx_skb), 0, NULL, GFP_ATOMIC); if (!skb) { -- cgit v1.2.3 From 9776ef0829ed33e8f36d9e7d859d00cc91c54ffd Mon Sep 17 00:00:00 2001 From: Matthias Urlichs Date: Mon, 11 Aug 2008 14:43:33 +0200 Subject: mISDN: Remove the local max-card limit This is not needed. Signed-off-by: Matthias Urlichs Signed-off-by: Karsten Keil --- drivers/isdn/hardware/mISDN/hfcpci.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index 80c356e5dbe..8461477a3ac 100644 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -34,7 +34,6 @@ static const char *hfcpci_revision = "2.0"; -#define MAX_CARDS 8 static int HFC_cnt; static uint debug; @@ -2077,9 +2076,6 @@ setup_card(struct hfc_pci *card) 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); -- cgit v1.2.3 From 8b6015f736125050722dbe59c4f943e78cd626f0 Mon Sep 17 00:00:00 2001 From: Matthias Urlichs Date: Tue, 12 Aug 2008 10:12:09 +0200 Subject: mISDN: Added an ioctl to change the device name To get persistent device names with hotplug we need to rename devices sometime. Signed-off-by: Matthias Urlichs Signed-off-by: Karsten Keil --- drivers/isdn/mISDN/socket.c | 15 +++++++++++++++ include/linux/mISDNif.h | 31 +++++++++++++++++++------------ 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c index 37a2de18cfd..98fa6c4a4bd 100644 --- a/drivers/isdn/mISDN/socket.c +++ b/drivers/isdn/mISDN/socket.c @@ -645,6 +645,21 @@ base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) } else err = -ENODEV; break; + case IMSETDEVNAME: + { + struct mISDN_devrename dn; + if (copy_from_user(&dn, (void __user *)arg, + sizeof(dn))) { + err = -EFAULT; + break; + } + dev = get_mdevice(dn.id); + if (dev) + strlcpy(dev->name, dn.name, MISDN_MAX_IDLEN); + else + err = -ENODEV; + } + break; default: err = -EINVAL; } diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h index 74c903cd7a0..be09476ed85 100644 --- a/include/linux/mISDNif.h +++ b/include/linux/mISDNif.h @@ -36,8 +36,8 @@ * - should be incremented on every checkin */ #define MISDN_MAJOR_VERSION 1 -#define MISDN_MINOR_VERSION 0 -#define MISDN_RELEASE 19 +#define MISDN_MINOR_VERSION 1 +#define MISDN_RELEASE 20 /* primitives for information exchange * generell format @@ -255,16 +255,6 @@ struct sockaddr_mISDN { unsigned char tei; }; -/* timer device ioctl */ -#define IMADDTIMER _IOR('I', 64, int) -#define IMDELTIMER _IOR('I', 65, int) -/* socket ioctls */ -#define IMGETVERSION _IOR('I', 66, int) -#define IMGETCOUNT _IOR('I', 67, int) -#define IMGETDEVINFO _IOR('I', 68, int) -#define IMCTRLREQ _IOR('I', 69, int) -#define IMCLEAR_L2 _IOR('I', 70, int) - struct mISDNversion { unsigned char major; unsigned char minor; @@ -281,6 +271,23 @@ struct mISDN_devinfo { char name[MISDN_MAX_IDLEN]; }; +struct mISDN_devrename { + u_int id; + char name[MISDN_MAX_IDLEN]; /* new name */ +}; + +/* timer device ioctl */ +#define IMADDTIMER _IOR('I', 64, int) +#define IMDELTIMER _IOR('I', 65, int) + +/* socket ioctls */ +#define IMGETVERSION _IOR('I', 66, int) +#define IMGETCOUNT _IOR('I', 67, int) +#define IMGETDEVINFO _IOR('I', 68, int) +#define IMCTRLREQ _IOR('I', 69, int) +#define IMCLEAR_L2 _IOR('I', 70, int) +#define IMSETDEVNAME _IOR('I', 71, struct mISDN_devrename) + static inline int test_channelmap(u_int nr, u_char *map) { -- cgit v1.2.3 From 837468d135dcc49cdabc9fa92fc9550479f60704 Mon Sep 17 00:00:00 2001 From: Matthias Urlichs Date: Sat, 16 Aug 2008 00:04:33 +0200 Subject: mISDN: Use struct device name field struct device already has a 'name' member, use it. Signed-off-by: Matthias Urlichs Signed-off-by: Karsten Keil --- drivers/isdn/mISDN/core.c | 8 ++++---- drivers/isdn/mISDN/layer1.c | 2 +- drivers/isdn/mISDN/socket.c | 6 +++--- drivers/isdn/mISDN/stack.c | 43 ++++++++++++++++++++++++------------------- drivers/isdn/mISDN/tei.c | 6 +++--- include/linux/mISDNif.h | 1 - 6 files changed, 35 insertions(+), 31 deletions(-) diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c index 751665c448d..43fd97b0fe4 100644 --- a/drivers/isdn/mISDN/core.c +++ b/drivers/isdn/mISDN/core.c @@ -82,12 +82,12 @@ mISDN_register_device(struct mISDNdevice *dev, char *name) if (dev->id < 0) return -EBUSY; if (name && name[0]) - strcpy(dev->name, name); + dev_set_name(&dev->dev, "%s", name); else - sprintf(dev->name, "mISDN%d", dev->id); + dev_set_name(&dev->dev, "mISDN%d", dev->id); if (debug & DEBUG_CORE) printk(KERN_DEBUG "mISDN_register %s %d\n", - dev->name, dev->id); + dev_name(&dev->dev), dev->id); err = create_stack(dev); if (err) return err; @@ -104,7 +104,7 @@ mISDN_unregister_device(struct mISDNdevice *dev) { if (debug & DEBUG_CORE) printk(KERN_DEBUG "mISDN_unregister %s %d\n", - dev->name, dev->id); + dev_name(&dev->dev), dev->id); write_lock_irqsave(&device_lock, flags); list_del(&dev->D.list); write_unlock_irqrestore(&device_lock, flags); diff --git a/drivers/isdn/mISDN/layer1.c b/drivers/isdn/mISDN/layer1.c index b73e952d12c..e826eeb1ece 100644 --- a/drivers/isdn/mISDN/layer1.c +++ b/drivers/isdn/mISDN/layer1.c @@ -101,7 +101,7 @@ l1m_debug(struct FsmInst *fi, char *fmt, ...) va_list va; va_start(va, fmt); - printk(KERN_DEBUG "%s: ", l1->dch->dev.name); + printk(KERN_DEBUG "%s: ", dev_name(&l1->dch->dev.dev)); vprintk(fmt, va); printk("\n"); va_end(va); diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c index 98fa6c4a4bd..2f6d6e88ff2 100644 --- a/drivers/isdn/mISDN/socket.c +++ b/drivers/isdn/mISDN/socket.c @@ -381,7 +381,7 @@ data_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) memcpy(di.channelmap, dev->channelmap, sizeof(di.channelmap)); di.nrbchan = dev->nrbchan; - strcpy(di.name, dev->name); + strcpy(di.name, dev_name(&dev->dev)); if (copy_to_user((void __user *)arg, &di, sizeof(di))) err = -EFAULT; } else @@ -639,7 +639,7 @@ base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) memcpy(di.channelmap, dev->channelmap, sizeof(di.channelmap)); di.nrbchan = dev->nrbchan; - strcpy(di.name, dev->name); + strcpy(di.name, dev_name(&dev->dev)); if (copy_to_user((void __user *)arg, &di, sizeof(di))) err = -EFAULT; } else @@ -655,7 +655,7 @@ base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) } dev = get_mdevice(dn.id); if (dev) - strlcpy(dev->name, dn.name, MISDN_MAX_IDLEN); + err = device_rename(&dev->dev, dn.name); else err = -ENODEV; } diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c index d55b14ae4e9..9b9fab47cb2 100644 --- a/drivers/isdn/mISDN/stack.c +++ b/drivers/isdn/mISDN/stack.c @@ -172,7 +172,8 @@ send_msg_to_layer(struct mISDNstack *st, struct sk_buff *skb) else printk(KERN_WARNING "%s: dev(%s) prim(%x) id(%x) no channel\n", - __func__, st->dev->name, hh->prim, hh->id); + __func__, dev_name(&st->dev->dev), hh->prim, + hh->id); } else if (lm == 0x8) { WARN_ON(lm == 0x8); ch = get_channel4id(st, hh->id); @@ -181,11 +182,12 @@ send_msg_to_layer(struct mISDNstack *st, struct sk_buff *skb) else printk(KERN_WARNING "%s: dev(%s) prim(%x) id(%x) no channel\n", - __func__, st->dev->name, hh->prim, hh->id); + __func__, dev_name(&st->dev->dev), hh->prim, + hh->id); } else { /* broadcast not handled yet */ printk(KERN_WARNING "%s: dev(%s) prim %x not delivered\n", - __func__, st->dev->name, hh->prim); + __func__, dev_name(&st->dev->dev), hh->prim); } return -ESRCH; } @@ -209,7 +211,8 @@ mISDNStackd(void *data) unlock_kernel(); #endif if (*debug & DEBUG_MSG_THREAD) - printk(KERN_DEBUG "mISDNStackd %s started\n", st->dev->name); + printk(KERN_DEBUG "mISDNStackd %s started\n", + dev_name(&st->dev->dev)); if (st->notify != NULL) { complete(st->notify); @@ -245,7 +248,7 @@ mISDNStackd(void *data) printk(KERN_DEBUG "%s: %s prim(%x) id(%x) " "send call(%d)\n", - __func__, st->dev->name, + __func__, dev_name(&st->dev->dev), mISDN_HEAD_PRIM(skb), mISDN_HEAD_ID(skb), err); dev_kfree_skb(skb); @@ -288,7 +291,7 @@ mISDNStackd(void *data) mISDN_STACK_ACTION_MASK)); if (*debug & DEBUG_MSG_THREAD) printk(KERN_DEBUG "%s: %s wake status %08lx\n", - __func__, st->dev->name, st->status); + __func__, dev_name(&st->dev->dev), st->status); test_and_set_bit(mISDN_STACK_ACTIVE, &st->status); test_and_clear_bit(mISDN_STACK_WAKEUP, &st->status); @@ -303,15 +306,16 @@ mISDNStackd(void *data) #ifdef MISDN_MSG_STATS printk(KERN_DEBUG "mISDNStackd daemon for %s proceed %d " "msg %d sleep %d stopped\n", - st->dev->name, st->msg_cnt, st->sleep_cnt, st->stopped_cnt); + dev_name(&st->dev->dev), st->msg_cnt, st->sleep_cnt, + st->stopped_cnt); printk(KERN_DEBUG "mISDNStackd daemon for %s utime(%ld) stime(%ld)\n", - st->dev->name, st->thread->utime, st->thread->stime); + dev_name(&st->dev->dev), st->thread->utime, st->thread->stime); printk(KERN_DEBUG "mISDNStackd daemon for %s nvcsw(%ld) nivcsw(%ld)\n", - st->dev->name, st->thread->nvcsw, st->thread->nivcsw); + dev_name(&st->dev->dev), st->thread->nvcsw, st->thread->nivcsw); printk(KERN_DEBUG "mISDNStackd daemon for %s killed now\n", - st->dev->name); + dev_name(&st->dev->dev)); #endif test_and_set_bit(mISDN_STACK_KILLED, &st->status); test_and_clear_bit(mISDN_STACK_RUNNING, &st->status); @@ -401,15 +405,16 @@ create_stack(struct mISDNdevice *dev) newst->own.send = mISDN_queue_message; newst->own.recv = mISDN_queue_message; if (*debug & DEBUG_CORE_FUNC) - printk(KERN_DEBUG "%s: st(%s)\n", __func__, newst->dev->name); + printk(KERN_DEBUG "%s: st(%s)\n", __func__, + dev_name(&newst->dev->dev)); newst->notify = &done; newst->thread = kthread_run(mISDNStackd, (void *)newst, "mISDN_%s", - newst->dev->name); + dev_name(&newst->dev->dev)); if (IS_ERR(newst->thread)) { err = PTR_ERR(newst->thread); printk(KERN_ERR "mISDN:cannot create kernel thread for %s (%d)\n", - newst->dev->name, err); + dev_name(&newst->dev->dev), err); delete_teimanager(dev->teimgr); kfree(newst); } else @@ -428,8 +433,8 @@ connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch, if (*debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", - __func__, dev->name, protocol, adr->dev, adr->channel, - adr->sapi, adr->tei); + __func__, dev_name(&dev->dev), protocol, adr->dev, + adr->channel, adr->sapi, adr->tei); switch (protocol) { case ISDN_P_NT_S0: case ISDN_P_NT_E1: @@ -473,7 +478,7 @@ connect_Bstack(struct mISDNdevice *dev, struct mISDNchannel *ch, if (*debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", - __func__, dev->name, protocol, + __func__, dev_name(&dev->dev), protocol, adr->dev, adr->channel, adr->sapi, adr->tei); ch->st = dev->D.st; @@ -529,7 +534,7 @@ create_l2entity(struct mISDNdevice *dev, struct mISDNchannel *ch, if (*debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", - __func__, dev->name, protocol, + __func__, dev_name(&dev->dev), protocol, adr->dev, adr->channel, adr->sapi, adr->tei); rq.protocol = ISDN_P_TE_S0; @@ -590,7 +595,7 @@ delete_channel(struct mISDNchannel *ch) } if (*debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%s) protocol(%x)\n", __func__, - ch->st->dev->name, ch->protocol); + dev_name(&ch->st->dev->dev), ch->protocol); if (ch->protocol >= ISDN_P_B_START) { if (ch->peer) { ch->peer->ctrl(ch->peer, CLOSE_CHANNEL, NULL); @@ -643,7 +648,7 @@ delete_stack(struct mISDNdevice *dev) if (*debug & DEBUG_CORE_FUNC) printk(KERN_DEBUG "%s: st(%s)\n", __func__, - st->dev->name); + dev_name(&st->dev->dev)); if (dev->teimgr) delete_teimanager(dev->teimgr); if (st->thread) { diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c index 5c43d19e7c1..b452dead8fd 100644 --- a/drivers/isdn/mISDN/tei.c +++ b/drivers/isdn/mISDN/tei.c @@ -968,9 +968,9 @@ create_teimgr(struct manager *mgr, struct channel_req *crq) if (*debug & DEBUG_L2_TEI) printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n", - __func__, mgr->ch.st->dev->name, crq->protocol, - crq->adr.dev, crq->adr.channel, crq->adr.sapi, - crq->adr.tei); + __func__, dev_name(&mgr->ch.st->dev->dev), + crq->protocol, crq->adr.dev, crq->adr.channel, + crq->adr.sapi, crq->adr.tei); if (crq->adr.sapi != 0) /* not supported yet */ return -EINVAL; if (crq->adr.tei > GROUP_TEI) diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h index be09476ed85..a59febb6143 100644 --- a/include/linux/mISDNif.h +++ b/include/linux/mISDNif.h @@ -431,7 +431,6 @@ struct mISDN_sock { struct mISDNdevice { struct mISDNchannel D; u_int id; - char name[MISDN_MAX_IDLEN]; u_int Dprotocols; u_int Bprotocols; u_int nrbchan; -- cgit v1.2.3 From e4cce225f05c1daf89d10565c654f4c11907a049 Mon Sep 17 00:00:00 2001 From: Peter Schlaile Date: Fri, 22 Aug 2008 19:44:21 +0200 Subject: mISDN: Fix HDLC DSP transmit Fix HDLC DSP transmit (DL_DATA frames were bounced back upwards instead of being sent down as PH_DATA frames) Thanks to Andreas Eversberg for the fix! Signed-off-by: Andreas Eversberg Signed-off-by: Peter Schlaile Signed-off-by: Karsten Keil --- drivers/isdn/mISDN/dsp_core.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c index 1d504ba954f..c16cb7ac3d2 100644 --- a/drivers/isdn/mISDN/dsp_core.c +++ b/drivers/isdn/mISDN/dsp_core.c @@ -867,11 +867,14 @@ dsp_function(struct mISDNchannel *ch, struct sk_buff *skb) } if (dsp->hdlc) { /* hdlc */ - spin_lock_irqsave(&dsp_lock, flags); - if (dsp->b_active) { - skb_queue_tail(&dsp->sendq, skb); - schedule_work(&dsp->workq); + if (!dsp->b_active) { + ret = -EIO; + break; } + hh->prim = PH_DATA_REQ; + spin_lock_irqsave(&dsp_lock, flags); + skb_queue_tail(&dsp->sendq, skb); + schedule_work(&dsp->workq); spin_unlock_irqrestore(&dsp_lock, flags); return 0; } -- cgit v1.2.3 From 0aafe75d5c9227f209c180012d799a9e19d7ee79 Mon Sep 17 00:00:00 2001 From: Peter Schlaile Date: Mon, 25 Aug 2008 17:55:53 +0200 Subject: mISDN: Make debug output a little bit more verbose Add usefull info to debug output. Signed-off-by: Peter Schlaile Signed-off-by: Karsten Keil --- drivers/isdn/mISDN/dsp_cmx.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c index 93fb819c520..b16f76c2d68 100644 --- a/drivers/isdn/mISDN/dsp_cmx.c +++ b/drivers/isdn/mISDN/dsp_cmx.c @@ -1823,7 +1823,9 @@ dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb) /* write to the space we have left */ ww = (ww - 1) & CMX_BUFF_MASK; /* end one byte prior tx_R */ if (dsp_debug & DEBUG_DSP_CLOCK) - printk(KERN_DEBUG "%s: TX overflow\n", __func__); + printk(KERN_DEBUG "%s: TX overflow space=%d skb->len=" + "%d, w=0x%04x, ww=0x%04x\n", __func__, space, + skb->len, w, ww); } else /* write until all byte are copied */ ww = (w + skb->len) & CMX_BUFF_MASK; -- cgit v1.2.3 From 83a8a55b1deb07ac1ce546a8f230a8ef8f3c5ce5 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 30 Aug 2008 06:50:34 +0200 Subject: mISDN: Fixed more indexing bugs Fix more indexing bugs when checking free timeslots. Signed-off-by: Andreas Eversberg Signed-off-by: Karsten Keil --- drivers/isdn/mISDN/dsp_cmx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c index b16f76c2d68..04dbb407f7a 100644 --- a/drivers/isdn/mISDN/dsp_cmx.c +++ b/drivers/isdn/mISDN/dsp_cmx.c @@ -744,11 +744,11 @@ conf_software: if (dsp->pcm_slot_rx >= 0 && dsp->pcm_slot_rx < sizeof(freeslots)) - freeslots[dsp->pcm_slot_tx] = 0; + freeslots[dsp->pcm_slot_rx] = 0; if (dsp->pcm_slot_tx >= 0 && dsp->pcm_slot_tx < sizeof(freeslots)) - freeslots[dsp->pcm_slot_rx] = 0; + freeslots[dsp->pcm_slot_tx] = 0; } } i = 0; @@ -836,11 +836,11 @@ conf_software: if (dsp->pcm_slot_rx >= 0 && dsp->pcm_slot_rx < sizeof(freeslots)) - freeslots[dsp->pcm_slot_tx] = 0; + freeslots[dsp->pcm_slot_rx] = 0; if (dsp->pcm_slot_tx >= 0 && dsp->pcm_slot_tx < sizeof(freeslots)) - freeslots[dsp->pcm_slot_rx] = 0; + freeslots[dsp->pcm_slot_tx] = 0; } } i1 = 0; -- cgit v1.2.3 From a9b61830cd88a1d3d5e6d61adb737a3e20f7f0f5 Mon Sep 17 00:00:00 2001 From: Martin Bachem Date: Wed, 3 Sep 2008 18:08:30 +0200 Subject: mISDN: Use protocol to detect D-channel Use protocol to detect D-channel not the channel number 0 Signed-off-by: Martin Bachem Signed-off-by: Karsten Keil --- drivers/isdn/hardware/mISDN/hfcpci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index 8461477a3ac..37c2e19c3b0 100644 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -1964,7 +1964,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); -- cgit v1.2.3 From 1f28fa19d34c0d9186f274e61e4b3dcfc6428c5c Mon Sep 17 00:00:00 2001 From: Martin Bachem Date: Wed, 3 Sep 2008 15:17:45 +0200 Subject: mISDN: Add E-Channel logging features New prim PH_DATA_E_IND. - all E-ch frames are indicated by recv_Echannel(), which pushes E-Channel frames into dch's rqueue - if dchannel is opened with channel nr 0, no E-Channel logging is requested - if dchannel is opened with channel nr 1, E-Channel logging is requested. if layer1 does not support that, -EINVAL in return is appropriate Signed-off-by: Martin Bachem Signed-off-by: Karsten Keil --- drivers/isdn/mISDN/hwchannel.c | 19 +++++++++++++++++++ drivers/isdn/mISDN/stack.c | 2 +- include/linux/mISDNhw.h | 1 + include/linux/mISDNif.h | 1 + 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/isdn/mISDN/hwchannel.c b/drivers/isdn/mISDN/hwchannel.c index 535ceacc05b..ab1168a110a 100644 --- a/drivers/isdn/mISDN/hwchannel.c +++ b/drivers/isdn/mISDN/hwchannel.c @@ -165,6 +165,25 @@ recv_Dchannel(struct dchannel *dch) } EXPORT_SYMBOL(recv_Dchannel); +void +recv_Echannel(struct dchannel *ech, struct dchannel *dch) +{ + struct mISDNhead *hh; + + if (ech->rx_skb->len < 2) { /* at least 2 for sapi / tei */ + dev_kfree_skb(ech->rx_skb); + ech->rx_skb = NULL; + return; + } + hh = mISDN_HEAD_P(ech->rx_skb); + hh->prim = PH_DATA_E_IND; + hh->id = get_sapi_tei(ech->rx_skb->data); + skb_queue_tail(&dch->rqueue, ech->rx_skb); + ech->rx_skb = NULL; + schedule_event(dch, FLG_RECVQUEUE); +} +EXPORT_SYMBOL(recv_Echannel); + void recv_Bchannel(struct bchannel *bch) { diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c index 9b9fab47cb2..8cff570bb8d 100644 --- a/drivers/isdn/mISDN/stack.c +++ b/drivers/isdn/mISDN/stack.c @@ -453,7 +453,7 @@ connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch, ch->peer = &dev->D.st->own; ch->st = dev->D.st; rq.protocol = protocol; - rq.adr.channel = 0; + rq.adr.channel = adr->channel; err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err); if (err) diff --git a/include/linux/mISDNhw.h b/include/linux/mISDNhw.h index 9384b92dfc6..97ffdc1d344 100644 --- a/include/linux/mISDNhw.h +++ b/include/linux/mISDNhw.h @@ -184,6 +184,7 @@ extern void queue_ch_frame(struct mISDNchannel *, u_int, extern int dchannel_senddata(struct dchannel *, struct sk_buff *); extern int bchannel_senddata(struct bchannel *, struct sk_buff *); extern void recv_Dchannel(struct dchannel *); +extern void recv_Echannel(struct dchannel *, struct dchannel *); extern void recv_Bchannel(struct bchannel *); extern void recv_Dchannel_skb(struct dchannel *, struct sk_buff *); extern void recv_Bchannel_skb(struct bchannel *, struct sk_buff *); diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h index a59febb6143..f75d596c531 100644 --- a/include/linux/mISDNif.h +++ b/include/linux/mISDNif.h @@ -80,6 +80,7 @@ #define PH_DEACTIVATE_IND 0x0202 #define PH_DEACTIVATE_CNF 0x4202 #define PH_DATA_IND 0x2002 +#define PH_DATA_E_IND 0x3002 #define MPH_ACTIVATE_IND 0x0502 #define MPH_DEACTIVATE_IND 0x0602 #define MPH_INFORMATION_IND 0x0702 -- cgit v1.2.3 From 55a6af9749d6a31e087f304f2ea39db3bb7b3f3e Mon Sep 17 00:00:00 2001 From: Martin Bachem Date: Thu, 4 Sep 2008 12:42:39 +0200 Subject: mISDN: Return error on E-channel access Return error on D-channel access with E-channel data requested Signed-off-by: Martin Bachem Signed-off-by: Karsten Keil --- drivers/isdn/hardware/mISDN/hfcpci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index 37c2e19c3b0..ea69d5862f6 100644 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -1894,6 +1894,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); -- cgit v1.2.3 From 3bd69ad197a4a3d0085a5dc3b5796111bf176b12 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 6 Sep 2008 09:03:46 +0200 Subject: mISDN: Add ISDN sample clock API to mISDN core Add ISDN sample clock API to mISDN core (new file clock.c) hfcmulti and mISDNdsp use clock API. Signed-off-by: Andreas Eversberg Signed-off-by: Karsten Keil --- drivers/isdn/hardware/mISDN/hfc_multi.h | 3 + drivers/isdn/hardware/mISDN/hfcmulti.c | 45 +++++-- drivers/isdn/mISDN/Makefile | 2 +- drivers/isdn/mISDN/clock.c | 216 ++++++++++++++++++++++++++++++++ drivers/isdn/mISDN/core.c | 1 + drivers/isdn/mISDN/core.h | 2 + drivers/isdn/mISDN/dsp_cmx.c | 40 +++--- include/linux/mISDNif.h | 17 +++ 8 files changed, 294 insertions(+), 32 deletions(-) create mode 100644 drivers/isdn/mISDN/clock.c diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h index 4aa6a8b41f5..663b77f578b 100644 --- a/drivers/isdn/hardware/mISDN/hfc_multi.h +++ b/drivers/isdn/hardware/mISDN/hfc_multi.h @@ -197,6 +197,9 @@ struct hfc_multi { spinlock_t lock; /* the lock */ + struct mISDNclock *iclock; /* isdn clock support */ + int iclock_on; + /* * the channel index is counted from 0, regardless where the channel * is located on the hfc-channel. diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c index 3fc2e9d9534..592db93105f 100644 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -133,6 +133,12 @@ * Give the value of the clock control register (A_ST_CLK_DLY) * of the S/T interfaces in TE mode. * This register is needed for the TBR3 certification, so don't change it. + * + * clock: + * NOTE: only one clockdelay_te value must be given once + * Selects interface with clock source for mISDN and applications. + * Set to card number starting with 1. Set to -1 to disable. + * By default, the first card is used as clock source. */ /* @@ -190,12 +196,13 @@ static int nt_t1_count[] = { 3840, 1920, 960, 480, 240, 120, 60, 30 }; */ static uint type[MAX_CARDS]; -static uint pcm[MAX_CARDS]; -static uint dslot[MAX_CARDS]; +static int pcm[MAX_CARDS]; +static int dslot[MAX_CARDS]; static uint iomode[MAX_CARDS]; static uint port[MAX_PORTS]; static uint debug; static uint poll; +static int clock; static uint timer; static uint clockdelay_te = CLKDEL_TE; static uint clockdelay_nt = CLKDEL_NT; @@ -207,12 +214,13 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(HFC_MULTI_VERSION); module_param(debug, uint, S_IRUGO | S_IWUSR); module_param(poll, uint, S_IRUGO | S_IWUSR); +module_param(clock, int, S_IRUGO | S_IWUSR); module_param(timer, uint, S_IRUGO | S_IWUSR); module_param(clockdelay_te, uint, S_IRUGO | S_IWUSR); module_param(clockdelay_nt, uint, S_IRUGO | S_IWUSR); module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(pcm, uint, NULL, S_IRUGO | S_IWUSR); -module_param_array(dslot, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(pcm, int, NULL, S_IRUGO | S_IWUSR); +module_param_array(dslot, int, NULL, S_IRUGO | S_IWUSR); module_param_array(iomode, uint, NULL, S_IRUGO | S_IWUSR); module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR); @@ -2629,6 +2637,7 @@ hfcmulti_interrupt(int intno, void *dev_id) iqcnt = 0; } #endif + if (!r_irq_statech && !(status & (V_DTMF_STA | V_LOST_STA | V_EXT_IRQSTA | V_MISC_IRQSTA | V_FR_IRQSTA))) { @@ -2683,11 +2692,13 @@ hfcmulti_interrupt(int intno, void *dev_id) plxsd_checksync(hc, 0); } } - if (r_irq_misc & V_TI_IRQ) + if (r_irq_misc & V_TI_IRQ) { + if (hc->iclock_on) + mISDN_clock_update(hc->iclock, poll, NULL); handle_timer_irq(hc); + } if (r_irq_misc & V_DTMF_IRQ) { - /* -> DTMF IRQ */ hfcmulti_dtmf(hc); } if (r_irq_misc & V_IRQ_PROC) { @@ -4075,6 +4086,15 @@ hfcm_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) return err; } +static int +clockctl(void *priv, int enable) +{ + struct hfc_multi *hc = priv; + + hc->iclock_on = enable; + return 0; +} + /* * initialize the card */ @@ -4489,10 +4509,14 @@ release_card(struct hfc_multi *hc) printk(KERN_WARNING "%s: release card (%d) entered\n", __func__, hc->id); + /* unregister clock source */ + if (hc->iclock) + mISDN_unregister_clock(hc->iclock); + + /* disable irq */ spin_lock_irqsave(&hc->lock, flags); disable_hwirq(hc); spin_unlock_irqrestore(&hc->lock, flags); - udelay(1000); /* dimm leds */ @@ -5003,6 +5027,10 @@ hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent) list_add_tail(&hc->list, &HFClist); spin_unlock_irqrestore(&HFClock, flags); + /* use as clock source */ + if (clock == HFC_cnt + 1) + hc->iclock = mISDN_register_clock("HFCMulti", 0, clockctl, hc); + /* initialize hardware */ ret_err = init_card(hc); if (ret_err) { @@ -5273,6 +5301,9 @@ HFCmulti_init(void) } + if (!clock) + clock = 1; + err = pci_register_driver(&hfcmultipci_driver); if (err < 0) { printk(KERN_ERR "error registering pci driver: %x\n", err); diff --git a/drivers/isdn/mISDN/Makefile b/drivers/isdn/mISDN/Makefile index 1cb5e633cf7..0a6bd2a9e73 100644 --- a/drivers/isdn/mISDN/Makefile +++ b/drivers/isdn/mISDN/Makefile @@ -8,6 +8,6 @@ obj-$(CONFIG_MISDN_L1OIP) += l1oip.o # multi objects -mISDN_core-objs := core.o fsm.o socket.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o +mISDN_core-objs := core.o fsm.o socket.o clock.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_pipeline.o dsp_hwec.o l1oip-objs := l1oip_core.o l1oip_codec.o diff --git a/drivers/isdn/mISDN/clock.c b/drivers/isdn/mISDN/clock.c new file mode 100644 index 00000000000..44d9c3d5d33 --- /dev/null +++ b/drivers/isdn/mISDN/clock.c @@ -0,0 +1,216 @@ +/* + * Copyright 2008 by Andreas Eversberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Quick API description: + * + * A clock source registers using mISDN_register_clock: + * name = text string to name clock source + * priority = value to priorize clock sources (0 = default) + * ctl = callback function to enable/disable clock source + * priv = private pointer of clock source + * return = pointer to clock source structure; + * + * Note: Callback 'ctl' can be called before mISDN_register_clock returns! + * Also it can be called during mISDN_unregister_clock. + * + * A clock source calls mISDN_clock_update with given samples elapsed, if + * enabled. If function call is delayed, tv must be set with the timestamp + * of the actual event. + * + * A clock source unregisters using mISDN_unregister_clock. + * + * To get current clock, call mISDN_clock_get. The signed short value + * counts the number of samples since. Time since last clock event is added. + * + */ + +#include +#include +#include +#include +#include "core.h" + +static u_int *debug; +static LIST_HEAD(iclock_list); +DEFINE_RWLOCK(iclock_lock); +u16 iclock_count; /* counter of last clock */ +struct timeval iclock_tv; /* time stamp of last clock */ +int iclock_tv_valid; /* already received one timestamp */ +struct mISDNclock *iclock_current; + +void +mISDN_init_clock(u_int *dp) +{ + debug = dp; + do_gettimeofday(&iclock_tv); +} + +static void +select_iclock(void) +{ + struct mISDNclock *iclock, *bestclock = NULL, *lastclock = NULL; + int pri = -128; + + list_for_each_entry(iclock, &iclock_list, list) { + if (iclock->pri > pri) { + pri = iclock->pri; + bestclock = iclock; + } + if (iclock_current == iclock) + lastclock = iclock; + } + if (lastclock && bestclock != lastclock) { + /* last used clock source still exists but changes, disable */ + if (*debug & DEBUG_CLOCK) + printk(KERN_DEBUG "Old clock source '%s' disable.\n", + lastclock->name); + lastclock->ctl(lastclock->priv, 0); + } + if (bestclock && bestclock != iclock_current) { + /* new clock source selected, enable */ + if (*debug & DEBUG_CLOCK) + printk(KERN_DEBUG "New clock source '%s' enable.\n", + bestclock->name); + bestclock->ctl(bestclock->priv, 1); + } + if (bestclock != iclock_current) { + /* no clock received yet */ + iclock_tv_valid = 0; + } + iclock_current = bestclock; +} + +struct mISDNclock +*mISDN_register_clock(char *name, int pri, clockctl_func_t *ctl, void *priv) +{ + u_long flags; + struct mISDNclock *iclock; + + if (*debug & (DEBUG_CORE | DEBUG_CLOCK)) + printk(KERN_DEBUG "%s: %s %d\n", __func__, name, pri); + iclock = kzalloc(sizeof(struct mISDNclock), GFP_ATOMIC); + if (!iclock) { + printk(KERN_ERR "%s: No memory for clock entry.\n", __func__); + return NULL; + } + strncpy(iclock->name, name, sizeof(iclock->name)-1); + iclock->pri = pri; + iclock->priv = priv; + iclock->ctl = ctl; + write_lock_irqsave(&iclock_lock, flags); + list_add_tail(&iclock->list, &iclock_list); + select_iclock(); + write_unlock_irqrestore(&iclock_lock, flags); + return iclock; +} +EXPORT_SYMBOL(mISDN_register_clock); + +void +mISDN_unregister_clock(struct mISDNclock *iclock) +{ + u_long flags; + + if (*debug & (DEBUG_CORE | DEBUG_CLOCK)) + printk(KERN_DEBUG "%s: %s %d\n", __func__, iclock->name, + iclock->pri); + write_lock_irqsave(&iclock_lock, flags); + if (iclock_current == iclock) { + if (*debug & DEBUG_CLOCK) + printk(KERN_DEBUG + "Current clock source '%s' unregisters.\n", + iclock->name); + iclock->ctl(iclock->priv, 0); + } + list_del(&iclock->list); + select_iclock(); + write_unlock_irqrestore(&iclock_lock, flags); +} +EXPORT_SYMBOL(mISDN_unregister_clock); + +void +mISDN_clock_update(struct mISDNclock *iclock, int samples, struct timeval *tv) +{ + u_long flags; + struct timeval tv_now; + time_t elapsed_sec; + int elapsed_8000th; + + write_lock_irqsave(&iclock_lock, flags); + if (iclock_current != iclock) { + printk(KERN_ERR "%s: '%s' sends us clock updates, but we do " + "listen to '%s'. This is a bug!\n", __func__, + iclock->name, + iclock_current ? iclock_current->name : "nothing"); + iclock->ctl(iclock->priv, 0); + write_unlock_irqrestore(&iclock_lock, flags); + return; + } + if (iclock_tv_valid) { + /* increment sample counter by given samples */ + iclock_count += samples; + if (tv) { /* tv must be set, if function call is delayed */ + iclock_tv.tv_sec = tv->tv_sec; + iclock_tv.tv_usec = tv->tv_usec; + } else + do_gettimeofday(&iclock_tv); + } else { + /* calc elapsed time by system clock */ + if (tv) { /* tv must be set, if function call is delayed */ + tv_now.tv_sec = tv->tv_sec; + tv_now.tv_usec = tv->tv_usec; + } else + do_gettimeofday(&tv_now); + elapsed_sec = tv_now.tv_sec - iclock_tv.tv_sec; + elapsed_8000th = (tv_now.tv_usec / 125) + - (iclock_tv.tv_usec / 125); + if (elapsed_8000th < 0) { + elapsed_sec -= 1; + elapsed_8000th += 8000; + } + /* add elapsed time to counter and set new timestamp */ + iclock_count += elapsed_sec * 8000 + elapsed_8000th; + iclock_tv.tv_sec = tv_now.tv_sec; + iclock_tv.tv_usec = tv_now.tv_usec; + iclock_tv_valid = 1; + if (*debug & DEBUG_CLOCK) + printk("Received first clock from source '%s'.\n", + iclock_current ? iclock_current->name : "nothing"); + } + write_unlock_irqrestore(&iclock_lock, flags); +} +EXPORT_SYMBOL(mISDN_clock_update); + +unsigned short +mISDN_clock_get(void) +{ + u_long flags; + struct timeval tv_now; + time_t elapsed_sec; + int elapsed_8000th; + u16 count; + + read_lock_irqsave(&iclock_lock, flags); + /* calc elapsed time by system clock */ + do_gettimeofday(&tv_now); + elapsed_sec = tv_now.tv_sec - iclock_tv.tv_sec; + elapsed_8000th = (tv_now.tv_usec / 125) - (iclock_tv.tv_usec / 125); + if (elapsed_8000th < 0) { + elapsed_sec -= 1; + elapsed_8000th += 8000; + } + /* add elapsed time to counter */ + count = iclock_count + elapsed_sec * 8000 + elapsed_8000th; + read_unlock_irqrestore(&iclock_lock, flags); + return count; +} +EXPORT_SYMBOL(mISDN_clock_get); + diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c index 43fd97b0fe4..9116f54def2 100644 --- a/drivers/isdn/mISDN/core.c +++ b/drivers/isdn/mISDN/core.c @@ -199,6 +199,7 @@ mISDNInit(void) printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n", MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE); + mISDN_init_clock(&debug); mISDN_initstack(&debug); err = mISDN_inittimer(&debug); if (err) diff --git a/drivers/isdn/mISDN/core.h b/drivers/isdn/mISDN/core.h index 7da7233b4c1..7ac2f81a812 100644 --- a/drivers/isdn/mISDN/core.h +++ b/drivers/isdn/mISDN/core.h @@ -74,4 +74,6 @@ extern void l1_cleanup(void); extern int Isdnl2_Init(u_int *); extern void Isdnl2_cleanup(void); +extern void mISDN_init_clock(u_int *); + #endif diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c index 04dbb407f7a..efe4c7430e6 100644 --- a/drivers/isdn/mISDN/dsp_cmx.c +++ b/drivers/isdn/mISDN/dsp_cmx.c @@ -1557,13 +1557,11 @@ send_packet: schedule_work(&dsp->workq); } -static u32 samplecount; +static u32 jittercount; /* counter for jitter check */; struct timer_list dsp_spl_tl; u32 dsp_spl_jiffies; /* calculate the next time to fire */ -#ifdef UNUSED -static u32 dsp_start_jiffies; /* jiffies at the time, the calculation begins */ -#endif /* UNUSED */ -static struct timeval dsp_start_tv; /* time at start of calculation */ +static u16 dsp_count; /* last sample count */ +static int dsp_count_valid ; /* if we have last sample count */ void dsp_cmx_send(void *arg) @@ -1577,38 +1575,32 @@ dsp_cmx_send(void *arg) int r, rr; int jittercheck = 0, delay, i; u_long flags; - struct timeval tv; - u32 elapsed; - s16 length; + u16 length, count; /* lock */ spin_lock_irqsave(&dsp_lock, flags); - if (!dsp_start_tv.tv_sec) { - do_gettimeofday(&dsp_start_tv); + if (!dsp_count_valid) { + dsp_count = mISDN_clock_get(); length = dsp_poll; + dsp_count_valid = 1; } else { - do_gettimeofday(&tv); - elapsed = ((tv.tv_sec - dsp_start_tv.tv_sec) * 8000) - + ((s32)(tv.tv_usec / 125) - (dsp_start_tv.tv_usec / 125)); - dsp_start_tv.tv_sec = tv.tv_sec; - dsp_start_tv.tv_usec = tv.tv_usec; - length = elapsed; + count = mISDN_clock_get(); + length = count - dsp_count; + dsp_count = count; } if (length > MAX_POLL + 100) length = MAX_POLL + 100; -/* printk(KERN_DEBUG "len=%d dsp_count=0x%x.%04x dsp_poll_diff=0x%x.%04x\n", - length, dsp_count >> 16, dsp_count & 0xffff, dsp_poll_diff >> 16, - dsp_poll_diff & 0xffff); - */ + /* printk(KERN_DEBUG "len=%d dsp_count=0x%x\n", length, dsp_count); */ /* - * check if jitter needs to be checked - * (this is about every second = 8192 samples) + * check if jitter needs to be checked (this is every second) */ - samplecount += length; - if ((samplecount & 8191) < length) + jittercount += length; + if (jittercount >= 8000) { + jittercount -= 8000; jittercheck = 1; + } /* loop all members that do not require conference mixing */ list_for_each_entry(dsp, &dsp_ilist, list) { diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h index f75d596c531..364f1018f0d 100644 --- a/include/linux/mISDNif.h +++ b/include/linux/mISDNif.h @@ -371,6 +371,7 @@ struct mISDN_ctrl_req { #define DEBUG_L2_TEI 0x00100000 #define DEBUG_L2_TEIFSM 0x00200000 #define DEBUG_TIMER 0x01000000 +#define DEBUG_CLOCK 0x02000000 #define mISDN_HEAD_P(s) ((struct mISDNhead *)&s->cb[0]) #define mISDN_HEAD_PRIM(s) (((struct mISDNhead *)&s->cb[0])->prim) @@ -384,6 +385,7 @@ struct mISDN_ctrl_req { struct mISDNchannel; struct mISDNdevice; struct mISDNstack; +struct mISDNclock; struct channel_req { u_int protocol; @@ -460,6 +462,16 @@ struct mISDNstack { #endif }; +typedef int (clockctl_func_t)(void *, int); + +struct mISDNclock { + struct list_head list; + char name[64]; + int pri; + clockctl_func_t *ctl; + void *priv; +}; + /* global alloc/queue functions */ static inline struct sk_buff * @@ -510,8 +522,13 @@ extern int mISDN_register_device(struct mISDNdevice *, char *name); extern void mISDN_unregister_device(struct mISDNdevice *); extern int mISDN_register_Bprotocol(struct Bprotocol *); extern void mISDN_unregister_Bprotocol(struct Bprotocol *); +extern struct mISDNclock *mISDN_register_clock(char *, int, clockctl_func_t *, + void *); +extern void mISDN_unregister_clock(struct mISDNclock *); extern void set_channel_address(struct mISDNchannel *, u_int, u_int); +extern void mISDN_clock_update(struct mISDNclock *, int, struct timeval *); +extern unsigned short mISDN_clock_get(void); #endif /* __KERNEL__ */ #endif /* mISDNIF_H */ -- cgit v1.2.3 From 9e6115f2f5a9f8568bc8565d1401086952f09736 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 6 Sep 2008 09:11:03 +0200 Subject: mISDN: Fix irq detection Fix false irq detection on disabled irqs. Signed-off-by: Andreas Eversberg Signed-off-by: Karsten Keil --- drivers/isdn/hardware/mISDN/hfcmulti.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c index 592db93105f..4cfab20d71a 100644 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -2658,6 +2658,7 @@ hfcmulti_interrupt(int intno, void *dev_id) if (status & V_MISC_IRQSTA) { /* misc IRQ */ r_irq_misc = HFC_inb_nodebug(hc, R_IRQ_MISC); + r_irq_misc &= hc->hw.r_irqmsk_misc; /* ignore disabled irqs */ if (r_irq_misc & V_STA_IRQ) { if (hc->type == 1) { /* state machine */ -- cgit v1.2.3 From 02282eee56b75a35e6bbc42cc34c9005eb1653f4 Mon Sep 17 00:00:00 2001 From: Martin Bachem Date: Mon, 8 Sep 2008 15:57:48 +0200 Subject: mISDN: Add ISDN_P_TE_UP0 / ISDN_P_NT_UP0 - new layer1 protocols for UP0 bus - helper #defines to test for TE/NT/S0/E1/UP0 Signed-off-by: Martin Bachem Signed-off-by: Karsten Keil --- include/linux/mISDNif.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h index 364f1018f0d..7f65aa0c1cc 100644 --- a/include/linux/mISDNif.h +++ b/include/linux/mISDNif.h @@ -200,6 +200,18 @@ #define ISDN_P_NT_S0 0x02 #define ISDN_P_TE_E1 0x03 #define ISDN_P_NT_E1 0x04 +#define ISDN_P_TE_UP0 0x05 +#define ISDN_P_NT_UP0 0x06 + +#define IS_ISDN_P_TE(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_TE_E1) || \ + (p == ISDN_P_TE_UP0)) +#define IS_ISDN_P_NT(p) ((p == ISDN_P_NT_S0) || (p == ISDN_P_NT_E1) || \ + (p == ISDN_P_NT_UP0)) +#define IS_ISDN_P_S0(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_NT_S0)) +#define IS_ISDN_P_E1(p) ((p == ISDN_P_TE_E1) || (p == ISDN_P_NT_E1)) +#define IS_ISDN_P_UP0(p) ((p == ISDN_P_TE_UP0) || (p == ISDN_P_NT_UP0)) + + #define ISDN_P_LAPD_TE 0x10 #define ISDN_P_LAPD_NT 0x11 -- cgit v1.2.3 From 1b4d33121f1d991f6ae226cc3333428ff87627bb Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 14 Sep 2008 12:30:18 +0200 Subject: mISDN: Fix deactivation, if peer IP is removed from l1oip instance. Added GETPEER operation. Socket now checks if device is already busy at a differen mode. Signed-off-by: Andreas Eversberg Signed-off-by: Karsten Keil --- drivers/isdn/mISDN/l1oip_core.c | 22 +++++++++++++++++++++- drivers/isdn/mISDN/socket.c | 20 ++++++++++++++++++++ drivers/isdn/mISDN/stack.c | 18 ------------------ include/linux/mISDNif.h | 5 +++-- 4 files changed, 44 insertions(+), 21 deletions(-) diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c index 0884dd6892f..3ddcd2e09dc 100644 --- a/drivers/isdn/mISDN/l1oip_core.c +++ b/drivers/isdn/mISDN/l1oip_core.c @@ -777,6 +777,8 @@ fail: static void l1oip_socket_close(struct l1oip *hc) { + struct dchannel *dch = hc->chan[hc->d_idx].dch; + /* kill thread */ if (hc->socket_thread) { if (debug & DEBUG_L1OIP_SOCKET) @@ -785,6 +787,16 @@ l1oip_socket_close(struct l1oip *hc) send_sig(SIGTERM, hc->socket_thread, 0); wait_for_completion(&hc->socket_complete); } + + /* if active, we send up a PH_DEACTIVATE and deactivate */ + if (test_bit(FLG_ACTIVE, &dch->Flags)) { + if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET)) + printk(KERN_DEBUG "%s: interface become deactivated " + "due to timeout\n", __func__); + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_ATOMIC); + } } static int @@ -944,7 +956,8 @@ channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq) switch (cq->op) { case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_SETPEER | MISDN_CTRL_UNSETPEER; + cq->op = MISDN_CTRL_SETPEER | MISDN_CTRL_UNSETPEER + | MISDN_CTRL_GETPEER; break; case MISDN_CTRL_SETPEER: hc->remoteip = (u32)cq->p1; @@ -964,6 +977,13 @@ channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq) hc->remoteip = 0; l1oip_socket_open(hc); break; + case MISDN_CTRL_GETPEER: + if (debug & DEBUG_L1OIP_SOCKET) + printk(KERN_DEBUG "%s: getting ip address.\n", + __func__); + (u32)cq->p1 = hc->remoteip; + cq->p2 = hc->remoteport | (hc->localport << 16); + break; default: printk(KERN_WARNING "%s: unknown Op %x\n", __func__, cq->op); diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c index 2f6d6e88ff2..916569ca156 100644 --- a/drivers/isdn/mISDN/socket.c +++ b/drivers/isdn/mISDN/socket.c @@ -460,6 +460,8 @@ data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) { struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr; struct sock *sk = sock->sk; + struct hlist_node *node; + struct sock *csk; int err = 0; if (*debug & DEBUG_SOCKET) @@ -480,6 +482,24 @@ data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) err = -ENODEV; goto done; } + + read_lock_bh(&data_sockets.lock); + sk_for_each(csk, node, &data_sockets.head) { + if (sk == csk) + continue; + if (_pms(csk)->dev != _pms(sk)->dev) + continue; + if (csk->sk_protocol >= ISDN_P_B_START) + continue; + if (IS_ISDN_P_TE(csk->sk_protocol) + == IS_ISDN_P_TE(sk->sk_protocol)) + continue; + read_unlock_bh(&data_sockets.lock); + err = -EBUSY; + goto done; + } + read_unlock_bh(&data_sockets.lock); + _pms(sk)->ch.send = mISDN_send; _pms(sk)->ch.ctrl = mISDN_ctrl; diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c index 8cff570bb8d..63afa8cf9e0 100644 --- a/drivers/isdn/mISDN/stack.c +++ b/drivers/isdn/mISDN/stack.c @@ -440,15 +440,6 @@ connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch, case ISDN_P_NT_E1: case ISDN_P_TE_S0: case ISDN_P_TE_E1: -#ifdef PROTOCOL_CHECK - /* this should be enhanced */ - if (!list_empty(&dev->D.st->layer2) - && dev->D.protocol != protocol) - return -EBUSY; - if (!hlist_empty(&dev->D.st->l1sock.head) - && dev->D.protocol != protocol) - return -EBUSY; -#endif ch->recv = mISDN_queue_message; ch->peer = &dev->D.st->own; ch->st = dev->D.st; @@ -546,15 +537,6 @@ create_l2entity(struct mISDNdevice *dev, struct mISDNchannel *ch, if (dev->Dprotocols & (1 << ISDN_P_NT_E1)) rq.protocol = ISDN_P_NT_E1; case ISDN_P_LAPD_TE: -#ifdef PROTOCOL_CHECK - /* this should be enhanced */ - if (!list_empty(&dev->D.st->layer2) - && dev->D.protocol != protocol) - return -EBUSY; - if (!hlist_empty(&dev->D.st->l1sock.head) - && dev->D.protocol != protocol) - return -EBUSY; -#endif ch->recv = mISDN_queue_message; ch->peer = &dev->D.st->own; ch->st = dev->D.st; diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h index 7f65aa0c1cc..3f9988849f3 100644 --- a/include/linux/mISDNif.h +++ b/include/linux/mISDNif.h @@ -204,9 +204,9 @@ #define ISDN_P_NT_UP0 0x06 #define IS_ISDN_P_TE(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_TE_E1) || \ - (p == ISDN_P_TE_UP0)) + (p == ISDN_P_TE_UP0) || (p == ISDN_P_LAPD_TE)) #define IS_ISDN_P_NT(p) ((p == ISDN_P_NT_S0) || (p == ISDN_P_NT_E1) || \ - (p == ISDN_P_NT_UP0)) + (p == ISDN_P_NT_UP0) || (p == ISDN_P_LAPD_NT)) #define IS_ISDN_P_S0(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_NT_S0)) #define IS_ISDN_P_E1(p) ((p == ISDN_P_TE_E1) || (p == ISDN_P_NT_E1)) #define IS_ISDN_P_UP0(p) ((p == ISDN_P_TE_UP0) || (p == ISDN_P_NT_UP0)) @@ -333,6 +333,7 @@ clear_channelmap(u_int nr, u_char *map) #define MISDN_CTRL_UNSETPEER 0x0080 #define MISDN_CTRL_RX_OFF 0x0100 #define MISDN_CTRL_FILL_EMPTY 0x0200 +#define MISDN_CTRL_GETPEER 0x0400 #define MISDN_CTRL_HW_FEATURES_OP 0x2000 #define MISDN_CTRL_HW_FEATURES 0x2001 #define MISDN_CTRL_HFC_OP 0x4000 -- cgit v1.2.3 From 9a812553bdc097a566aa79df7fae3457449c555b Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 14 Sep 2008 14:42:18 +0200 Subject: mISDN: Correct busy device detection Correct busy device detection. This fix belongs to last commit. Signed-off-by: Andreas Eversberg Signed-off-by: Karsten Keil --- drivers/isdn/mISDN/socket.c | 30 ++++++++++++++++-------------- drivers/isdn/mISDN/stack.c | 3 ++- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c index 916569ca156..508945d1b9c 100644 --- a/drivers/isdn/mISDN/socket.c +++ b/drivers/isdn/mISDN/socket.c @@ -483,22 +483,24 @@ data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) goto done; } - read_lock_bh(&data_sockets.lock); - sk_for_each(csk, node, &data_sockets.head) { - if (sk == csk) - continue; - if (_pms(csk)->dev != _pms(sk)->dev) - continue; - if (csk->sk_protocol >= ISDN_P_B_START) - continue; - if (IS_ISDN_P_TE(csk->sk_protocol) - == IS_ISDN_P_TE(sk->sk_protocol)) - continue; + if (sk->sk_protocol < ISDN_P_B_START) { + read_lock_bh(&data_sockets.lock); + sk_for_each(csk, node, &data_sockets.head) { + if (sk == csk) + continue; + if (_pms(csk)->dev != _pms(sk)->dev) + continue; + if (csk->sk_protocol >= ISDN_P_B_START) + continue; + if (IS_ISDN_P_TE(csk->sk_protocol) + == IS_ISDN_P_TE(sk->sk_protocol)) + continue; + read_unlock_bh(&data_sockets.lock); + err = -EBUSY; + goto done; + } read_unlock_bh(&data_sockets.lock); - err = -EBUSY; - goto done; } - read_unlock_bh(&data_sockets.lock); _pms(sk)->ch.send = mISDN_send; _pms(sk)->ch.ctrl = mISDN_ctrl; diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c index 63afa8cf9e0..e2f45019ebf 100644 --- a/drivers/isdn/mISDN/stack.c +++ b/drivers/isdn/mISDN/stack.c @@ -446,7 +446,8 @@ connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch, rq.protocol = protocol; rq.adr.channel = adr->channel; err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq); - printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err); + printk(KERN_DEBUG "%s: ret %d (dev %d)\n", __func__, err, + dev->id); if (err) return err; write_lock_bh(&dev->D.st->l1sock.lock); -- cgit v1.2.3 From 1b36c78f26bfa74c1782be98acb827afa95c3b0c Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 20 Sep 2008 13:43:28 +0200 Subject: mISDN: Minor fixes minor typo fix compiler warning fix Signed-off-by: Andreas Eversberg Signed-off-by: Karsten Keil --- drivers/isdn/hardware/mISDN/hfcmulti.c | 2 +- drivers/isdn/mISDN/l1oip_core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c index 4cfab20d71a..cd3f3994b7f 100644 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -135,7 +135,7 @@ * This register is needed for the TBR3 certification, so don't change it. * * clock: - * NOTE: only one clockdelay_te value must be given once + * NOTE: only one clock value must be given once * Selects interface with clock source for mISDN and applications. * Set to card number starting with 1. Set to -1 to disable. * By default, the first card is used as clock source. diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c index 3ddcd2e09dc..a6a9e1af2ad 100644 --- a/drivers/isdn/mISDN/l1oip_core.c +++ b/drivers/isdn/mISDN/l1oip_core.c @@ -981,7 +981,7 @@ channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq) if (debug & DEBUG_L1OIP_SOCKET) printk(KERN_DEBUG "%s: getting ip address.\n", __func__); - (u32)cq->p1 = hc->remoteip; + cq->p1 = hc->remoteip; cq->p2 = hc->remoteport | (hc->localport << 16); break; default: -- cgit v1.2.3 From 87c5fa1bb42624254a2013cbbc3b170d6017f5d6 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 28 Sep 2008 13:01:01 +0200 Subject: mISDN: Add different different timer settings for hfc-pci - Poll-timer can now be set from 8 to 256 samples, depending on your kernel. - If default or 128 is used, the normal controller's clock is used as before. Usage: modprobe hfcpci poll=XXX - Added some debug code for dsp buffer size. (CMX_DELAY_DEBUG) Signed-off-by: Andreas Eversberg Signed-off-by: Karsten Keil --- drivers/isdn/hardware/mISDN/hfc_pci.h | 1 - drivers/isdn/hardware/mISDN/hfcpci.c | 152 ++++++++++++++++++++++++++++------ drivers/isdn/mISDN/dsp_cmx.c | 23 +++++ 3 files changed, 148 insertions(+), 28 deletions(-) diff --git a/drivers/isdn/hardware/mISDN/hfc_pci.h b/drivers/isdn/hardware/mISDN/hfc_pci.h index fd9241ab180..3132ddc99fc 100644 --- a/drivers/isdn/hardware/mISDN/hfc_pci.h +++ b/drivers/isdn/hardware/mISDN/hfc_pci.h @@ -26,7 +26,6 @@ * change mask and threshold simultaneously */ #define HFCPCI_BTRANS_THRESHOLD 128 -#define HFCPCI_BTRANS_MAX 256 #define HFCPCI_FILLEMPTY 64 #define HFCPCI_BTRANS_THRESMASK 0x00 diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index ea69d5862f6..cd5d26c9909 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 @@ -36,10 +55,14 @@ static const char *hfcpci_revision = "2.0"; 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); +module_param(poll, uint, S_IRUGO | S_IWUSR); static LIST_HEAD(HFClist); static DEFINE_RWLOCK(HFClock); @@ -519,9 +542,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; @@ -533,17 +556,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); @@ -568,7 +593,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; } /* @@ -579,12 +603,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; @@ -624,9 +647,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; @@ -782,9 +806,9 @@ hfcpci_fill_fifo(struct bchannel *bch) 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 */ @@ -1164,37 +1188,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); @@ -1203,6 +1227,41 @@ hfcpci_int(int intno, void *dev_id) return IRQ_HANDLED; } +static void +hfcpci_softirq(void *arg) +{ + u_long flags; + struct bchannel *bch; + struct hfc_pci *hc; + + write_lock_irqsave(&HFClock, flags); + list_for_each_entry(hc, &HFClist, list) { + 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); + } + } + write_unlock_irqrestore(&HFClock, flags); + + /* 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); +} + /* * timer callback for D-chan busy resolution. Currently no function */ @@ -1312,14 +1371,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; } @@ -1427,7 +1488,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 @@ -1436,7 +1498,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 @@ -2273,7 +2336,39 @@ 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; } @@ -2282,6 +2377,9 @@ 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); } diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c index efe4c7430e6..02f643456b1 100644 --- a/drivers/isdn/mISDN/dsp_cmx.c +++ b/drivers/isdn/mISDN/dsp_cmx.c @@ -137,6 +137,7 @@ /* #define CMX_CONF_DEBUG */ /*#define CMX_DEBUG * massive read/write pointer output */ +/*#define CMX_DELAY_DEBUG * gives rx-buffer delay overview */ /*#define CMX_TX_DEBUG * massive read/write on tx-buffer with content */ static inline int @@ -1135,6 +1136,25 @@ dsp_cmx_conf(struct dsp *dsp, u32 conf_id) return 0; } +#ifdef CMX_DELAY_DEBUG +int delaycount; +static void +showdelay(struct dsp *dsp, int samples, int delay) +{ + char bar[] = "--------------------------------------------------|"; + int sdelay; + + delaycount += samples; + if (delaycount < 8000) + return; + delaycount = 0; + + sdelay = delay * 50 / (dsp_poll << 2); + + printk(KERN_DEBUG "DELAY (%s) %3d >%s\n", dsp->name, delay, + sdelay > 50 ? "..." : bar + 50 - sdelay); +} +#endif /* * audio data is received from card @@ -1256,6 +1276,9 @@ dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb) /* increase write-pointer */ dsp->rx_W = ((dsp->rx_W+len) & CMX_BUFF_MASK); +#ifdef CMX_DELAY_DEBUG + showdelay(dsp, len, (dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK); +#endif } -- cgit v1.2.3 From 808a14a1583ca3790bf0a9c20c7d4cbac212c775 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 28 Sep 2008 15:40:15 +0200 Subject: mISDN: Add missing release functions Add missing release function of ec-devices. Each device require a relase function now. All destruction (memory and list entry) must be done within the given release function of device, rather than after unregistering device. Signed-off-by: Andreas Eversberg Signed-off-by: Karsten Keil --- drivers/isdn/mISDN/dsp_pipeline.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c index 83639be7f7a..7a3a3e1e4d8 100644 --- a/drivers/isdn/mISDN/dsp_pipeline.c +++ b/drivers/isdn/mISDN/dsp_pipeline.c @@ -75,6 +75,15 @@ static struct device_attribute element_attributes[] = { __ATTR(args, 0444, attr_show_args, NULL), }; +static void +mISDN_dsp_dev_release(struct device *dev) +{ + struct dsp_element_entry *entry = + container_of(dev, struct dsp_element_entry, dev); + list_del(&entry->list); + kfree(entry); +} + int mISDN_dsp_element_register(struct mISDN_dsp_element *elem) { struct dsp_element_entry *entry; @@ -90,6 +99,7 @@ int mISDN_dsp_element_register(struct mISDN_dsp_element *elem) entry->elem = elem; entry->dev.class = elements_class; + entry->dev.release = mISDN_dsp_dev_release; dev_set_drvdata(&entry->dev, elem); dev_set_name(&entry->dev, elem->name); ret = device_register(&entry->dev); @@ -98,6 +108,7 @@ int mISDN_dsp_element_register(struct mISDN_dsp_element *elem) __func__, elem->name); goto err1; } + list_add_tail(&entry->list, &dsp_elements); for (i = 0; i < (sizeof(element_attributes) / sizeof(struct device_attribute)); ++i) @@ -109,14 +120,15 @@ int mISDN_dsp_element_register(struct mISDN_dsp_element *elem) goto err2; } - list_add_tail(&entry->list, &dsp_elements); - +#ifdef PIPELINE_DEBUG printk(KERN_DEBUG "%s: %s registered\n", __func__, elem->name); +#endif return 0; err2: device_unregister(&entry->dev); + return ret; err1: kfree(entry); return ret; @@ -132,11 +144,11 @@ void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem) list_for_each_entry_safe(entry, n, &dsp_elements, list) if (entry->elem == elem) { - list_del(&entry->list); device_unregister(&entry->dev); - kfree(entry); +#ifdef PIPELINE_DEBUG printk(KERN_DEBUG "%s: %s unregistered\n", __func__, elem->name); +#endif return; } printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name); @@ -173,7 +185,9 @@ void dsp_pipeline_module_exit(void) kfree(entry); } +#ifdef PIPELINE_DEBUG printk(KERN_DEBUG "%s: dsp pipeline module exited\n", __func__); +#endif } int dsp_pipeline_init(struct dsp_pipeline *pipeline) @@ -258,7 +272,7 @@ int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) pipeline_entry = kmalloc(sizeof(struct dsp_pipeline_entry), GFP_KERNEL); if (!pipeline_entry) { - printk(KERN_DEBUG "%s: failed to add " + printk(KERN_ERR "%s: failed to add " "entry to pipeline: %s (out of " "memory)\n", __func__, elem->name); incomplete = 1; @@ -286,7 +300,7 @@ int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) args : ""); #endif } else { - printk(KERN_DEBUG "%s: failed " + printk(KERN_ERR "%s: failed " "to add entry to pipeline: " "%s (new() returned NULL)\n", __func__, elem->name); @@ -301,7 +315,7 @@ int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) if (found) found = 0; else { - printk(KERN_DEBUG "%s: element not found, skipping: " + printk(KERN_ERR "%s: element not found, skipping: " "%s\n", __func__, name); incomplete = 1; } -- cgit v1.2.3 From b36b654a7e82308cea063cdf909a7f246105c2a3 Mon Sep 17 00:00:00 2001 From: Matthias Urlichs Date: Sat, 16 Aug 2008 00:09:24 +0200 Subject: mISDN: Create /sys/class/mISDN Create /sys/class/mISDN and implement functions to handle device renames. Signed-Off-By: Matthias Urlichs Signed-off-by: Karsten Keil --- drivers/isdn/hardware/mISDN/hfcmulti.c | 6 +- drivers/isdn/hardware/mISDN/hfcpci.c | 100 ++++++------ drivers/isdn/mISDN/core.c | 271 ++++++++++++++++++++++++++------- drivers/isdn/mISDN/l1oip_core.c | 3 +- include/linux/mISDNif.h | 8 +- 5 files changed, 273 insertions(+), 115 deletions(-) diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c index cd3f3994b7f..97f4708b387 100644 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -4718,7 +4718,7 @@ init_e1_port(struct hfc_multi *hc, struct hm_map *m) } else hc->chan[hc->dslot].jitter = 2; /* default */ snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d", HFC_cnt + 1); - ret = mISDN_register_device(&dch->dev, name); + ret = mISDN_register_device(&dch->dev, &hc->pci_dev->dev, name); if (ret) goto free_chan; hc->created[0] = 1; @@ -4826,9 +4826,9 @@ init_multi_port(struct hfc_multi *hc, int pt) test_and_set_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[i + 2].cfg); } - snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-%ds.%d/%d", + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-%ds.%d-%d", hc->type, HFC_cnt + 1, pt + 1); - ret = mISDN_register_device(&dch->dev, name); + ret = mISDN_register_device(&dch->dev, &hc->pci_dev->dev, name); if (ret) goto free_chan; hc->created[pt] = 1; diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index cd5d26c9909..8df12bf02af 100644 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -64,9 +64,6 @@ MODULE_LICENSE("GPL"); module_param(debug, uint, 0); module_param(poll, uint, S_IRUGO | S_IWUSR); -static LIST_HEAD(HFClist); -static DEFINE_RWLOCK(HFClock); - enum { HFC_CCD_2BD0, HFC_CCD_B000, @@ -136,7 +133,6 @@ struct hfcPCI_hw { struct hfc_pci { - struct list_head list; u_char subtype; u_char chanlimit; u_char initdone; @@ -1227,41 +1223,6 @@ hfcpci_int(int intno, void *dev_id) return IRQ_HANDLED; } -static void -hfcpci_softirq(void *arg) -{ - u_long flags; - struct bchannel *bch; - struct hfc_pci *hc; - - write_lock_irqsave(&HFClock, flags); - list_for_each_entry(hc, &HFClist, list) { - 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); - } - } - write_unlock_irqrestore(&HFClock, flags); - - /* 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); -} - /* * timer callback for D-chan busy resolution. Currently no function */ @@ -2131,7 +2092,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); } @@ -2141,7 +2101,6 @@ setup_card(struct hfc_pci *card) { int err = -EINVAL; u_int i; - u_long flags; char name[MISDN_MAX_IDLEN]; card->dch.debug = debug; @@ -2169,13 +2128,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: @@ -2311,15 +2267,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__); } @@ -2331,6 +2284,46 @@ 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) { @@ -2375,14 +2368,9 @@ HFC_init(void) 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); } diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c index 9116f54def2..9426c9827e4 100644 --- a/drivers/isdn/mISDN/core.c +++ b/drivers/isdn/mISDN/core.c @@ -25,39 +25,183 @@ MODULE_AUTHOR("Karsten Keil"); MODULE_LICENSE("GPL"); module_param(debug, uint, S_IRUGO | S_IWUSR); -static LIST_HEAD(devices); -static DEFINE_RWLOCK(device_lock); static u64 device_ids; #define MAX_DEVICE_ID 63 static LIST_HEAD(Bprotocols); static DEFINE_RWLOCK(bp_lock); +static void mISDN_dev_release(struct device *dev) +{ + /* nothing to do: the device is part of its parent's data structure */ +} + +static ssize_t _show_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return -ENODEV; + return sprintf(buf, "%d\n", mdev->id); +} + +static ssize_t _show_nrbchan(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return -ENODEV; + return sprintf(buf, "%d\n", mdev->nrbchan); +} + +static ssize_t _show_d_protocols(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return -ENODEV; + return sprintf(buf, "%d\n", mdev->Dprotocols); +} + +static ssize_t _show_b_protocols(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return -ENODEV; + return sprintf(buf, "%d\n", mdev->Bprotocols | get_all_Bprotocols()); +} + +static ssize_t _show_protocol(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return -ENODEV; + return sprintf(buf, "%d\n", mdev->D.protocol); +} + +static ssize_t _show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + strcpy(buf, dev_name(dev)); + return strlen(buf); +} + +#if 0 /* hangs */ +static ssize_t _set_name(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err = 0; + char *out = kmalloc(count + 1, GFP_KERNEL); + + if (!out) + return -ENOMEM; + + memcpy(out, buf, count); + if (count && out[count - 1] == '\n') + out[--count] = 0; + if (count) + err = device_rename(dev, out); + kfree(out); + + return (err < 0) ? err : count; +} +#endif + +static ssize_t _show_channelmap(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + char *bp = buf; + int i; + + for (i = 0; i <= mdev->nrbchan; i++) + *bp++ = test_channelmap(i, mdev->channelmap) ? '1' : '0'; + + return bp - buf; +} + +static struct device_attribute mISDN_dev_attrs[] = { + __ATTR(id, S_IRUGO, _show_id, NULL), + __ATTR(d_protocols, S_IRUGO, _show_d_protocols, NULL), + __ATTR(b_protocols, S_IRUGO, _show_b_protocols, NULL), + __ATTR(protocol, S_IRUGO, _show_protocol, NULL), + __ATTR(channelmap, S_IRUGO, _show_channelmap, NULL), + __ATTR(nrbchan, S_IRUGO, _show_nrbchan, NULL), + __ATTR(name, S_IRUGO, _show_name, NULL), +/* __ATTR(name, S_IRUGO|S_IWUSR, _show_name, _set_name), */ + {} +}; + +#ifdef CONFIG_HOTPLUG +static int mISDN_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return 0; + + if (add_uevent_var(env, "nchans=%d", mdev->nrbchan)) + return -ENOMEM; + + return 0; +} +#endif + +static void mISDN_class_release(struct class *cls) +{ + /* do nothing, it's static */ +} + +static struct class mISDN_class = { + .name = "mISDN", + .owner = THIS_MODULE, +#ifdef CONFIG_HOTPLUG + .dev_uevent = mISDN_uevent, +#endif + .dev_attrs = mISDN_dev_attrs, + .dev_release = mISDN_dev_release, + .class_release = mISDN_class_release, +}; + +static int +_get_mdevice(struct device *dev, void *id) +{ + struct mISDNdevice *mdev = dev_to_mISDN(dev); + + if (!mdev) + return 0; + if (mdev->id != *(u_int *)id) + return 0; + return 1; +} + struct mISDNdevice *get_mdevice(u_int id) { - struct mISDNdevice *dev; + return dev_to_mISDN(class_find_device(&mISDN_class, NULL, &id, + _get_mdevice)); +} - read_lock(&device_lock); - list_for_each_entry(dev, &devices, D.list) - if (dev->id == id) { - read_unlock(&device_lock); - return dev; - } - read_unlock(&device_lock); - return NULL; +static int +_get_mdevice_count(struct device *dev, void *cnt) +{ + *(int *)cnt += 1; + return 0; } int get_mdevice_count(void) { - struct mISDNdevice *dev; - int cnt = 0; + int cnt = 0; - read_lock(&device_lock); - list_for_each_entry(dev, &devices, D.list) - cnt++; - read_unlock(&device_lock); + class_for_each_device(&mISDN_class, NULL, &cnt, _get_mdevice_count); return cnt; } @@ -68,19 +212,24 @@ get_free_devid(void) for (i = 0; i <= MAX_DEVICE_ID; i++) if (!test_and_set_bit(i, (u_long *)&device_ids)) - return i; - return -1; + break; + if (i > MAX_DEVICE_ID) + return -1; + return i; } int -mISDN_register_device(struct mISDNdevice *dev, char *name) +mISDN_register_device(struct mISDNdevice *dev, + struct device *parent, char *name) { - u_long flags; int err; dev->id = get_free_devid(); + err = -EBUSY; if (dev->id < 0) - return -EBUSY; + goto error1; + + device_initialize(&dev->dev); if (name && name[0]) dev_set_name(&dev->dev, "%s", name); else @@ -90,26 +239,39 @@ mISDN_register_device(struct mISDNdevice *dev, char *name) dev_name(&dev->dev), dev->id); err = create_stack(dev); if (err) - return err; - write_lock_irqsave(&device_lock, flags); - list_add_tail(&dev->D.list, &devices); - write_unlock_irqrestore(&device_lock, flags); + goto error1; + + dev->dev.class = &mISDN_class; + dev->dev.platform_data = dev; + dev->dev.parent = parent; + dev_set_drvdata(&dev->dev, dev); + + err = device_add(&dev->dev); + if (err) + goto error3; return 0; + +error3: + delete_stack(dev); + return err; +error1: + return err; + } EXPORT_SYMBOL(mISDN_register_device); void mISDN_unregister_device(struct mISDNdevice *dev) { - u_long flags; - if (debug & DEBUG_CORE) printk(KERN_DEBUG "mISDN_unregister %s %d\n", dev_name(&dev->dev), dev->id); - write_lock_irqsave(&device_lock, flags); - list_del(&dev->D.list); - write_unlock_irqrestore(&device_lock, flags); + /* sysfs_remove_link(&dev->dev.kobj, "device"); */ + device_del(&dev->dev); + dev_set_drvdata(&dev->dev, NULL); + test_and_clear_bit(dev->id, (u_long *)&device_ids); delete_stack(dev); + put_device(&dev->dev); } EXPORT_SYMBOL(mISDN_unregister_device); @@ -201,42 +363,43 @@ mISDNInit(void) MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE); mISDN_init_clock(&debug); mISDN_initstack(&debug); + err = class_register(&mISDN_class); + if (err) + goto error1; err = mISDN_inittimer(&debug); if (err) - goto error; + goto error2; err = l1_init(&debug); - if (err) { - mISDN_timer_cleanup(); - goto error; - } + if (err) + goto error3; err = Isdnl2_Init(&debug); - if (err) { - mISDN_timer_cleanup(); - l1_cleanup(); - goto error; - } + if (err) + goto error4; err = misdn_sock_init(&debug); - if (err) { - mISDN_timer_cleanup(); - l1_cleanup(); - Isdnl2_cleanup(); - } -error: + if (err) + goto error5; + return 0; + +error5: + Isdnl2_cleanup(); +error4: + l1_cleanup(); +error3: + mISDN_timer_cleanup(); +error2: + class_unregister(&mISDN_class); +error1: return err; } static void mISDN_cleanup(void) { misdn_sock_cleanup(); - mISDN_timer_cleanup(); - l1_cleanup(); Isdnl2_cleanup(); + l1_cleanup(); + mISDN_timer_cleanup(); + class_unregister(&mISDN_class); - if (!list_empty(&devices)) - printk(KERN_ERR "%s devices still registered\n", __func__); - - if (!list_empty(&Bprotocols)) - printk(KERN_ERR "%s Bprotocols still registered\n", __func__); printk(KERN_DEBUG "mISDNcore unloaded\n"); } diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c index a6a9e1af2ad..abe57498957 100644 --- a/drivers/isdn/mISDN/l1oip_core.c +++ b/drivers/isdn/mISDN/l1oip_core.c @@ -1433,7 +1433,8 @@ init_card(struct l1oip *hc, int pri, int bundle) hc->chan[i + ch].bch = bch; set_channelmap(bch->nr, dch->dev.channelmap); } - ret = mISDN_register_device(&dch->dev, hc->name); + /* TODO: create a parent device for this driver */ + ret = mISDN_register_device(&dch->dev, NULL, hc->name); if (ret) return ret; hc->registered = 1; diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h index 3f9988849f3..d4229aebf64 100644 --- a/include/linux/mISDNif.h +++ b/include/linux/mISDNif.h @@ -531,7 +531,8 @@ _queue_data(struct mISDNchannel *ch, u_int prim, /* global register/unregister functions */ -extern int mISDN_register_device(struct mISDNdevice *, char *name); +extern int mISDN_register_device(struct mISDNdevice *, + struct device *parent, char *name); extern void mISDN_unregister_device(struct mISDNdevice *); extern int mISDN_register_Bprotocol(struct Bprotocol *); extern void mISDN_unregister_Bprotocol(struct Bprotocol *); @@ -539,6 +540,11 @@ extern struct mISDNclock *mISDN_register_clock(char *, int, clockctl_func_t *, void *); extern void mISDN_unregister_clock(struct mISDNclock *); +static inline struct mISDNdevice *dev_to_mISDN(struct device *dev) +{ + return dev_get_drvdata(dev); +} + extern void set_channel_address(struct mISDNchannel *, u_int, u_int); extern void mISDN_clock_update(struct mISDNclock *, int, struct timeval *); extern unsigned short mISDN_clock_get(void); -- cgit v1.2.3 From 400fd9783e7374bdf73b9cee9d7b90e35e3007da Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 11 Oct 2008 08:13:29 +0200 Subject: mISDN: Minor cleanups Signed-off-by: Andreas Eversberg Signed-off-by: Karsten Keil --- drivers/isdn/hardware/mISDN/hfcpci.c | 4 ++-- drivers/isdn/mISDN/dsp_core.c | 2 +- drivers/isdn/mISDN/dsp_pipeline.c | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index 8df12bf02af..2db06490b1a 100644 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -2333,10 +2333,10 @@ HFC_init(void) poll = HFCPCI_BTRANS_THRESHOLD; if (poll != HFCPCI_BTRANS_THRESHOLD) { - tics = poll * HZ / 8000; + tics = (poll * HZ) / 8000; if (tics < 1) tics = 1; - poll = tics * 8000 / HZ; + 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); diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c index c16cb7ac3d2..7e60cb94b5c 100644 --- a/drivers/isdn/mISDN/dsp_core.c +++ b/drivers/isdn/mISDN/dsp_core.c @@ -1141,7 +1141,7 @@ static int dsp_init(void) } else { poll = 8; while (poll <= MAX_POLL) { - tics = poll * HZ / 8000; + tics = (poll * HZ) / 8000; if (tics * 8000 == poll * HZ) { dsp_tics = tics; dsp_poll = poll; diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c index 7a3a3e1e4d8..bf999bdc41c 100644 --- a/drivers/isdn/mISDN/dsp_pipeline.c +++ b/drivers/isdn/mISDN/dsp_pipeline.c @@ -92,7 +92,7 @@ int mISDN_dsp_element_register(struct mISDN_dsp_element *elem) if (!elem) return -EINVAL; - entry = kzalloc(sizeof(struct dsp_element_entry), GFP_KERNEL); + entry = kzalloc(sizeof(struct dsp_element_entry), GFP_ATOMIC); if (!entry) return -ENOMEM; @@ -253,7 +253,7 @@ int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) if (!len) return 0; - dup = kmalloc(len + 1, GFP_KERNEL); + dup = kmalloc(len + 1, GFP_ATOMIC); if (!dup) return 0; strcpy(dup, cfg); @@ -270,7 +270,7 @@ int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg) elem = entry->elem; pipeline_entry = kmalloc(sizeof(struct - dsp_pipeline_entry), GFP_KERNEL); + dsp_pipeline_entry), GFP_ATOMIC); if (!pipeline_entry) { printk(KERN_ERR "%s: failed to add " "entry to pipeline: %s (out of " -- cgit v1.2.3 From e314f89a62c1d74380455690325b1336ea0dca9d Mon Sep 17 00:00:00 2001 From: Matthias Urlichs Date: Thu, 16 Oct 2008 13:58:54 +0200 Subject: mISDN: Add MODULE_DEVICE_TABLE() to hfcpci Add missed table. Signed-off-by: Matthias Urlichs Signed-off-by: Karsten Keil --- drivers/isdn/hardware/mISDN/hfcpci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index 2db06490b1a..019dbe9b082 100644 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -2376,3 +2376,5 @@ HFC_cleanup(void) module_init(HFC_init); module_exit(HFC_cleanup); + +MODULE_DEVICE_TABLE(pci, hfc_ids); -- cgit v1.2.3 From c3b3cdeba5a671ce5d0064c52c8a85f5b1e72e92 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 9 Nov 2008 10:23:19 +0100 Subject: mISDN: Added missing create_l1() call create_l1() was missed when changing mode to TE. Signed-off-by: Andreas Eversberg Signed-off-by: Karsten Keil --- drivers/isdn/hardware/mISDN/hfcpci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index 019dbe9b082..917bf41a293 100644 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -1937,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); -- cgit v1.2.3 From c6a2e587e5b28177eabfc2db4d5abd25b87604f2 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 28 Dec 2008 16:31:26 +0100 Subject: mISDN: Fix kernel crash when doing hardware conference with more than two members Fix kernel crash when doing hardware conference with more than two members. Removed DTMF threshold notice when debugging is disabled. Signed-off-by: Andreas Eversberg Signed-off-by: Karsten Keil --- drivers/isdn/mISDN/dsp_cmx.c | 7 +++---- drivers/isdn/mISDN/dsp_core.c | 5 +++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c index 02f643456b1..0ac67bff303 100644 --- a/drivers/isdn/mISDN/dsp_cmx.c +++ b/drivers/isdn/mISDN/dsp_cmx.c @@ -927,10 +927,6 @@ conf_software: /* for more than two members.. */ - /* in case of hdlc, we change to software */ - if (dsp->hdlc) - goto conf_software; - /* if all members already have the same conference */ if (all_conf) return; @@ -941,6 +937,9 @@ conf_software: if (current_conf >= 0) { join_members: list_for_each_entry(member, &conf->mlist, list) { + /* in case of hdlc, change to software */ + if (member->dsp->hdlc) + goto conf_software; /* join to current conference */ if (member->dsp->hfc_conf == current_conf) continue; diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c index 7e60cb94b5c..3083338716b 100644 --- a/drivers/isdn/mISDN/dsp_core.c +++ b/drivers/isdn/mISDN/dsp_core.c @@ -301,8 +301,9 @@ dsp_control_req(struct dsp *dsp, struct mISDNhead *hh, struct sk_buff *skb) if (dsp_debug & DEBUG_DSP_CORE) printk(KERN_DEBUG "%s: start dtmf\n", __func__); if (len == sizeof(int)) { - printk(KERN_NOTICE "changing DTMF Threshold " - "to %d\n", *((int *)data)); + if (dsp_debug & DEBUG_DSP_CORE) + printk(KERN_NOTICE "changing DTMF Threshold " + "to %d\n", *((int *)data)); dsp->dtmf.treshold = (*(int *)data) * 10000; } /* init goertzel */ -- cgit v1.2.3 From 3f75e84a6a697c5cffb78ee15e79498a35473e05 Mon Sep 17 00:00:00 2001 From: Martin Bachem Date: Tue, 4 Nov 2008 14:11:22 +0100 Subject: mISDN: Add layer1 prim MPH_INFORMATION_REQ MPH_INFORMATION provides full D- and B-Channel status overview - new layer1 primitive: MPF_INFORMATON_REQ - layer1 replies with MPH_INFORMATION_IND containing - dch->[state,Flags,nrbchan] - bch[]->[protocol,Flags] - hardware driver should send MPH_INFORMATION_IND on all ph state changes and BChannel state changes to MISDN_ID_ANY Signed-off-by: Martin Bachem Signed-off-by: Karsten Keil --- include/linux/mISDNif.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h index d4229aebf64..557477ac3d5 100644 --- a/include/linux/mISDNif.h +++ b/include/linux/mISDNif.h @@ -289,6 +289,23 @@ struct mISDN_devrename { char name[MISDN_MAX_IDLEN]; /* new name */ }; +/* MPH_INFORMATION_REQ payload */ +struct ph_info_ch { + __u32 protocol; + __u64 Flags; +}; + +struct ph_info_dch { + struct ph_info_ch ch; + __u16 state; + __u16 num_bch; +}; + +struct ph_info { + struct ph_info_dch dch; + struct ph_info_ch bch[]; +}; + /* timer device ioctl */ #define IMADDTIMER _IOR('I', 64, int) #define IMDELTIMER _IOR('I', 65, int) -- cgit v1.2.3 From 69f52adb2d534afc41fcc658f155e01f0b322f9e Mon Sep 17 00:00:00 2001 From: Karsten Keil Date: Fri, 9 Jan 2009 16:20:51 +0100 Subject: mISDN: Add HFC USB driver Enable support for USB ISDN TAs with Cologne Chip AG's HFC-S USB ISDN Controller. Signed-off-by: Martin Bachem Signed-off-by: Karsten Keil --- drivers/isdn/hardware/mISDN/Kconfig | 7 + drivers/isdn/hardware/mISDN/Makefile | 1 + drivers/isdn/hardware/mISDN/hfcsusb.c | 2196 +++++++++++++++++++++++++++++++++ drivers/isdn/hardware/mISDN/hfcsusb.h | 418 +++++++ 4 files changed, 2622 insertions(+) create mode 100644 drivers/isdn/hardware/mISDN/hfcsusb.c create mode 100644 drivers/isdn/hardware/mISDN/hfcsusb.h diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig index 14793480c45..fd112ae252c 100644 --- a/drivers/isdn/hardware/mISDN/Kconfig +++ b/drivers/isdn/hardware/mISDN/Kconfig @@ -23,3 +23,10 @@ config MISDN_HFCMULTI * HFC-8S (8 S/T interfaces on one chip) * HFC-E1 (E1 interface for 2Mbit ISDN) +config MISDN_HFCUSB + tristate "Support for HFC-S USB based TAs" + depends on USB + help + Enable support for USB ISDN TAs with Cologne Chip AG's + HFC-S USB ISDN Controller + diff --git a/drivers/isdn/hardware/mISDN/Makefile b/drivers/isdn/hardware/mISDN/Makefile index 1e7ca5332ad..b0403526bbb 100644 --- a/drivers/isdn/hardware/mISDN/Makefile +++ b/drivers/isdn/hardware/mISDN/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_MISDN_HFCPCI) += hfcpci.o obj-$(CONFIG_MISDN_HFCMULTI) += hfcmulti.o +obj-$(CONFIG_MISDN_HFCUSB) += hfcsusb.o diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.c b/drivers/isdn/hardware/mISDN/hfcsusb.c new file mode 100644 index 00000000000..ba6925fbf38 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfcsusb.c @@ -0,0 +1,2196 @@ +/* hfcsusb.c + * mISDN driver for Colognechip HFC-S USB chip + * + * Copyright 2001 by Peter Sprenger (sprenger@moving-bytes.de) + * Copyright 2008 by Martin Bachem (info@bachem-it.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * module params + * debug=, default=0, with n=0xHHHHGGGG + * H - l1 driver flags described in hfcsusb.h + * G - common mISDN debug flags described at mISDNhw.h + * + * poll=, default 128 + * n : burst size of PH_DATA_IND at transparent rx data + * + */ + +#include +#include +#include +#include +#include "hfcsusb.h" + +const char *hfcsusb_rev = "Revision: 0.3.3 (socket), 2008-11-05"; + +static unsigned int debug; +static int poll = DEFAULT_TRANSP_BURST_SZ; + +static LIST_HEAD(HFClist); +static DEFINE_RWLOCK(HFClock); + + +MODULE_AUTHOR("Martin Bachem"); +MODULE_LICENSE("GPL"); +module_param(debug, uint, S_IRUGO | S_IWUSR); +module_param(poll, int, 0); + +static int hfcsusb_cnt; + +/* some function prototypes */ +static void hfcsusb_ph_command(struct hfcsusb *hw, u_char command); +static void release_hw(struct hfcsusb *hw); +static void reset_hfcsusb(struct hfcsusb *hw); +static void setPortMode(struct hfcsusb *hw); +static void hfcsusb_start_endpoint(struct hfcsusb *hw, int channel); +static void hfcsusb_stop_endpoint(struct hfcsusb *hw, int channel); +static int hfcsusb_setup_bch(struct bchannel *bch, int protocol); +static void deactivate_bchannel(struct bchannel *bch); +static void hfcsusb_ph_info(struct hfcsusb *hw); + +/* start next background transfer for control channel */ +static void +ctrl_start_transfer(struct hfcsusb *hw) +{ + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + if (hw->ctrl_cnt) { + hw->ctrl_urb->pipe = hw->ctrl_out_pipe; + hw->ctrl_urb->setup_packet = (u_char *)&hw->ctrl_write; + hw->ctrl_urb->transfer_buffer = NULL; + hw->ctrl_urb->transfer_buffer_length = 0; + hw->ctrl_write.wIndex = + cpu_to_le16(hw->ctrl_buff[hw->ctrl_out_idx].hfcs_reg); + hw->ctrl_write.wValue = + cpu_to_le16(hw->ctrl_buff[hw->ctrl_out_idx].reg_val); + + usb_submit_urb(hw->ctrl_urb, GFP_ATOMIC); + } +} + +/* + * queue a control transfer request to write HFC-S USB + * chip register using CTRL resuest queue + */ +static int write_reg(struct hfcsusb *hw, __u8 reg, __u8 val) +{ + struct ctrl_buf *buf; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s reg(0x%02x) val(0x%02x)\n", + hw->name, __func__, reg, val); + + spin_lock(&hw->ctrl_lock); + if (hw->ctrl_cnt >= HFC_CTRL_BUFSIZE) + return 1; + buf = &hw->ctrl_buff[hw->ctrl_in_idx]; + buf->hfcs_reg = reg; + buf->reg_val = val; + if (++hw->ctrl_in_idx >= HFC_CTRL_BUFSIZE) + hw->ctrl_in_idx = 0; + if (++hw->ctrl_cnt == 1) + ctrl_start_transfer(hw); + spin_unlock(&hw->ctrl_lock); + + return 0; +} + +/* control completion routine handling background control cmds */ +static void +ctrl_complete(struct urb *urb) +{ + struct hfcsusb *hw = (struct hfcsusb *) urb->context; + struct ctrl_buf *buf; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + urb->dev = hw->dev; + if (hw->ctrl_cnt) { + buf = &hw->ctrl_buff[hw->ctrl_out_idx]; + hw->ctrl_cnt--; /* decrement actual count */ + if (++hw->ctrl_out_idx >= HFC_CTRL_BUFSIZE) + hw->ctrl_out_idx = 0; /* pointer wrap */ + + ctrl_start_transfer(hw); /* start next transfer */ + } +} + +/* handle LED bits */ +static void +set_led_bit(struct hfcsusb *hw, signed short led_bits, int set_on) +{ + if (set_on) { + if (led_bits < 0) + hw->led_state &= ~abs(led_bits); + else + hw->led_state |= led_bits; + } else { + if (led_bits < 0) + hw->led_state |= abs(led_bits); + else + hw->led_state &= ~led_bits; + } +} + +/* handle LED requests */ +static void +handle_led(struct hfcsusb *hw, int event) +{ + struct hfcsusb_vdata *driver_info = (struct hfcsusb_vdata *) + hfcsusb_idtab[hw->vend_idx].driver_info; + __u8 tmpled; + + if (driver_info->led_scheme == LED_OFF) + return; + tmpled = hw->led_state; + + switch (event) { + case LED_POWER_ON: + set_led_bit(hw, driver_info->led_bits[0], 1); + set_led_bit(hw, driver_info->led_bits[1], 0); + set_led_bit(hw, driver_info->led_bits[2], 0); + set_led_bit(hw, driver_info->led_bits[3], 0); + break; + case LED_POWER_OFF: + set_led_bit(hw, driver_info->led_bits[0], 0); + set_led_bit(hw, driver_info->led_bits[1], 0); + set_led_bit(hw, driver_info->led_bits[2], 0); + set_led_bit(hw, driver_info->led_bits[3], 0); + break; + case LED_S0_ON: + set_led_bit(hw, driver_info->led_bits[1], 1); + break; + case LED_S0_OFF: + set_led_bit(hw, driver_info->led_bits[1], 0); + break; + case LED_B1_ON: + set_led_bit(hw, driver_info->led_bits[2], 1); + break; + case LED_B1_OFF: + set_led_bit(hw, driver_info->led_bits[2], 0); + break; + case LED_B2_ON: + set_led_bit(hw, driver_info->led_bits[3], 1); + break; + case LED_B2_OFF: + set_led_bit(hw, driver_info->led_bits[3], 0); + break; + } + + if (hw->led_state != tmpled) { + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s reg(0x%02x) val(x%02x)\n", + hw->name, __func__, + HFCUSB_P_DATA, hw->led_state); + + write_reg(hw, HFCUSB_P_DATA, hw->led_state); + } +} + +/* + * Layer2 -> Layer 1 Bchannel data + */ +static int +hfcusb_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfcsusb *hw = bch->hw; + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + u_long flags; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + switch (hh->prim) { + case PH_DATA_REQ: + spin_lock_irqsave(&hw->lock, flags); + ret = bchannel_senddata(bch, skb); + spin_unlock_irqrestore(&hw->lock, flags); + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s PH_DATA_REQ ret(%i)\n", + hw->name, __func__, ret); + if (ret > 0) { + /* + * other l1 drivers don't send early confirms on + * transp data, but hfcsusb does because tx_next + * skb is needed in tx_iso_complete() + */ + queue_ch_frame(ch, PH_DATA_CNF, hh->id, NULL); + ret = 0; + } + return ret; + case PH_ACTIVATE_REQ: + if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { + hfcsusb_start_endpoint(hw, bch->nr); + ret = hfcsusb_setup_bch(bch, ch->protocol); + } else + ret = 0; + if (!ret) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + break; + case PH_DEACTIVATE_REQ: + deactivate_bchannel(bch); + _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + ret = 0; + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +/* + * send full D/B channel status information + * as MPH_INFORMATION_IND + */ +static void +hfcsusb_ph_info(struct hfcsusb *hw) +{ + struct ph_info *phi; + struct dchannel *dch = &hw->dch; + int i; + + phi = kzalloc(sizeof(struct ph_info) + + dch->dev.nrbchan * sizeof(struct ph_info_ch), GFP_ATOMIC); + phi->dch.ch.protocol = hw->protocol; + phi->dch.ch.Flags = dch->Flags; + phi->dch.state = dch->state; + phi->dch.num_bch = dch->dev.nrbchan; + for (i = 0; i < dch->dev.nrbchan; i++) { + phi->bch[i].protocol = hw->bch[i].ch.protocol; + phi->bch[i].Flags = hw->bch[i].Flags; + } + _queue_data(&dch->dev.D, MPH_INFORMATION_IND, MISDN_ID_ANY, + sizeof(struct ph_info_dch) + dch->dev.nrbchan * + sizeof(struct ph_info_ch), phi, GFP_ATOMIC); +} + +/* + * Layer2 -> Layer 1 Dchannel data + */ +static int +hfcusb_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct mISDNhead *hh = mISDN_HEAD_P(skb); + struct hfcsusb *hw = dch->hw; + int ret = -EINVAL; + u_long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s: PH_DATA_REQ\n", + hw->name, __func__); + + spin_lock_irqsave(&hw->lock, flags); + ret = dchannel_senddata(dch, skb); + spin_unlock_irqrestore(&hw->lock, flags); + if (ret > 0) { + ret = 0; + queue_ch_frame(ch, PH_DATA_CNF, hh->id, NULL); + } + break; + + case PH_ACTIVATE_REQ: + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s: PH_ACTIVATE_REQ %s\n", + hw->name, __func__, + (hw->protocol == ISDN_P_NT_S0) ? "NT" : "TE"); + + if (hw->protocol == ISDN_P_NT_S0) { + ret = 0; + if (test_bit(FLG_ACTIVE, &dch->Flags)) { + _queue_data(&dch->dev.D, + PH_ACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_ATOMIC); + } else { + hfcsusb_ph_command(hw, + HFC_L1_ACTIVATE_NT); + test_and_set_bit(FLG_L2_ACTIVATED, + &dch->Flags); + } + } else { + hfcsusb_ph_command(hw, HFC_L1_ACTIVATE_TE); + ret = l1_event(dch->l1, hh->prim); + } + break; + + case PH_DEACTIVATE_REQ: + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s: PH_DEACTIVATE_REQ\n", + hw->name, __func__); + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + + if (hw->protocol == ISDN_P_NT_S0) { + hfcsusb_ph_command(hw, HFC_L1_DEACTIVATE_NT); + spin_lock_irqsave(&hw->lock, flags); + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + spin_unlock_irqrestore(&hw->lock, flags); +#ifdef FIXME + if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) + dchannel_sched_event(&hc->dch, D_CLEARBUSY); +#endif + ret = 0; + } else + ret = l1_event(dch->l1, hh->prim); + break; + case MPH_INFORMATION_REQ: + hfcsusb_ph_info(hw); + ret = 0; + break; + } + + return ret; +} + +/* + * Layer 1 callback function + */ +static int +hfc_l1callback(struct dchannel *dch, u_int cmd) +{ + struct hfcsusb *hw = dch->hw; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s cmd 0x%x\n", + hw->name, __func__, cmd); + + switch (cmd) { + case INFO3_P8: + case INFO3_P10: + case HW_RESET_REQ: + case HW_POWERUP_REQ: + break; + + case HW_DEACT_REQ: + skb_queue_purge(&dch->squeue); + if (dch->tx_skb) { + dev_kfree_skb(dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + dev_kfree_skb(dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + break; + case PH_ACTIVATE_IND: + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + case PH_DEACTIVATE_IND: + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL, + GFP_ATOMIC); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: unknown cmd %x\n", + hw->name, __func__, cmd); + return -1; + } + hfcsusb_ph_info(hw); + return 0; +} + +static int +open_dchannel(struct hfcsusb *hw, struct mISDNchannel *ch, + struct channel_req *rq) +{ + int err = 0; + + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: %s: dev(%d) open addr(%i) from %p\n", + hw->name, __func__, hw->dch.dev.id, rq->adr.channel, + __builtin_return_address(0)); + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + + test_and_clear_bit(FLG_ACTIVE, &hw->dch.Flags); + test_and_clear_bit(FLG_ACTIVE, &hw->ech.Flags); + hfcsusb_start_endpoint(hw, HFC_CHAN_D); + + /* E-Channel logging */ + if (rq->adr.channel == 1) { + if (hw->fifos[HFCUSB_PCM_RX].pipe) { + hfcsusb_start_endpoint(hw, HFC_CHAN_E); + set_bit(FLG_ACTIVE, &hw->ech.Flags); + _queue_data(&hw->ech.dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + } else + return -EINVAL; + } + + if (!hw->initdone) { + hw->protocol = rq->protocol; + if (rq->protocol == ISDN_P_TE_S0) { + err = create_l1(&hw->dch, hfc_l1callback); + if (err) + return err; + } + setPortMode(hw); + ch->protocol = rq->protocol; + hw->initdone = 1; + } else { + if (rq->protocol != ch->protocol) + return -EPROTONOSUPPORT; + } + + if (((ch->protocol == ISDN_P_NT_S0) && (hw->dch.state == 3)) || + ((ch->protocol == ISDN_P_TE_S0) && (hw->dch.state == 7))) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + rq->ch = ch; + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s: %s: cannot get module\n", + hw->name, __func__); + return 0; +} + +static int +open_bchannel(struct hfcsusb *hw, struct channel_req *rq) +{ + struct bchannel *bch; + + if (rq->adr.channel > 2) + return -EINVAL; + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s B%i\n", + hw->name, __func__, rq->adr.channel); + + bch = &hw->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; + + /* start USB endpoint for bchannel */ + if (rq->adr.channel == 1) + hfcsusb_start_endpoint(hw, HFC_CHAN_B1); + else + hfcsusb_start_endpoint(hw, HFC_CHAN_B2); + + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s: %s:cannot get module\n", + hw->name, __func__); + return 0; +} + +static int +channel_ctrl(struct hfcsusb *hw, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s op(0x%x) channel(0x%x)\n", + hw->name, __func__, (cq->op), (cq->channel)); + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_CONNECT | + MISDN_CTRL_DISCONNECT; + break; + default: + printk(KERN_WARNING "%s: %s: unknown Op %x\n", + hw->name, __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +/* + * device control function + */ +static int +hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct hfcsusb *hw = dch->hw; + struct channel_req *rq; + int err = 0; + + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: cmd:%x %p\n", + hw->name, __func__, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + rq = arg; + if ((rq->protocol == ISDN_P_TE_S0) || + (rq->protocol == ISDN_P_NT_S0)) + err = open_dchannel(hw, ch, rq); + else + err = open_bchannel(hw, rq); + if (!err) + hw->open++; + break; + case CLOSE_CHANNEL: + hw->open--; + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG + "%s: %s: dev(%d) close from %p (open %d)\n", + hw->name, __func__, hw->dch.dev.id, + __builtin_return_address(0), hw->open); + if (!hw->open) { + hfcsusb_stop_endpoint(hw, HFC_CHAN_D); + if (hw->fifos[HFCUSB_PCM_RX].pipe) + hfcsusb_stop_endpoint(hw, HFC_CHAN_E); + handle_led(hw, LED_POWER_ON); + } + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: + err = channel_ctrl(hw, arg); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: unknown command %x\n", + hw->name, __func__, cmd); + return -EINVAL; + } + return err; +} + +/* + * S0 TE state change event handler + */ +static void +ph_state_te(struct dchannel *dch) +{ + struct hfcsusb *hw = dch->hw; + + if (debug & DEBUG_HW) { + if (dch->state <= HFC_MAX_TE_LAYER1_STATE) + printk(KERN_DEBUG "%s: %s: %s\n", hw->name, __func__, + HFC_TE_LAYER1_STATES[dch->state]); + else + printk(KERN_DEBUG "%s: %s: TE F%d\n", + hw->name, __func__, dch->state); + } + + switch (dch->state) { + case 0: + l1_event(dch->l1, HW_RESET_IND); + break; + case 3: + l1_event(dch->l1, HW_DEACT_IND); + break; + case 5: + case 8: + l1_event(dch->l1, ANYSIGNAL); + break; + case 6: + l1_event(dch->l1, INFO2); + break; + case 7: + l1_event(dch->l1, INFO4_P8); + break; + } + if (dch->state == 7) + handle_led(hw, LED_S0_ON); + else + handle_led(hw, LED_S0_OFF); +} + +/* + * S0 NT state change event handler + */ +static void +ph_state_nt(struct dchannel *dch) +{ + struct hfcsusb *hw = dch->hw; + + if (debug & DEBUG_HW) { + if (dch->state <= HFC_MAX_NT_LAYER1_STATE) + printk(KERN_DEBUG "%s: %s: %s\n", + hw->name, __func__, + HFC_NT_LAYER1_STATES[dch->state]); + + else + printk(KERN_INFO DRIVER_NAME "%s: %s: NT G%d\n", + hw->name, __func__, dch->state); + } + + switch (dch->state) { + case (1): + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + hw->nt_timer = 0; + hw->timers &= ~NT_ACTIVATION_TIMER; + handle_led(hw, LED_S0_OFF); + break; + + case (2): + if (hw->nt_timer < 0) { + hw->nt_timer = 0; + hw->timers &= ~NT_ACTIVATION_TIMER; + hfcsusb_ph_command(dch->hw, HFC_L1_DEACTIVATE_NT); + } else { + hw->timers |= NT_ACTIVATION_TIMER; + hw->nt_timer = NT_T1_COUNT; + /* allow G2 -> G3 transition */ + write_reg(hw, HFCUSB_STATES, 2 | HFCUSB_NT_G2_G3); + } + break; + case (3): + hw->nt_timer = 0; + hw->timers &= ~NT_ACTIVATION_TIMER; + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + handle_led(hw, LED_S0_ON); + break; + case (4): + hw->nt_timer = 0; + hw->timers &= ~NT_ACTIVATION_TIMER; + break; + default: + break; + } + hfcsusb_ph_info(hw); +} + +static void +ph_state(struct dchannel *dch) +{ + struct hfcsusb *hw = dch->hw; + + if (hw->protocol == ISDN_P_NT_S0) + ph_state_nt(dch); + else if (hw->protocol == ISDN_P_TE_S0) + ph_state_te(dch); +} + +/* + * disable/enable BChannel for desired protocoll + */ +static int +hfcsusb_setup_bch(struct bchannel *bch, int protocol) +{ + struct hfcsusb *hw = bch->hw; + __u8 conhdlc, sctrl, sctrl_r; + + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: protocol %x-->%x B%d\n", + hw->name, __func__, bch->state, protocol, + bch->nr); + + /* setup val for CON_HDLC */ + conhdlc = 0; + if (protocol > ISDN_P_NONE) + conhdlc = 8; /* enable FIFO */ + + switch (protocol) { + case (-1): /* used for init */ + bch->state = -1; + /* fall trough */ + case (ISDN_P_NONE): + if (bch->state == ISDN_P_NONE) + return 0; /* already in idle state */ + bch->state = ISDN_P_NONE; + clear_bit(FLG_HDLC, &bch->Flags); + clear_bit(FLG_TRANSPARENT, &bch->Flags); + break; + case (ISDN_P_B_RAW): + conhdlc |= 2; + bch->state = protocol; + set_bit(FLG_TRANSPARENT, &bch->Flags); + break; + case (ISDN_P_B_HDLC): + bch->state = protocol; + set_bit(FLG_HDLC, &bch->Flags); + break; + default: + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: prot not known %x\n", + hw->name, __func__, protocol); + return -ENOPROTOOPT; + } + + if (protocol >= ISDN_P_NONE) { + write_reg(hw, HFCUSB_FIFO, (bch->nr == 1) ? 0 : 2); + write_reg(hw, HFCUSB_CON_HDLC, conhdlc); + write_reg(hw, HFCUSB_INC_RES_F, 2); + write_reg(hw, HFCUSB_FIFO, (bch->nr == 1) ? 1 : 3); + write_reg(hw, HFCUSB_CON_HDLC, conhdlc); + write_reg(hw, HFCUSB_INC_RES_F, 2); + + sctrl = 0x40 + ((hw->protocol == ISDN_P_TE_S0) ? 0x00 : 0x04); + sctrl_r = 0x0; + if (test_bit(FLG_ACTIVE, &hw->bch[0].Flags)) { + sctrl |= 1; + sctrl_r |= 1; + } + if (test_bit(FLG_ACTIVE, &hw->bch[1].Flags)) { + sctrl |= 2; + sctrl_r |= 2; + } + write_reg(hw, HFCUSB_SCTRL, sctrl); + write_reg(hw, HFCUSB_SCTRL_R, sctrl_r); + + if (protocol > ISDN_P_NONE) + handle_led(hw, (bch->nr == 1) ? LED_B1_ON : LED_B2_ON); + else + handle_led(hw, (bch->nr == 1) ? LED_B1_OFF : + LED_B2_OFF); + } + hfcsusb_ph_info(hw); + return 0; +} + +static void +hfcsusb_ph_command(struct hfcsusb *hw, u_char command) +{ + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: %x\n", + hw->name, __func__, command); + + switch (command) { + case HFC_L1_ACTIVATE_TE: + /* force sending sending INFO1 */ + write_reg(hw, HFCUSB_STATES, 0x14); + /* start l1 activation */ + write_reg(hw, HFCUSB_STATES, 0x04); + break; + + case HFC_L1_FORCE_DEACTIVATE_TE: + write_reg(hw, HFCUSB_STATES, 0x10); + write_reg(hw, HFCUSB_STATES, 0x03); + break; + + case HFC_L1_ACTIVATE_NT: + if (hw->dch.state == 3) + _queue_data(&hw->dch.dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + else + write_reg(hw, HFCUSB_STATES, HFCUSB_ACTIVATE | + HFCUSB_DO_ACTION | HFCUSB_NT_G2_G3); + break; + + case HFC_L1_DEACTIVATE_NT: + write_reg(hw, HFCUSB_STATES, + HFCUSB_DO_ACTION); + break; + } +} + +/* + * Layer 1 B-channel hardware access + */ +static int +channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + 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); + ret = -EINVAL; + break; + } + return ret; +} + +/* collect data from incoming interrupt or isochron USB data */ +static void +hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len, + int finish) +{ + struct hfcsusb *hw = fifo->hw; + struct sk_buff *rx_skb = NULL; + int maxlen = 0; + int fifon = fifo->fifonum; + int i; + int hdlc = 0; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s: fifo(%i) len(%i) " + "dch(%p) bch(%p) ech(%p)\n", + hw->name, __func__, fifon, len, + fifo->dch, fifo->bch, fifo->ech); + + if (!len) + return; + + if ((!!fifo->dch + !!fifo->bch + !!fifo->ech) != 1) { + printk(KERN_DEBUG "%s: %s: undefined channel\n", + hw->name, __func__); + return; + } + + spin_lock(&hw->lock); + if (fifo->dch) { + rx_skb = fifo->dch->rx_skb; + maxlen = fifo->dch->maxlen; + hdlc = 1; + } + if (fifo->bch) { + rx_skb = fifo->bch->rx_skb; + maxlen = fifo->bch->maxlen; + hdlc = test_bit(FLG_HDLC, &fifo->bch->Flags); + } + if (fifo->ech) { + rx_skb = fifo->ech->rx_skb; + maxlen = fifo->ech->maxlen; + hdlc = 1; + } + + if (!rx_skb) { + rx_skb = mI_alloc_skb(maxlen, GFP_ATOMIC); + if (rx_skb) { + if (fifo->dch) + fifo->dch->rx_skb = rx_skb; + if (fifo->bch) + fifo->bch->rx_skb = rx_skb; + if (fifo->ech) + fifo->ech->rx_skb = rx_skb; + skb_trim(rx_skb, 0); + } else { + printk(KERN_DEBUG "%s: %s: No mem for rx_skb\n", + hw->name, __func__); + spin_unlock(&hw->lock); + return; + } + } + + if (fifo->dch || fifo->ech) { + /* D/E-Channel SKB range check */ + if ((rx_skb->len + len) >= MAX_DFRAME_LEN_L1) { + printk(KERN_DEBUG "%s: %s: sbk mem exceeded " + "for fifo(%d) HFCUSB_D_RX\n", + hw->name, __func__, fifon); + skb_trim(rx_skb, 0); + spin_unlock(&hw->lock); + return; + } + } else if (fifo->bch) { + /* B-Channel SKB range check */ + if ((rx_skb->len + len) >= (MAX_BCH_SIZE + 3)) { + printk(KERN_DEBUG "%s: %s: sbk mem exceeded " + "for fifo(%d) HFCUSB_B_RX\n", + hw->name, __func__, fifon); + skb_trim(rx_skb, 0); + spin_unlock(&hw->lock); + return; + } + } + + memcpy(skb_put(rx_skb, len), data, len); + + if (hdlc) { + /* we have a complete hdlc packet */ + if (finish) { + if ((rx_skb->len > 3) && + (!(rx_skb->data[rx_skb->len - 1]))) { + if (debug & DBG_HFC_FIFO_VERBOSE) { + printk(KERN_DEBUG "%s: %s: fifon(%i)" + " new RX len(%i): ", + hw->name, __func__, fifon, + rx_skb->len); + i = 0; + while (i < rx_skb->len) + printk("%02x ", + rx_skb->data[i++]); + printk("\n"); + } + + /* remove CRC & status */ + skb_trim(rx_skb, rx_skb->len - 3); + + if (fifo->dch) + recv_Dchannel(fifo->dch); + if (fifo->bch) + recv_Bchannel(fifo->bch); + if (fifo->ech) + recv_Echannel(fifo->ech, + &hw->dch); + } else { + if (debug & DBG_HFC_FIFO_VERBOSE) { + printk(KERN_DEBUG + "%s: CRC or minlen ERROR fifon(%i) " + "RX len(%i): ", + hw->name, fifon, rx_skb->len); + i = 0; + while (i < rx_skb->len) + printk("%02x ", + rx_skb->data[i++]); + printk("\n"); + } + skb_trim(rx_skb, 0); + } + } + } else { + /* deliver transparent data to layer2 */ + if (rx_skb->len >= poll) + recv_Bchannel(fifo->bch); + } + spin_unlock(&hw->lock); +} + +void +fill_isoc_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, + void *buf, int num_packets, int packet_size, int interval, + usb_complete_t complete, void *context) +{ + int k; + + usb_fill_bulk_urb(urb, dev, pipe, buf, packet_size * num_packets, + complete, context); + + urb->number_of_packets = num_packets; + urb->transfer_flags = URB_ISO_ASAP; + urb->actual_length = 0; + urb->interval = interval; + + for (k = 0; k < num_packets; k++) { + urb->iso_frame_desc[k].offset = packet_size * k; + urb->iso_frame_desc[k].length = packet_size; + urb->iso_frame_desc[k].actual_length = 0; + } +} + +/* receive completion routine for all ISO tx fifos */ +static void +rx_iso_complete(struct urb *urb) +{ + struct iso_urb *context_iso_urb = (struct iso_urb *) urb->context; + struct usb_fifo *fifo = context_iso_urb->owner_fifo; + struct hfcsusb *hw = fifo->hw; + int k, len, errcode, offset, num_isoc_packets, fifon, maxlen, + status, iso_status, i; + __u8 *buf; + static __u8 eof[8]; + __u8 s0_state; + + fifon = fifo->fifonum; + status = urb->status; + + spin_lock(&hw->lock); + if (fifo->stop_gracefull) { + fifo->stop_gracefull = 0; + fifo->active = 0; + spin_unlock(&hw->lock); + return; + } + spin_unlock(&hw->lock); + + /* + * ISO transfer only partially completed, + * look at individual frame status for details + */ + if (status == -EXDEV) { + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: with -EXDEV " + "urb->status %d, fifonum %d\n", + hw->name, __func__, status, fifon); + + /* clear status, so go on with ISO transfers */ + status = 0; + } + + s0_state = 0; + if (fifo->active && !status) { + num_isoc_packets = iso_packets[fifon]; + maxlen = fifo->usb_packet_maxlen; + + for (k = 0; k < num_isoc_packets; ++k) { + len = urb->iso_frame_desc[k].actual_length; + offset = urb->iso_frame_desc[k].offset; + buf = context_iso_urb->buffer + offset; + iso_status = urb->iso_frame_desc[k].status; + + if (iso_status && (debug & DBG_HFC_FIFO_VERBOSE)) { + printk(KERN_DEBUG "%s: %s: " + "ISO packet %i, status: %i\n", + hw->name, __func__, k, iso_status); + } + + /* USB data log for every D ISO in */ + if ((fifon == HFCUSB_D_RX) && + (debug & DBG_HFC_USB_VERBOSE)) { + printk(KERN_DEBUG + "%s: %s: %d (%d/%d) len(%d) ", + hw->name, __func__, urb->start_frame, + k, num_isoc_packets-1, + len); + for (i = 0; i < len; i++) + printk("%x ", buf[i]); + printk("\n"); + } + + if (!iso_status) { + if (fifo->last_urblen != maxlen) { + /* + * save fifo fill-level threshold bits + * to use them later in TX ISO URB + * completions + */ + hw->threshold_mask = buf[1]; + + if (fifon == HFCUSB_D_RX) + s0_state = (buf[0] >> 4); + + eof[fifon] = buf[0] & 1; + if (len > 2) + hfcsusb_rx_frame(fifo, buf + 2, + len - 2, (len < maxlen) + ? eof[fifon] : 0); + } else + hfcsusb_rx_frame(fifo, buf, len, + (len < maxlen) ? + eof[fifon] : 0); + fifo->last_urblen = len; + } + } + + /* signal S0 layer1 state change */ + if ((s0_state) && (hw->initdone) && + (s0_state != hw->dch.state)) { + hw->dch.state = s0_state; + schedule_event(&hw->dch, FLG_PHCHANGE); + } + + fill_isoc_urb(urb, fifo->hw->dev, fifo->pipe, + context_iso_urb->buffer, num_isoc_packets, + fifo->usb_packet_maxlen, fifo->intervall, + (usb_complete_t)rx_iso_complete, urb->context); + errcode = usb_submit_urb(urb, GFP_ATOMIC); + if (errcode < 0) { + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: error submitting " + "ISO URB: %d\n", + hw->name, __func__, errcode); + } + } else { + if (status && (debug & DBG_HFC_URB_INFO)) + printk(KERN_DEBUG "%s: %s: rx_iso_complete : " + "urb->status %d, fifonum %d\n", + hw->name, __func__, status, fifon); + } +} + +/* receive completion routine for all interrupt rx fifos */ +static void +rx_int_complete(struct urb *urb) +{ + int len, status, i; + __u8 *buf, maxlen, fifon; + struct usb_fifo *fifo = (struct usb_fifo *) urb->context; + struct hfcsusb *hw = fifo->hw; + static __u8 eof[8]; + + spin_lock(&hw->lock); + if (fifo->stop_gracefull) { + fifo->stop_gracefull = 0; + fifo->active = 0; + spin_unlock(&hw->lock); + return; + } + spin_unlock(&hw->lock); + + fifon = fifo->fifonum; + if ((!fifo->active) || (urb->status)) { + if (debug & DBG_HFC_URB_ERROR) + printk(KERN_DEBUG + "%s: %s: RX-Fifo %i is going down (%i)\n", + hw->name, __func__, fifon, urb->status); + + fifo->urb->interval = 0; /* cancel automatic rescheduling */ + return; + } + len = urb->actual_length; + buf = fifo->buffer; + maxlen = fifo->usb_packet_maxlen; + + /* USB data log for every D INT in */ + if ((fifon == HFCUSB_D_RX) && (debug & DBG_HFC_USB_VERBOSE)) { + printk(KERN_DEBUG "%s: %s: D RX INT len(%d) ", + hw->name, __func__, len); + for (i = 0; i < len; i++) + printk("%02x ", buf[i]); + printk("\n"); + } + + if (fifo->last_urblen != fifo->usb_packet_maxlen) { + /* the threshold mask is in the 2nd status byte */ + hw->threshold_mask = buf[1]; + + /* signal S0 layer1 state change */ + if (hw->initdone && ((buf[0] >> 4) != hw->dch.state)) { + hw->dch.state = (buf[0] >> 4); + schedule_event(&hw->dch, FLG_PHCHANGE); + } + + eof[fifon] = buf[0] & 1; + /* if we have more than the 2 status bytes -> collect data */ + if (len > 2) + hfcsusb_rx_frame(fifo, buf + 2, + urb->actual_length - 2, + (len < maxlen) ? eof[fifon] : 0); + } else { + hfcsusb_rx_frame(fifo, buf, urb->actual_length, + (len < maxlen) ? eof[fifon] : 0); + } + fifo->last_urblen = urb->actual_length; + + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) { + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: error resubmitting USB\n", + hw->name, __func__); + } +} + +/* transmit completion routine for all ISO tx fifos */ +static void +tx_iso_complete(struct urb *urb) +{ + struct iso_urb *context_iso_urb = (struct iso_urb *) urb->context; + struct usb_fifo *fifo = context_iso_urb->owner_fifo; + struct hfcsusb *hw = fifo->hw; + struct sk_buff *tx_skb; + int k, tx_offset, num_isoc_packets, sink, remain, current_len, + errcode, hdlc, i; + int *tx_idx; + int frame_complete, fifon, status; + __u8 threshbit; + + spin_lock(&hw->lock); + if (fifo->stop_gracefull) { + fifo->stop_gracefull = 0; + fifo->active = 0; + spin_unlock(&hw->lock); + return; + } + + if (fifo->dch) { + tx_skb = fifo->dch->tx_skb; + tx_idx = &fifo->dch->tx_idx; + hdlc = 1; + } else if (fifo->bch) { + tx_skb = fifo->bch->tx_skb; + tx_idx = &fifo->bch->tx_idx; + hdlc = test_bit(FLG_HDLC, &fifo->bch->Flags); + } else { + printk(KERN_DEBUG "%s: %s: neither BCH nor DCH\n", + hw->name, __func__); + spin_unlock(&hw->lock); + return; + } + + fifon = fifo->fifonum; + status = urb->status; + + tx_offset = 0; + + /* + * ISO transfer only partially completed, + * look at individual frame status for details + */ + if (status == -EXDEV) { + if (debug & DBG_HFC_URB_ERROR) + printk(KERN_DEBUG "%s: %s: " + "-EXDEV (%i) fifon (%d)\n", + hw->name, __func__, status, fifon); + + /* clear status, so go on with ISO transfers */ + status = 0; + } + + if (fifo->active && !status) { + /* is FifoFull-threshold set for our channel? */ + threshbit = (hw->threshold_mask & (1 << fifon)); + num_isoc_packets = iso_packets[fifon]; + + /* predict dataflow to avoid fifo overflow */ + if (fifon >= HFCUSB_D_TX) + sink = (threshbit) ? SINK_DMIN : SINK_DMAX; + else + sink = (threshbit) ? SINK_MIN : SINK_MAX; + fill_isoc_urb(urb, fifo->hw->dev, fifo->pipe, + context_iso_urb->buffer, num_isoc_packets, + fifo->usb_packet_maxlen, fifo->intervall, + (usb_complete_t)tx_iso_complete, urb->context); + memset(context_iso_urb->buffer, 0, + sizeof(context_iso_urb->buffer)); + frame_complete = 0; + + for (k = 0; k < num_isoc_packets; ++k) { + /* analyze tx success of previous ISO packets */ + if (debug & DBG_HFC_URB_ERROR) { + errcode = urb->iso_frame_desc[k].status; + if (errcode) { + printk(KERN_DEBUG "%s: %s: " + "ISO packet %i, status: %i\n", + hw->name, __func__, k, errcode); + } + } + + /* Generate next ISO Packets */ + if (tx_skb) + remain = tx_skb->len - *tx_idx; + else + remain = 0; + + if (remain > 0) { + fifo->bit_line -= sink; + current_len = (0 - fifo->bit_line) / 8; + if (current_len > 14) + current_len = 14; + if (current_len < 0) + current_len = 0; + if (remain < current_len) + current_len = remain; + + /* how much bit do we put on the line? */ + fifo->bit_line += current_len * 8; + + context_iso_urb->buffer[tx_offset] = 0; + if (current_len == remain) { + if (hdlc) { + /* signal frame completion */ + context_iso_urb-> + buffer[tx_offset] = 1; + /* add 2 byte flags and 16bit + * CRC at end of ISDN frame */ + fifo->bit_line += 32; + } + frame_complete = 1; + } + + /* copy tx data to iso-urb buffer */ + memcpy(context_iso_urb->buffer + tx_offset + 1, + (tx_skb->data + *tx_idx), current_len); + *tx_idx += current_len; + + urb->iso_frame_desc[k].offset = tx_offset; + urb->iso_frame_desc[k].length = current_len + 1; + + /* USB data log for every D ISO out */ + if ((fifon == HFCUSB_D_RX) && + (debug & DBG_HFC_USB_VERBOSE)) { + printk(KERN_DEBUG + "%s: %s (%d/%d) offs(%d) len(%d) ", + hw->name, __func__, + k, num_isoc_packets-1, + urb->iso_frame_desc[k].offset, + urb->iso_frame_desc[k].length); + + for (i = urb->iso_frame_desc[k].offset; + i < (urb->iso_frame_desc[k].offset + + urb->iso_frame_desc[k].length); + i++) + printk("%x ", + context_iso_urb->buffer[i]); + + printk(" skb->len(%i) tx-idx(%d)\n", + tx_skb->len, *tx_idx); + } + + tx_offset += (current_len + 1); + } else { + urb->iso_frame_desc[k].offset = tx_offset++; + urb->iso_frame_desc[k].length = 1; + /* we lower data margin every msec */ + fifo->bit_line -= sink; + if (fifo->bit_line < BITLINE_INF) + fifo->bit_line = BITLINE_INF; + } + + if (frame_complete) { + frame_complete = 0; + + if (debug & DBG_HFC_FIFO_VERBOSE) { + printk(KERN_DEBUG "%s: %s: " + "fifon(%i) new TX len(%i): ", + hw->name, __func__, + fifon, tx_skb->len); + i = 0; + while (i < tx_skb->len) + printk("%02x ", + tx_skb->data[i++]); + printk("\n"); + } + + dev_kfree_skb(tx_skb); + tx_skb = NULL; + if (fifo->dch && get_next_dframe(fifo->dch)) + tx_skb = fifo->dch->tx_skb; + else if (fifo->bch && + get_next_bframe(fifo->bch)) { + if (test_bit(FLG_TRANSPARENT, + &fifo->bch->Flags)) + confirm_Bsend(fifo->bch); + tx_skb = fifo->bch->tx_skb; + } + } + } + errcode = usb_submit_urb(urb, GFP_ATOMIC); + if (errcode < 0) { + if (debug & DEBUG_HW) + printk(KERN_DEBUG + "%s: %s: error submitting ISO URB: %d \n", + hw->name, __func__, errcode); + } + + /* + * abuse DChannel tx iso completion to trigger NT mode state + * changes tx_iso_complete is assumed to be called every + * fifo->intervall (ms) + */ + if ((fifon == HFCUSB_D_TX) && (hw->protocol == ISDN_P_NT_S0) + && (hw->timers & NT_ACTIVATION_TIMER)) { + if ((--hw->nt_timer) < 0) + schedule_event(&hw->dch, FLG_PHCHANGE); + } + + } else { + if (status && (debug & DBG_HFC_URB_ERROR)) + printk(KERN_DEBUG "%s: %s: urb->status %s (%i)" + "fifonum=%d\n", + hw->name, __func__, + symbolic(urb_errlist, status), status, fifon); + } + spin_unlock(&hw->lock); +} + +/* + * allocs urbs and start isoc transfer with two pending urbs to avoid + * gaps in the transfer chain + */ +static int +start_isoc_chain(struct usb_fifo *fifo, int num_packets_per_urb, + usb_complete_t complete, int packet_size) +{ + struct hfcsusb *hw = fifo->hw; + int i, k, errcode; + + if (debug) + printk(KERN_DEBUG "%s: %s: fifo %i\n", + hw->name, __func__, fifo->fifonum); + + /* allocate Memory for Iso out Urbs */ + for (i = 0; i < 2; i++) { + if (!(fifo->iso[i].urb)) { + fifo->iso[i].urb = + usb_alloc_urb(num_packets_per_urb, GFP_KERNEL); + if (!(fifo->iso[i].urb)) { + printk(KERN_DEBUG + "%s: %s: alloc urb for fifo %i failed", + hw->name, __func__, fifo->fifonum); + } + fifo->iso[i].owner_fifo = (struct usb_fifo *) fifo; + fifo->iso[i].indx = i; + + /* Init the first iso */ + if (ISO_BUFFER_SIZE >= + (fifo->usb_packet_maxlen * + num_packets_per_urb)) { + fill_isoc_urb(fifo->iso[i].urb, + fifo->hw->dev, fifo->pipe, + fifo->iso[i].buffer, + num_packets_per_urb, + fifo->usb_packet_maxlen, + fifo->intervall, complete, + &fifo->iso[i]); + memset(fifo->iso[i].buffer, 0, + sizeof(fifo->iso[i].buffer)); + + for (k = 0; k < num_packets_per_urb; k++) { + fifo->iso[i].urb-> + iso_frame_desc[k].offset = + k * packet_size; + fifo->iso[i].urb-> + iso_frame_desc[k].length = + packet_size; + } + } else { + printk(KERN_DEBUG + "%s: %s: ISO Buffer size to small!\n", + hw->name, __func__); + } + } + fifo->bit_line = BITLINE_INF; + + errcode = usb_submit_urb(fifo->iso[i].urb, GFP_KERNEL); + fifo->active = (errcode >= 0) ? 1 : 0; + fifo->stop_gracefull = 0; + if (errcode < 0) { + printk(KERN_DEBUG "%s: %s: %s URB nr:%d\n", + hw->name, __func__, + symbolic(urb_errlist, errcode), i); + } + } + return fifo->active; +} + +static void +stop_iso_gracefull(struct usb_fifo *fifo) +{ + struct hfcsusb *hw = fifo->hw; + int i, timeout; + u_long flags; + + for (i = 0; i < 2; i++) { + spin_lock_irqsave(&hw->lock, flags); + if (debug) + printk(KERN_DEBUG "%s: %s for fifo %i.%i\n", + hw->name, __func__, fifo->fifonum, i); + fifo->stop_gracefull = 1; + spin_unlock_irqrestore(&hw->lock, flags); + } + + for (i = 0; i < 2; i++) { + timeout = 3; + while (fifo->stop_gracefull && timeout--) + schedule_timeout_interruptible((HZ/1000)*16); + if (debug && fifo->stop_gracefull) + printk(KERN_DEBUG "%s: ERROR %s for fifo %i.%i\n", + hw->name, __func__, fifo->fifonum, i); + } +} + +static void +stop_int_gracefull(struct usb_fifo *fifo) +{ + struct hfcsusb *hw = fifo->hw; + int timeout; + u_long flags; + + spin_lock_irqsave(&hw->lock, flags); + if (debug) + printk(KERN_DEBUG "%s: %s for fifo %i\n", + hw->name, __func__, fifo->fifonum); + fifo->stop_gracefull = 1; + spin_unlock_irqrestore(&hw->lock, flags); + + timeout = 3; + while (fifo->stop_gracefull && timeout--) + schedule_timeout_interruptible((HZ/1000)*3); + if (debug && fifo->stop_gracefull) + printk(KERN_DEBUG "%s: ERROR %s for fifo %i\n", + hw->name, __func__, fifo->fifonum); +} + +/* start the interrupt transfer for the given fifo */ +static void +start_int_fifo(struct usb_fifo *fifo) +{ + struct hfcsusb *hw = fifo->hw; + int errcode; + + if (debug) + printk(KERN_DEBUG "%s: %s: INT IN fifo:%d\n", + hw->name, __func__, fifo->fifonum); + + if (!fifo->urb) { + fifo->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!fifo->urb) + return; + } + usb_fill_int_urb(fifo->urb, fifo->hw->dev, fifo->pipe, + fifo->buffer, fifo->usb_packet_maxlen, + (usb_complete_t)rx_int_complete, fifo, fifo->intervall); + fifo->active = 1; + fifo->stop_gracefull = 0; + errcode = usb_submit_urb(fifo->urb, GFP_KERNEL); + if (errcode) { + printk(KERN_DEBUG "%s: %s: submit URB: status:%i\n", + hw->name, __func__, errcode); + fifo->active = 0; + } +} + +static void +setPortMode(struct hfcsusb *hw) +{ + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s %s\n", hw->name, __func__, + (hw->protocol == ISDN_P_TE_S0) ? "TE" : "NT"); + + if (hw->protocol == ISDN_P_TE_S0) { + write_reg(hw, HFCUSB_SCTRL, 0x40); + write_reg(hw, HFCUSB_SCTRL_E, 0x00); + write_reg(hw, HFCUSB_CLKDEL, CLKDEL_TE); + write_reg(hw, HFCUSB_STATES, 3 | 0x10); + write_reg(hw, HFCUSB_STATES, 3); + } else { + write_reg(hw, HFCUSB_SCTRL, 0x44); + write_reg(hw, HFCUSB_SCTRL_E, 0x09); + write_reg(hw, HFCUSB_CLKDEL, CLKDEL_NT); + write_reg(hw, HFCUSB_STATES, 1 | 0x10); + write_reg(hw, HFCUSB_STATES, 1); + } +} + +static void +reset_hfcsusb(struct hfcsusb *hw) +{ + struct usb_fifo *fifo; + int i; + + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + /* do Chip reset */ + write_reg(hw, HFCUSB_CIRM, 8); + + /* aux = output, reset off */ + write_reg(hw, HFCUSB_CIRM, 0x10); + + /* set USB_SIZE to match the wMaxPacketSize for INT or BULK transfers */ + write_reg(hw, HFCUSB_USB_SIZE, (hw->packet_size / 8) | + ((hw->packet_size / 8) << 4)); + + /* set USB_SIZE_I to match the the wMaxPacketSize for ISO transfers */ + write_reg(hw, HFCUSB_USB_SIZE_I, hw->iso_packet_size); + + /* enable PCM/GCI master mode */ + write_reg(hw, HFCUSB_MST_MODE1, 0); /* set default values */ + write_reg(hw, HFCUSB_MST_MODE0, 1); /* enable master mode */ + + /* init the fifos */ + write_reg(hw, HFCUSB_F_THRES, + (HFCUSB_TX_THRESHOLD / 8) | ((HFCUSB_RX_THRESHOLD / 8) << 4)); + + fifo = hw->fifos; + for (i = 0; i < HFCUSB_NUM_FIFOS; i++) { + write_reg(hw, HFCUSB_FIFO, i); /* select the desired fifo */ + fifo[i].max_size = + (i <= HFCUSB_B2_RX) ? MAX_BCH_SIZE : MAX_DFRAME_LEN; + fifo[i].last_urblen = 0; + + /* set 2 bit for D- & E-channel */ + write_reg(hw, HFCUSB_HDLC_PAR, ((i <= HFCUSB_B2_RX) ? 0 : 2)); + + /* enable all fifos */ + if (i == HFCUSB_D_TX) + write_reg(hw, HFCUSB_CON_HDLC, + (hw->protocol == ISDN_P_NT_S0) ? 0x08 : 0x09); + else + write_reg(hw, HFCUSB_CON_HDLC, 0x08); + write_reg(hw, HFCUSB_INC_RES_F, 2); /* reset the fifo */ + } + + write_reg(hw, HFCUSB_SCTRL_R, 0); /* disable both B receivers */ + handle_led(hw, LED_POWER_ON); +} + +/* start USB data pipes dependand on device's endpoint configuration */ +static void +hfcsusb_start_endpoint(struct hfcsusb *hw, int channel) +{ + /* quick check if endpoint already running */ + if ((channel == HFC_CHAN_D) && (hw->fifos[HFCUSB_D_RX].active)) + return; + if ((channel == HFC_CHAN_B1) && (hw->fifos[HFCUSB_B1_RX].active)) + return; + if ((channel == HFC_CHAN_B2) && (hw->fifos[HFCUSB_B2_RX].active)) + return; + if ((channel == HFC_CHAN_E) && (hw->fifos[HFCUSB_PCM_RX].active)) + return; + + /* start rx endpoints using USB INT IN method */ + if (hw->cfg_used == CNF_3INT3ISO || hw->cfg_used == CNF_4INT3ISO) + start_int_fifo(hw->fifos + channel*2 + 1); + + /* start rx endpoints using USB ISO IN method */ + if (hw->cfg_used == CNF_3ISO3ISO || hw->cfg_used == CNF_4ISO3ISO) { + switch (channel) { + case HFC_CHAN_D: + start_isoc_chain(hw->fifos + HFCUSB_D_RX, + ISOC_PACKETS_D, + (usb_complete_t)rx_iso_complete, + 16); + break; + case HFC_CHAN_E: + start_isoc_chain(hw->fifos + HFCUSB_PCM_RX, + ISOC_PACKETS_D, + (usb_complete_t)rx_iso_complete, + 16); + break; + case HFC_CHAN_B1: + start_isoc_chain(hw->fifos + HFCUSB_B1_RX, + ISOC_PACKETS_B, + (usb_complete_t)rx_iso_complete, + 16); + break; + case HFC_CHAN_B2: + start_isoc_chain(hw->fifos + HFCUSB_B2_RX, + ISOC_PACKETS_B, + (usb_complete_t)rx_iso_complete, + 16); + break; + } + } + + /* start tx endpoints using USB ISO OUT method */ + switch (channel) { + case HFC_CHAN_D: + start_isoc_chain(hw->fifos + HFCUSB_D_TX, + ISOC_PACKETS_B, + (usb_complete_t)tx_iso_complete, 1); + break; + case HFC_CHAN_B1: + start_isoc_chain(hw->fifos + HFCUSB_B1_TX, + ISOC_PACKETS_D, + (usb_complete_t)tx_iso_complete, 1); + break; + case HFC_CHAN_B2: + start_isoc_chain(hw->fifos + HFCUSB_B2_TX, + ISOC_PACKETS_B, + (usb_complete_t)tx_iso_complete, 1); + break; + } +} + +/* stop USB data pipes dependand on device's endpoint configuration */ +static void +hfcsusb_stop_endpoint(struct hfcsusb *hw, int channel) +{ + /* quick check if endpoint currently running */ + if ((channel == HFC_CHAN_D) && (!hw->fifos[HFCUSB_D_RX].active)) + return; + if ((channel == HFC_CHAN_B1) && (!hw->fifos[HFCUSB_B1_RX].active)) + return; + if ((channel == HFC_CHAN_B2) && (!hw->fifos[HFCUSB_B2_RX].active)) + return; + if ((channel == HFC_CHAN_E) && (!hw->fifos[HFCUSB_PCM_RX].active)) + return; + + /* rx endpoints using USB INT IN method */ + if (hw->cfg_used == CNF_3INT3ISO || hw->cfg_used == CNF_4INT3ISO) + stop_int_gracefull(hw->fifos + channel*2 + 1); + + /* rx endpoints using USB ISO IN method */ + if (hw->cfg_used == CNF_3ISO3ISO || hw->cfg_used == CNF_4ISO3ISO) + stop_iso_gracefull(hw->fifos + channel*2 + 1); + + /* tx endpoints using USB ISO OUT method */ + if (channel != HFC_CHAN_E) + stop_iso_gracefull(hw->fifos + channel*2); +} + + +/* Hardware Initialization */ +int +setup_hfcsusb(struct hfcsusb *hw) +{ + int err; + u_char b; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + /* check the chip id */ + if (read_reg_atomic(hw, HFCUSB_CHIP_ID, &b) != 1) { + printk(KERN_DEBUG "%s: %s: cannot read chip id\n", + hw->name, __func__); + return 1; + } + if (b != HFCUSB_CHIPID) { + printk(KERN_DEBUG "%s: %s: Invalid chip id 0x%02x\n", + hw->name, __func__, b); + return 1; + } + + /* first set the needed config, interface and alternate */ + err = usb_set_interface(hw->dev, hw->if_used, hw->alt_used); + + hw->led_state = 0; + + /* init the background machinery for control requests */ + hw->ctrl_read.bRequestType = 0xc0; + hw->ctrl_read.bRequest = 1; + hw->ctrl_read.wLength = cpu_to_le16(1); + hw->ctrl_write.bRequestType = 0x40; + hw->ctrl_write.bRequest = 0; + hw->ctrl_write.wLength = 0; + usb_fill_control_urb(hw->ctrl_urb, hw->dev, hw->ctrl_out_pipe, + (u_char *)&hw->ctrl_write, NULL, 0, + (usb_complete_t)ctrl_complete, hw); + + reset_hfcsusb(hw); + return 0; +} + +static void +release_hw(struct hfcsusb *hw) +{ + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + /* + * stop all endpoints gracefully + * TODO: mISDN_core should generate CLOSE_CHANNEL + * signals after calling mISDN_unregister_device() + */ + hfcsusb_stop_endpoint(hw, HFC_CHAN_D); + hfcsusb_stop_endpoint(hw, HFC_CHAN_B1); + hfcsusb_stop_endpoint(hw, HFC_CHAN_B2); + if (hw->fifos[HFCUSB_PCM_RX].pipe) + hfcsusb_stop_endpoint(hw, HFC_CHAN_E); + if (hw->protocol == ISDN_P_TE_S0) + l1_event(hw->dch.l1, CLOSE_CHANNEL); + + mISDN_unregister_device(&hw->dch.dev); + mISDN_freebchannel(&hw->bch[1]); + mISDN_freebchannel(&hw->bch[0]); + mISDN_freedchannel(&hw->dch); + + if (hw->ctrl_urb) { + usb_kill_urb(hw->ctrl_urb); + usb_free_urb(hw->ctrl_urb); + hw->ctrl_urb = NULL; + } + + if (hw->intf) + usb_set_intfdata(hw->intf, NULL); + list_del(&hw->list); + kfree(hw); + hw = NULL; +} + +static void +deactivate_bchannel(struct bchannel *bch) +{ + struct hfcsusb *hw = bch->hw; + u_long flags; + + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: bch->nr(%i)\n", + hw->name, __func__, bch->nr); + + spin_lock_irqsave(&hw->lock, flags); + if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) { + dev_kfree_skb(bch->next_skb); + bch->next_skb = NULL; + } + if (bch->tx_skb) { + dev_kfree_skb(bch->tx_skb); + bch->tx_skb = NULL; + } + bch->tx_idx = 0; + if (bch->rx_skb) { + dev_kfree_skb(bch->rx_skb); + bch->rx_skb = NULL; + } + clear_bit(FLG_ACTIVE, &bch->Flags); + clear_bit(FLG_TX_BUSY, &bch->Flags); + spin_unlock_irqrestore(&hw->lock, flags); + hfcsusb_setup_bch(bch, ISDN_P_NONE); + hfcsusb_stop_endpoint(hw, bch->nr); +} + +/* + * Layer 1 B-channel hardware access + */ +static int +hfc_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + int ret = -EINVAL; + + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", __func__, cmd, arg); + + switch (cmd) { + case HW_TESTRX_RAW: + case HW_TESTRX_HDLC: + case HW_TESTRX_OFF: + ret = -EINVAL; + break; + + case CLOSE_CHANNEL: + test_and_clear_bit(FLG_OPEN, &bch->Flags); + if (test_bit(FLG_ACTIVE, &bch->Flags)) + deactivate_bchannel(bch); + ch->protocol = ISDN_P_NONE; + ch->peer = NULL; + module_put(THIS_MODULE); + ret = 0; + break; + case CONTROL_CHANNEL: + ret = channel_bctrl(bch, arg); + break; + default: + printk(KERN_WARNING "%s: unknown prim(%x)\n", + __func__, cmd); + } + return ret; +} + +static int +setup_instance(struct hfcsusb *hw, struct device *parent) +{ + u_long flags; + int err, i; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + spin_lock_init(&hw->ctrl_lock); + spin_lock_init(&hw->lock); + + mISDN_initdchannel(&hw->dch, MAX_DFRAME_LEN_L1, ph_state); + hw->dch.debug = debug & 0xFFFF; + hw->dch.hw = hw; + hw->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); + hw->dch.dev.D.send = hfcusb_l2l1D; + hw->dch.dev.D.ctrl = hfc_dctrl; + + /* enable E-Channel logging */ + if (hw->fifos[HFCUSB_PCM_RX].pipe) + mISDN_initdchannel(&hw->ech, MAX_DFRAME_LEN_L1, NULL); + + hw->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + hw->dch.dev.nrbchan = 2; + for (i = 0; i < 2; i++) { + hw->bch[i].nr = i + 1; + set_channelmap(i + 1, hw->dch.dev.channelmap); + hw->bch[i].debug = debug; + mISDN_initbchannel(&hw->bch[i], MAX_DATA_MEM); + hw->bch[i].hw = hw; + hw->bch[i].ch.send = hfcusb_l2l1B; + hw->bch[i].ch.ctrl = hfc_bctrl; + hw->bch[i].ch.nr = i + 1; + list_add(&hw->bch[i].ch.list, &hw->dch.dev.bchannels); + } + + hw->fifos[HFCUSB_B1_TX].bch = &hw->bch[0]; + hw->fifos[HFCUSB_B1_RX].bch = &hw->bch[0]; + hw->fifos[HFCUSB_B2_TX].bch = &hw->bch[1]; + hw->fifos[HFCUSB_B2_RX].bch = &hw->bch[1]; + hw->fifos[HFCUSB_D_TX].dch = &hw->dch; + hw->fifos[HFCUSB_D_RX].dch = &hw->dch; + hw->fifos[HFCUSB_PCM_RX].ech = &hw->ech; + hw->fifos[HFCUSB_PCM_TX].ech = &hw->ech; + + err = setup_hfcsusb(hw); + if (err) + goto out; + + snprintf(hw->name, MISDN_MAX_IDLEN - 1, "%s.%d", DRIVER_NAME, + hfcsusb_cnt + 1); + printk(KERN_INFO "%s: registered as '%s'\n", + DRIVER_NAME, hw->name); + + err = mISDN_register_device(&hw->dch.dev, parent, hw->name); + if (err) + goto out; + + hfcsusb_cnt++; + write_lock_irqsave(&HFClock, flags); + list_add_tail(&hw->list, &HFClist); + write_unlock_irqrestore(&HFClock, flags); + return 0; + +out: + mISDN_freebchannel(&hw->bch[1]); + mISDN_freebchannel(&hw->bch[0]); + mISDN_freedchannel(&hw->dch); + kfree(hw); + return err; +} + +static int +hfcsusb_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct hfcsusb *hw; + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_host_interface *iface = intf->cur_altsetting; + struct usb_host_interface *iface_used = NULL; + struct usb_host_endpoint *ep; + struct hfcsusb_vdata *driver_info; + int ifnum = iface->desc.bInterfaceNumber, i, idx, alt_idx, + probe_alt_setting, vend_idx, cfg_used, *vcf, attr, cfg_found, + ep_addr, cmptbl[16], small_match, iso_packet_size, packet_size, + alt_used = 0; + + vend_idx = 0xffff; + for (i = 0; hfcsusb_idtab[i].idVendor; i++) { + if ((le16_to_cpu(dev->descriptor.idVendor) + == hfcsusb_idtab[i].idVendor) && + (le16_to_cpu(dev->descriptor.idProduct) + == hfcsusb_idtab[i].idProduct)) { + vend_idx = i; + continue; + } + } + + printk(KERN_DEBUG + "%s: interface(%d) actalt(%d) minor(%d) vend_idx(%d)\n", + __func__, ifnum, iface->desc.bAlternateSetting, + intf->minor, vend_idx); + + if (vend_idx == 0xffff) { + printk(KERN_WARNING + "%s: no valid vendor found in USB descriptor\n", + __func__); + return -EIO; + } + /* if vendor and product ID is OK, start probing alternate settings */ + alt_idx = 0; + small_match = -1; + + /* default settings */ + iso_packet_size = 16; + packet_size = 64; + + while (alt_idx < intf->num_altsetting) { + iface = intf->altsetting + alt_idx; + probe_alt_setting = iface->desc.bAlternateSetting; + cfg_used = 0; + + while (validconf[cfg_used][0]) { + cfg_found = 1; + vcf = validconf[cfg_used]; + ep = iface->endpoint; + memcpy(cmptbl, vcf, 16 * sizeof(int)); + + /* check for all endpoints in this alternate setting */ + for (i = 0; i < iface->desc.bNumEndpoints; i++) { + ep_addr = ep->desc.bEndpointAddress; + + /* get endpoint base */ + idx = ((ep_addr & 0x7f) - 1) * 2; + if (ep_addr & 0x80) + idx++; + attr = ep->desc.bmAttributes; + + if (cmptbl[idx] != EP_NOP) { + if (cmptbl[idx] == EP_NUL) + cfg_found = 0; + if (attr == USB_ENDPOINT_XFER_INT + && cmptbl[idx] == EP_INT) + cmptbl[idx] = EP_NUL; + if (attr == USB_ENDPOINT_XFER_BULK + && cmptbl[idx] == EP_BLK) + cmptbl[idx] = EP_NUL; + if (attr == USB_ENDPOINT_XFER_ISOC + && cmptbl[idx] == EP_ISO) + cmptbl[idx] = EP_NUL; + + if (attr == USB_ENDPOINT_XFER_INT && + ep->desc.bInterval < vcf[17]) { + cfg_found = 0; + } + } + ep++; + } + + for (i = 0; i < 16; i++) + if (cmptbl[i] != EP_NOP && cmptbl[i] != EP_NUL) + cfg_found = 0; + + if (cfg_found) { + if (small_match < cfg_used) { + small_match = cfg_used; + alt_used = probe_alt_setting; + iface_used = iface; + } + } + cfg_used++; + } + alt_idx++; + } /* (alt_idx < intf->num_altsetting) */ + + /* not found a valid USB Ta Endpoint config */ + if (small_match == -1) + return -EIO; + + iface = iface_used; + hw = kzalloc(sizeof(struct hfcsusb), GFP_KERNEL); + if (!hw) + return -ENOMEM; /* got no mem */ + snprintf(hw->name, MISDN_MAX_IDLEN - 1, "%s", DRIVER_NAME); + + ep = iface->endpoint; + vcf = validconf[small_match]; + + for (i = 0; i < iface->desc.bNumEndpoints; i++) { + struct usb_fifo *f; + + ep_addr = ep->desc.bEndpointAddress; + /* get endpoint base */ + idx = ((ep_addr & 0x7f) - 1) * 2; + if (ep_addr & 0x80) + idx++; + f = &hw->fifos[idx & 7]; + + /* init Endpoints */ + if (vcf[idx] == EP_NOP || vcf[idx] == EP_NUL) { + ep++; + continue; + } + switch (ep->desc.bmAttributes) { + case USB_ENDPOINT_XFER_INT: + f->pipe = usb_rcvintpipe(dev, + ep->desc.bEndpointAddress); + f->usb_transfer_mode = USB_INT; + packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); + break; + case USB_ENDPOINT_XFER_BULK: + if (ep_addr & 0x80) + f->pipe = usb_rcvbulkpipe(dev, + ep->desc.bEndpointAddress); + else + f->pipe = usb_sndbulkpipe(dev, + ep->desc.bEndpointAddress); + f->usb_transfer_mode = USB_BULK; + packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); + break; + case USB_ENDPOINT_XFER_ISOC: + if (ep_addr & 0x80) + f->pipe = usb_rcvisocpipe(dev, + ep->desc.bEndpointAddress); + else + f->pipe = usb_sndisocpipe(dev, + ep->desc.bEndpointAddress); + f->usb_transfer_mode = USB_ISOC; + iso_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); + break; + default: + f->pipe = 0; + } + + if (f->pipe) { + f->fifonum = idx & 7; + f->hw = hw; + f->usb_packet_maxlen = + le16_to_cpu(ep->desc.wMaxPacketSize); + f->intervall = ep->desc.bInterval; + } + ep++; + } + hw->dev = dev; /* save device */ + hw->if_used = ifnum; /* save used interface */ + hw->alt_used = alt_used; /* and alternate config */ + hw->ctrl_paksize = dev->descriptor.bMaxPacketSize0; /* control size */ + hw->cfg_used = vcf[16]; /* store used config */ + hw->vend_idx = vend_idx; /* store found vendor */ + hw->packet_size = packet_size; + hw->iso_packet_size = iso_packet_size; + + /* create the control pipes needed for register access */ + hw->ctrl_in_pipe = usb_rcvctrlpipe(hw->dev, 0); + hw->ctrl_out_pipe = usb_sndctrlpipe(hw->dev, 0); + hw->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); + + driver_info = + (struct hfcsusb_vdata *)hfcsusb_idtab[vend_idx].driver_info; + printk(KERN_DEBUG "%s: %s: detected \"%s\" (%s, if=%d alt=%d)\n", + hw->name, __func__, driver_info->vend_name, + conf_str[small_match], ifnum, alt_used); + + if (setup_instance(hw, dev->dev.parent)) + return -EIO; + + hw->intf = intf; + usb_set_intfdata(hw->intf, hw); + return 0; +} + +/* function called when an active device is removed */ +static void +hfcsusb_disconnect(struct usb_interface *intf) +{ + struct hfcsusb *hw = usb_get_intfdata(intf); + struct hfcsusb *next; + int cnt = 0; + + printk(KERN_INFO "%s: device disconnected\n", hw->name); + + handle_led(hw, LED_POWER_OFF); + release_hw(hw); + + list_for_each_entry_safe(hw, next, &HFClist, list) + cnt++; + if (!cnt) + hfcsusb_cnt = 0; + + usb_set_intfdata(intf, NULL); +} + +static struct usb_driver hfcsusb_drv = { + .name = DRIVER_NAME, + .id_table = hfcsusb_idtab, + .probe = hfcsusb_probe, + .disconnect = hfcsusb_disconnect, +}; + +static int __init +hfcsusb_init(void) +{ + printk(KERN_INFO DRIVER_NAME " driver Rev. %s debug(0x%x) poll(%i)\n", + hfcsusb_rev, debug, poll); + + if (usb_register(&hfcsusb_drv)) { + printk(KERN_INFO DRIVER_NAME + ": Unable to register hfcsusb module at usb stack\n"); + return -ENODEV; + } + + return 0; +} + +static void __exit +hfcsusb_cleanup(void) +{ + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_INFO DRIVER_NAME ": %s\n", __func__); + + /* unregister Hardware */ + usb_deregister(&hfcsusb_drv); /* release our driver */ +} + +module_init(hfcsusb_init); +module_exit(hfcsusb_cleanup); diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.h b/drivers/isdn/hardware/mISDN/hfcsusb.h new file mode 100644 index 00000000000..098486b8e8d --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfcsusb.h @@ -0,0 +1,418 @@ +/* + * hfcsusb.h, HFC-S USB mISDN driver + */ + +#ifndef __HFCSUSB_H__ +#define __HFCSUSB_H__ + + +#define DRIVER_NAME "HFC-S_USB" + +#define DBG_HFC_CALL_TRACE 0x00010000 +#define DBG_HFC_FIFO_VERBOSE 0x00020000 +#define DBG_HFC_USB_VERBOSE 0x00100000 +#define DBG_HFC_URB_INFO 0x00200000 +#define DBG_HFC_URB_ERROR 0x00400000 + +#define DEFAULT_TRANSP_BURST_SZ 128 + +#define HFC_CTRL_TIMEOUT 20 /* 5ms timeout writing/reading regs */ +#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ +#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */ + +/* hfcsusb Layer1 commands */ +#define HFC_L1_ACTIVATE_TE 1 +#define HFC_L1_ACTIVATE_NT 2 +#define HFC_L1_DEACTIVATE_NT 3 +#define HFC_L1_FORCE_DEACTIVATE_TE 4 + +/* cmd FLAGS in HFCUSB_STATES register */ +#define HFCUSB_LOAD_STATE 0x10 +#define HFCUSB_ACTIVATE 0x20 +#define HFCUSB_DO_ACTION 0x40 +#define HFCUSB_NT_G2_G3 0x80 + +/* timers */ +#define NT_ACTIVATION_TIMER 0x01 /* enables NT mode activation Timer */ +#define NT_T1_COUNT 10 + +#define MAX_BCH_SIZE 2048 /* allowed B-channel packet size */ + +#define HFCUSB_RX_THRESHOLD 64 /* threshold for fifo report bit rx */ +#define HFCUSB_TX_THRESHOLD 96 /* threshold for fifo report bit tx */ + +#define HFCUSB_CHIP_ID 0x16 /* Chip ID register index */ +#define HFCUSB_CIRM 0x00 /* cirm register index */ +#define HFCUSB_USB_SIZE 0x07 /* int length register */ +#define HFCUSB_USB_SIZE_I 0x06 /* iso length register */ +#define HFCUSB_F_CROSS 0x0b /* bit order register */ +#define HFCUSB_CLKDEL 0x37 /* bit delay register */ +#define HFCUSB_CON_HDLC 0xfa /* channel connect register */ +#define HFCUSB_HDLC_PAR 0xfb +#define HFCUSB_SCTRL 0x31 /* S-bus control register (tx) */ +#define HFCUSB_SCTRL_E 0x32 /* same for E and special funcs */ +#define HFCUSB_SCTRL_R 0x33 /* S-bus control register (rx) */ +#define HFCUSB_F_THRES 0x0c /* threshold register */ +#define HFCUSB_FIFO 0x0f /* fifo select register */ +#define HFCUSB_F_USAGE 0x1a /* fifo usage register */ +#define HFCUSB_MST_MODE0 0x14 +#define HFCUSB_MST_MODE1 0x15 +#define HFCUSB_P_DATA 0x1f +#define HFCUSB_INC_RES_F 0x0e +#define HFCUSB_B1_SSL 0x20 +#define HFCUSB_B2_SSL 0x21 +#define HFCUSB_B1_RSL 0x24 +#define HFCUSB_B2_RSL 0x25 +#define HFCUSB_STATES 0x30 + + +#define HFCUSB_CHIPID 0x40 /* ID value of HFC-S USB */ + +/* fifo registers */ +#define HFCUSB_NUM_FIFOS 8 /* maximum number of fifos */ +#define HFCUSB_B1_TX 0 /* index for B1 transmit bulk/int */ +#define HFCUSB_B1_RX 1 /* index for B1 receive bulk/int */ +#define HFCUSB_B2_TX 2 +#define HFCUSB_B2_RX 3 +#define HFCUSB_D_TX 4 +#define HFCUSB_D_RX 5 +#define HFCUSB_PCM_TX 6 +#define HFCUSB_PCM_RX 7 + + +#define USB_INT 0 +#define USB_BULK 1 +#define USB_ISOC 2 + +#define ISOC_PACKETS_D 8 +#define ISOC_PACKETS_B 8 +#define ISO_BUFFER_SIZE 128 + +/* defines how much ISO packets are handled in one URB */ +static int iso_packets[8] = + { ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B, + ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D +}; + + +/* Fifo flow Control for TX ISO */ +#define SINK_MAX 68 +#define SINK_MIN 48 +#define SINK_DMIN 12 +#define SINK_DMAX 18 +#define BITLINE_INF (-96*8) + +/* HFC-S USB register access by Control-URSs */ +#define write_reg_atomic(a, b, c) \ + usb_control_msg((a)->dev, (a)->ctrl_out_pipe, 0, 0x40, (c), (b), \ + 0, 0, HFC_CTRL_TIMEOUT) +#define read_reg_atomic(a, b, c) \ + usb_control_msg((a)->dev, (a)->ctrl_in_pipe, 1, 0xC0, 0, (b), (c), \ + 1, HFC_CTRL_TIMEOUT) +#define HFC_CTRL_BUFSIZE 64 + +struct ctrl_buf { + __u8 hfcs_reg; /* register number */ + __u8 reg_val; /* value to be written (or read) */ +}; + +/* + * URB error codes + * Used to represent a list of values and their respective symbolic names + */ +struct hfcusb_symbolic_list { + const int num; + const char *name; +}; + +static struct hfcusb_symbolic_list urb_errlist[] = { + {-ENOMEM, "No memory for allocation of internal structures"}, + {-ENOSPC, "The host controller's bandwidth is already consumed"}, + {-ENOENT, "URB was canceled by unlink_urb"}, + {-EXDEV, "ISO transfer only partially completed"}, + {-EAGAIN, "Too match scheduled for the future"}, + {-ENXIO, "URB already queued"}, + {-EFBIG, "Too much ISO frames requested"}, + {-ENOSR, "Buffer error (overrun)"}, + {-EPIPE, "Specified endpoint is stalled (device not responding)"}, + {-EOVERFLOW, "Babble (bad cable?)"}, + {-EPROTO, "Bit-stuff error (bad cable?)"}, + {-EILSEQ, "CRC/Timeout"}, + {-ETIMEDOUT, "NAK (device does not respond)"}, + {-ESHUTDOWN, "Device unplugged"}, + {-1, NULL} +}; + +static inline const char * +symbolic(struct hfcusb_symbolic_list list[], const int num) +{ + int i; + for (i = 0; list[i].name != NULL; i++) + if (list[i].num == num) + return list[i].name; + return ""; +} + +/* USB descriptor need to contain one of the following EndPoint combination: */ +#define CNF_4INT3ISO 1 /* 4 INT IN, 3 ISO OUT */ +#define CNF_3INT3ISO 2 /* 3 INT IN, 3 ISO OUT */ +#define CNF_4ISO3ISO 3 /* 4 ISO IN, 3 ISO OUT */ +#define CNF_3ISO3ISO 4 /* 3 ISO IN, 3 ISO OUT */ + +#define EP_NUL 1 /* Endpoint at this position not allowed */ +#define EP_NOP 2 /* all type of endpoints allowed at this position */ +#define EP_ISO 3 /* Isochron endpoint mandatory at this position */ +#define EP_BLK 4 /* Bulk endpoint mandatory at this position */ +#define EP_INT 5 /* Interrupt endpoint mandatory at this position */ + +#define HFC_CHAN_B1 0 +#define HFC_CHAN_B2 1 +#define HFC_CHAN_D 2 +#define HFC_CHAN_E 3 + + +/* + * List of all supported enpoints configiration sets, used to find the + * best matching endpoint configuration within a devices' USB descriptor. + * We need at least 3 RX endpoints, and 3 TX endpoints, either + * INT-in and ISO-out, or ISO-in and ISO-out) + * with 4 RX endpoints even E-Channel logging is possible + */ +static int +validconf[][19] = { + /* INT in, ISO out config */ + {EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NOP, EP_INT, + EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL, + CNF_4INT3ISO, 2, 1}, + {EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_NUL, + EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL, + CNF_3INT3ISO, 2, 0}, + /* ISO in, ISO out config */ + {EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, + EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NOP, EP_ISO, + CNF_4ISO3ISO, 2, 1}, + {EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, + EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NUL, EP_NUL, + CNF_3ISO3ISO, 2, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* EOL element */ +}; + +/* string description of chosen config */ +char *conf_str[] = { + "4 Interrupt IN + 3 Isochron OUT", + "3 Interrupt IN + 3 Isochron OUT", + "4 Isochron IN + 3 Isochron OUT", + "3 Isochron IN + 3 Isochron OUT" +}; + + +#define LED_OFF 0 /* no LED support */ +#define LED_SCHEME1 1 /* LED standard scheme */ +#define LED_SCHEME2 2 /* not used yet... */ + +#define LED_POWER_ON 1 +#define LED_POWER_OFF 2 +#define LED_S0_ON 3 +#define LED_S0_OFF 4 +#define LED_B1_ON 5 +#define LED_B1_OFF 6 +#define LED_B1_DATA 7 +#define LED_B2_ON 8 +#define LED_B2_OFF 9 +#define LED_B2_DATA 10 + +#define LED_NORMAL 0 /* LEDs are normal */ +#define LED_INVERTED 1 /* LEDs are inverted */ + +/* time in ms to perform a Flashing LED when B-Channel has traffic */ +#define LED_TIME 250 + + + +struct hfcsusb; +struct usb_fifo; + +/* structure defining input+output fifos (interrupt/bulk mode) */ +struct iso_urb { + struct urb *urb; + __u8 buffer[ISO_BUFFER_SIZE]; /* buffer rx/tx USB URB data */ + struct usb_fifo *owner_fifo; /* pointer to owner fifo */ + __u8 indx; /* Fifos's ISO double buffer 0 or 1 ? */ +#ifdef ISO_FRAME_START_DEBUG + int start_frames[ISO_FRAME_START_RING_COUNT]; + __u8 iso_frm_strt_pos; /* index in start_frame[] */ +#endif +}; + +struct usb_fifo { + int fifonum; /* fifo index attached to this structure */ + int active; /* fifo is currently active */ + struct hfcsusb *hw; /* pointer to main structure */ + int pipe; /* address of endpoint */ + __u8 usb_packet_maxlen; /* maximum length for usb transfer */ + unsigned int max_size; /* maximum size of receive/send packet */ + __u8 intervall; /* interrupt interval */ + struct urb *urb; /* transfer structure for usb routines */ + __u8 buffer[128]; /* buffer USB INT OUT URB data */ + int bit_line; /* how much bits are in the fifo? */ + + __u8 usb_transfer_mode; /* switched between ISO and INT */ + struct iso_urb iso[2]; /* two urbs to have one always + one pending */ + + struct dchannel *dch; /* link to hfcsusb_t->dch */ + struct bchannel *bch; /* link to hfcsusb_t->bch */ + struct dchannel *ech; /* link to hfcsusb_t->ech, TODO: E-CHANNEL */ + int last_urblen; /* remember length of last packet */ + __u8 stop_gracefull; /* stops URB retransmission */ +}; + +struct hfcsusb { + struct list_head list; + struct dchannel dch; + struct bchannel bch[2]; + struct dchannel ech; /* TODO : wait for struct echannel ;) */ + + struct usb_device *dev; /* our device */ + struct usb_interface *intf; /* used interface */ + int if_used; /* used interface number */ + int alt_used; /* used alternate config */ + int cfg_used; /* configuration index used */ + int vend_idx; /* index in hfcsusb_idtab */ + int packet_size; + int iso_packet_size; + struct usb_fifo fifos[HFCUSB_NUM_FIFOS]; + + /* control pipe background handling */ + struct ctrl_buf ctrl_buff[HFC_CTRL_BUFSIZE]; + int ctrl_in_idx, ctrl_out_idx, ctrl_cnt; + struct urb *ctrl_urb; + struct usb_ctrlrequest ctrl_write; + struct usb_ctrlrequest ctrl_read; + int ctrl_paksize; + int ctrl_in_pipe, ctrl_out_pipe; + spinlock_t ctrl_lock; /* lock for ctrl */ + spinlock_t lock; + + __u8 threshold_mask; + __u8 led_state; + + __u8 protocol; + int nt_timer; + int open; + __u8 timers; + __u8 initdone; + char name[MISDN_MAX_IDLEN]; +}; + +/* private vendor specific data */ +struct hfcsusb_vdata { + __u8 led_scheme; /* led display scheme */ + signed short led_bits[8]; /* array of 8 possible LED bitmask */ + char *vend_name; /* device name */ +}; + + +#define HFC_MAX_TE_LAYER1_STATE 8 +#define HFC_MAX_NT_LAYER1_STATE 4 + +const char *HFC_TE_LAYER1_STATES[HFC_MAX_TE_LAYER1_STATE + 1] = { + "TE F0 - Reset", + "TE F1 - Reset", + "TE F2 - Sensing", + "TE F3 - Deactivated", + "TE F4 - Awaiting signal", + "TE F5 - Identifying input", + "TE F6 - Synchronized", + "TE F7 - Activated", + "TE F8 - Lost framing", +}; + +const char *HFC_NT_LAYER1_STATES[HFC_MAX_NT_LAYER1_STATE + 1] = { + "NT G0 - Reset", + "NT G1 - Deactive", + "NT G2 - Pending activation", + "NT G3 - Active", + "NT G4 - Pending deactivation", +}; + +/* supported devices */ +static struct usb_device_id hfcsusb_idtab[] = { + { + USB_DEVICE(0x0959, 0x2bd0), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_OFF, {4, 0, 2, 1}, + "ISDN USB TA (Cologne Chip HFC-S USB based)"}), + }, + { + USB_DEVICE(0x0675, 0x1688), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {1, 2, 0, 0}, + "DrayTek miniVigor 128 USB ISDN TA"}), + }, + { + USB_DEVICE(0x07b0, 0x0007), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x80, -64, -32, -16}, + "Billion tiny USB ISDN TA 128"}), + }, + { + USB_DEVICE(0x0742, 0x2008), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {4, 0, 2, 1}, + "Stollmann USB TA"}), + }, + { + USB_DEVICE(0x0742, 0x2009), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {4, 0, 2, 1}, + "Aceex USB ISDN TA"}), + }, + { + USB_DEVICE(0x0742, 0x200A), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {4, 0, 2, 1}, + "OEM USB ISDN TA"}), + }, + { + USB_DEVICE(0x08e3, 0x0301), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {2, 0, 1, 4}, + "Olitec USB RNIS"}), + }, + { + USB_DEVICE(0x07fa, 0x0846), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x80, -64, -32, -16}, + "Bewan Modem RNIS USB"}), + }, + { + USB_DEVICE(0x07fa, 0x0847), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x80, -64, -32, -16}, + "Djinn Numeris USB"}), + }, + { + USB_DEVICE(0x07b0, 0x0006), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x80, -64, -32, -16}, + "Twister ISDN TA"}), + }, + { + USB_DEVICE(0x071d, 0x1005), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x02, 0, 0x01, 0x04}, + "Eicon DIVA USB 4.0"}), + }, + { + USB_DEVICE(0x0586, 0x0102), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x88, -64, -32, -16}, + "ZyXEL OMNI.NET USB II"}), + }, + { } +}; + +MODULE_DEVICE_TABLE(usb, hfcsusb_idtab); + +#endif /* __HFCSUSB_H__ */ -- cgit v1.2.3