// SPDX-License-Identifier: GPL-2.0-or-later /* * hfcmulti.c low level driver for hfc-4s/hfc-8s/hfc-e1 based cards * * Author Andreas Eversberg (jolly@eversberg.eu) * ported to mqueue mechanism: * Peter Sprenger (sprengermoving-bytes.de) * * inspired by existing hfc-pci driver: * Copyright 1999 by Werner Cornelius (werner@isdn-development.de) * Copyright 2008 by Karsten Keil (kkeil@suse.de) * Copyright 2008 by Andreas Eversberg (jolly@eversberg.eu) * * Thanks to Cologne Chip AG for this great controller!
*/
/* * module parameters: * type: * By default (0), the card is automatically detected. * Or use the following combinations: * Bit 0-7 = 0x00001 = HFC-E1 (1 port) * or Bit 0-7 = 0x00004 = HFC-4S (4 ports) * or Bit 0-7 = 0x00008 = HFC-8S (8 ports) * Bit 8 = 0x00100 = uLaw (instead of aLaw) * Bit 9 = 0x00200 = Disable DTMF detect on all B-channels via hardware * Bit 10 = spare * Bit 11 = 0x00800 = Force PCM bus into slave mode. (otherwise auto) * or Bit 12 = 0x01000 = Force PCM bus into master mode. (otherwise auto) * Bit 13 = spare * Bit 14 = 0x04000 = Use external ram (128K) * Bit 15 = 0x08000 = Use external ram (512K) * Bit 16 = 0x10000 = Use 64 timeslots instead of 32 * or Bit 17 = 0x20000 = Use 128 timeslots instead of anything else * Bit 18 = spare * Bit 19 = 0x80000 = Send the Watchdog a Signal (Dual E1 with Watchdog) * (all other bits are reserved and shall be 0) * example: 0x20204 one HFC-4S with dtmf detection and 128 timeslots on PCM * bus (PCM master) * * port: (optional or required for all ports on all installed cards) * HFC-4S/HFC-8S only bits: * Bit 0 = 0x001 = Use master clock for this S/T interface * (only once per chip). * Bit 1 = 0x002 = transmitter line setup (non capacitive mode) * Don't use this unless you know what you are doing! * Bit 2 = 0x004 = Disable E-channel. (No E-channel processing) * example: 0x0001,0x0000,0x0000,0x0000 one HFC-4S with master clock * received from port 1 * * HFC-E1 only bits: * Bit 0 = 0x0001 = interface: 0=copper, 1=optical * Bit 1 = 0x0002 = reserved (later for 32 B-channels transparent mode) * Bit 2 = 0x0004 = Report LOS * Bit 3 = 0x0008 = Report AIS * Bit 4 = 0x0010 = Report SLIP * Bit 5 = 0x0020 = Report RDI * Bit 8 = 0x0100 = Turn off CRC-4 Multiframe Mode, use double frame * mode instead. * Bit 9 = 0x0200 = Force get clock from interface, even in NT mode. * or Bit 10 = 0x0400 = Force put clock to interface, even in TE mode. * Bit 11 = 0x0800 = Use direct RX clock for PCM sync rather than PLL. * (E1 only) * Bit 12-13 = 0xX000 = elastic jitter buffer (1-3), Set both bits to 0 * for default. * (all other bits are reserved and shall be 0) * * debug: * NOTE: only one debug value must be given for all cards * enable debugging (see hfc_multi.h for debug options) * * 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! * Valid is 8, 16, 32, 64, 128, 256. * * pcm: * NOTE: only one pcm value must be given for every card. * The PCM bus id tells the mISDNdsp module about the connected PCM bus. * By default (0), the PCM bus id is 100 for the card that is PCM master. * If multiple cards are PCM master (because they are not interconnected), * each card with PCM master will have increasing PCM id. * All PCM buses with the same ID are expected to be connected and have * common time slots slots. * Only one chip of the PCM bus must be master, the others slave. * -1 means no support of PCM bus not even. * Omit this value, if all cards are interconnected or none is connected. * If unsure, don't give this parameter. * * dmask and bmask: * NOTE: One dmask value must be given for every HFC-E1 card. * If omitted, the E1 card has D-channel on time slot 16, which is default. * dmask is a 32 bit mask. The bit must be set for an alternate time slot. * If multiple bits are set, multiple virtual card fragments are created. * For each bit set, a bmask value must be given. Each bit on the bmask * value stands for a B-channel. The bmask may not overlap with dmask or * with other bmask values for that card. * Example: dmask=0x00020002 bmask=0x0000fffc,0xfffc0000 * This will create one fragment with D-channel on slot 1 with * B-channels on slots 2..15, and a second fragment with D-channel * on slot 17 with B-channels on slot 18..31. Slot 16 is unused. * If bit 0 is set (dmask=0x00000001) the D-channel is on slot 0 and will * not function. * Example: dmask=0x00000001 bmask=0xfffffffe * This will create a port with all 31 usable timeslots as * B-channels. * If no bits are set on bmask, no B-channel is created for that fragment. * Example: dmask=0xfffffffe bmask=0,0,0,0.... (31 0-values for bmask) * This will create 31 ports with one D-channel only. * If you don't know how to use it, you don't need it! * * iomode: * NOTE: only one mode value must be given for every card. * -> See hfc_multi.h for HFC_IO_MODE_* values * By default, the IO mode is pci memory IO (MEMIO). * Some cards require specific IO mode, so it cannot be changed. * It may be useful to set IO mode to register io (REGIO) to solve * PCI bridge problems. * If unsure, don't give this parameter. * * clockdelay_nt: * NOTE: only one clockdelay_nt value must be given once for all cards. * Give the value of the clock control register (A_ST_CLK_DLY) * of the S/T interfaces in NT mode. * This register is needed for the TBR3 certification, so don't change it. * * clockdelay_te: * NOTE: only one clockdelay_te value must be given once * 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 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. * * hwid: * NOTE: only one hwid value must be given once * Enable special embedded devices with XHFC controllers.
*/
/* * debug register access (never use this, it will flood your system log) * #define HFC_REGISTER_DEBUG
*/
static LIST_HEAD(HFClist); static DEFINE_SPINLOCK(HFClock); /* global hfc list lock */
staticvoid ph_state_change(struct dchannel *);
staticstruct hfc_multi *syncmaster; staticint plxsd_master; /* if we have a master card (yet) */ static DEFINE_SPINLOCK(plx_lock); /* may not acquire other lock inside */
i = -1; while (hfc_register_names[++i].name) { if (hfc_register_names[i].reg == reg)
strcat(regname, hfc_register_names[i].name);
} if (regname[0] == '\0')
strcpy(regname, "register");
i = 0; while (hfc_register_names[i++].name)
; while (hfc_register_names[++i].name) { if (hfc_register_names[i].reg == reg)
strcat(regname, hfc_register_names[i].name);
} if (regname[0] == '\0')
strcpy(regname, "register");
i = 0; while (hfc_register_names[i++].name)
; while (hfc_register_names[++i].name) { if (hfc_register_names[i].reg == reg)
strcat(regname, hfc_register_names[i].name);
} if (regname[0] == '\0')
strcpy(regname, "register");
printk(KERN_DEBUG "HFC_inw(chip %d, %02x=%s) = 0x%04x; in %s() line %d\n",
hc->id, reg, regname, val, function, line); return val;
} staticvoid
HFC_wait_debug(struct hfc_multi *hc, constchar *function, int line)
{
printk(KERN_DEBUG "HFC_wait(chip %d); in %s() line %d\n",
hc->id, function, line);
HFC_wait_nodebug(hc);
} #endif
/* write fifo data (REGIO) */ staticvoid
write_fifo_regio(struct hfc_multi *hc, u_char *data, int len)
{
outb(A_FIFO_DATA0, (hc->pci_iobase) + 4); while (len >> 2) {
outl(cpu_to_le32(*(u32 *)data), hc->pci_iobase);
data += 4;
len -= 4;
} while (len >> 1) {
outw(cpu_to_le16(*(u16 *)data), hc->pci_iobase);
data += 2;
len -= 2;
} while (len) {
outb(*data, hc->pci_iobase);
data++;
len--;
}
} /* write fifo data (PCIMEM) */ staticvoid
write_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len)
{ while (len >> 2) {
writel(cpu_to_le32(*(u32 *)data),
hc->pci_membase + A_FIFO_DATA0);
data += 4;
len -= 4;
} while (len >> 1) {
writew(cpu_to_le16(*(u16 *)data),
hc->pci_membase + A_FIFO_DATA0);
data += 2;
len -= 2;
} while (len) {
writeb(*data, hc->pci_membase + A_FIFO_DATA0);
data++;
len--;
}
}
/* read fifo data (REGIO) */ staticvoid
read_fifo_regio(struct hfc_multi *hc, u_char *data, int len)
{
outb(A_FIFO_DATA0, (hc->pci_iobase) + 4); while (len >> 2) {
*(u32 *)data = le32_to_cpu(inl(hc->pci_iobase));
data += 4;
len -= 4;
} while (len >> 1) {
*(u16 *)data = le16_to_cpu(inw(hc->pci_iobase));
data += 2;
len -= 2;
} while (len) {
*data = inb(hc->pci_iobase);
data++;
len--;
}
}
/* read fifo data (PCIMEM) */ staticvoid
read_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len)
{ while (len >> 2) {
*(u32 *)data =
le32_to_cpu(readl(hc->pci_membase + A_FIFO_DATA0));
data += 4;
len -= 4;
} while (len >> 1) {
*(u16 *)data =
le16_to_cpu(readw(hc->pci_membase + A_FIFO_DATA0));
data += 2;
len -= 2;
} while (len) {
*data = readb(hc->pci_membase + A_FIFO_DATA0);
data++;
len--;
}
}
/* select local bridge port address by writing to CIP port */ /* data = HFC_inb(c, cipv); * was _io before */
outw(cipv, hc->pci_iobase + 4);
data = inb(hc->pci_iobase);
/* restore R_CTRL for normal PCI read cycle speed */
HFC_outb(hc, R_CTRL, 0x0); /* was _io before */
/* select local bridge port address by writing to CIP port */
outw(cipv, hc->pci_iobase + 4); /* define a 32 bit dword with 4 identical bytes for write sequence */
datav = data | ((__u32) data << 8) | ((__u32) data << 16) |
((__u32) data << 24);
/* * write this 32 bit dword to the bridge data port * this will initiate a write sequence of up to 4 writes to the same * address on the local bus interface the number of write accesses * is undefined but >=1 and depends on the next PCI transaction * during write sequence on the local bus
*/
outl(datav, hc->pci_iobase);
}
staticinlinevoid
cpld_set_reg(struct hfc_multi *hc, unsignedchar reg)
{ /* Do data pin read low byte */
HFC_outb(hc, R_GPIO_OUT1, reg);
}
for (x = 0; x < NUM_EC; x++) { /* Setup GPIO's */ if (!x) {
ver = vpm_in(wc, x, 0x1a0);
printk(KERN_DEBUG "VPM: Chip %d: ver %02x\n", x, ver);
}
for (y = 0; y < 4; y++) {
vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */
vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */
vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */
}
vpm_out(wc, x, 0x24, 0x02);
reg = vpm_in(wc, x, 0x24);
printk(KERN_DEBUG "NLP Thresh is set to %d (0x%x)\n", reg, reg);
/* Initialize echo cans */ for (i = 0; i < MAX_TDM_CHAN; i++) { if (mask & (0x00000001 << i))
vpm_out(wc, x, i, 0x00);
}
/* * ARM arch at least disallows a udelay of * more than 2ms... it gives a fake "__bad_udelay" * reference at link-time. * long delays in kernel code are pretty sucky anyway * for now work around it using 5 x 2ms instead of 1 x 10ms
*/
if ((gpi2 & 0x3) != 0x3)
printk(KERN_DEBUG "Got interrupt 0x%x from VPM!\n", gpi2);
} #endif/* UNUSED */
/* * Interface to enable/disable the HW Echocan * * these functions are called within a spin_lock_irqsave on * the channel instance lock, so we are not disturbed by irqs * * we can later easily change the interface to make other * things configurable, for now we configure the taps *
*/
staticvoid
vpm_echocan_on(struct hfc_multi *hc, int ch, int taps)
{ unsignedint timeslot; unsignedint unit; struct bchannel *bch = hc->chan[ch].bch; #ifdef TXADJ int txadj = -4; struct sk_buff *skb; #endif if (hc->chan[ch].protocol != ISDN_P_B_RAW) return;
printk(KERN_NOTICE "vpm_echocan_off called on timeslot %d\n",
timeslot); /* FILLME */
vpm_out(hc, unit, timeslot, 0x01);
}
/* * Speech Design resync feature * NOTE: This is called sometimes outside interrupt handler. * We must lock irqsave, so no other interrupt (other card) will occur! * Also multiple interrupts may nest, so must lock each access (lists, card)!
*/ staticinlinevoid
hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm)
{ struct hfc_multi *hc, *next, *pcmmaster = NULL; void __iomem *plx_acc_32;
u_int pv;
u_long flags;
spin_lock_irqsave(&HFClock, flags);
spin_lock(&plx_lock); /* must be locked inside other locks */
if (debug & DEBUG_HFCMULTI_PLXSD)
printk(KERN_DEBUG "%s: RESYNC(syncmaster=0x%p)\n",
__func__, syncmaster);
/* select new master */ if (newmaster) { if (debug & DEBUG_HFCMULTI_PLXSD)
printk(KERN_DEBUG "using provided controller\n");
} else {
list_for_each_entry_safe(hc, next, &HFClist, list) { if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { if (hc->syncronized) {
newmaster = hc; break;
}
}
}
}
/* Disable sync of all cards */
list_for_each_entry_safe(hc, next, &HFClist, list) { if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
plx_acc_32 = hc->plx_membase + PLX_GPIOC;
pv = readl(plx_acc_32);
pv &= ~PLX_SYNC_O_EN;
writel(pv, plx_acc_32); if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) {
pcmmaster = hc; if (hc->ctype == HFC_TYPE_E1) { if (debug & DEBUG_HFCMULTI_PLXSD)
printk(KERN_DEBUG "Schedule SYNC_I\n");
hc->e1_resync |= 1; /* get SYNC_I */
}
}
}
}
if (newmaster) {
hc = newmaster; if (debug & DEBUG_HFCMULTI_PLXSD)
printk(KERN_DEBUG "id=%d (0x%p) = synchronized with " "interface.\n", hc->id, hc); /* Enable new sync master */
plx_acc_32 = hc->plx_membase + PLX_GPIOC;
pv = readl(plx_acc_32);
pv |= PLX_SYNC_O_EN;
writel(pv, plx_acc_32); /* switch to jatt PLL, if not disabled by RX_SYNC */ if (hc->ctype == HFC_TYPE_E1
&& !test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) { if (debug & DEBUG_HFCMULTI_PLXSD)
printk(KERN_DEBUG "Schedule jatt PLL\n");
hc->e1_resync |= 2; /* switch to jatt */
}
} else { if (pcmmaster) {
hc = pcmmaster; if (debug & DEBUG_HFCMULTI_PLXSD)
printk(KERN_DEBUG "id=%d (0x%p) = PCM master synchronized " "with QUARTZ\n", hc->id, hc); if (hc->ctype == HFC_TYPE_E1) { /* Use the crystal clock for the PCM
master card */ if (debug & DEBUG_HFCMULTI_PLXSD)
printk(KERN_DEBUG "Schedule QUARTZ for HFC-E1\n");
hc->e1_resync |= 4; /* switch quartz */
} else { if (debug & DEBUG_HFCMULTI_PLXSD)
printk(KERN_DEBUG "QUARTZ is automatically " "enabled by HFC-%dS\n", hc->ctype);
}
plx_acc_32 = hc->plx_membase + PLX_GPIOC;
pv = readl(plx_acc_32);
pv |= PLX_SYNC_O_EN;
writel(pv, plx_acc_32);
} else if (!rm)
printk(KERN_ERR "%s no pcm master, this MUST " "not happen!\n", __func__);
}
syncmaster = newmaster;
/* This must be called AND hc must be locked irqsave!!! */ staticinlinevoid
plxsd_checksync(struct hfc_multi *hc, int rm)
{ if (hc->syncronized) { if (syncmaster == NULL) { if (debug & DEBUG_HFCMULTI_PLXSD)
printk(KERN_DEBUG "%s: GOT sync on card %d" " (id=%d)\n", __func__, hc->id + 1,
hc->id);
hfcmulti_resync(hc, hc, rm);
}
} else { if (syncmaster == hc) { if (debug & DEBUG_HFCMULTI_PLXSD)
printk(KERN_DEBUG "%s: LOST sync on card %d" " (id=%d)\n", __func__, hc->id + 1,
hc->id);
hfcmulti_resync(hc, NULL, rm);
}
}
}
/* pcm id */ if (hc->pcm)
printk(KERN_INFO "controller has given PCM BUS ID %d\n",
hc->pcm); else { if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)
|| test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
PCM_cnt++; /* SD has proprietary bridging */
}
hc->pcm = PCM_cnt;
printk(KERN_INFO "controller has PCM BUS ID %d " "(auto selected)\n", hc->pcm);
}
/* set up timer */
HFC_outb(hc, R_TI_WD, poll_timer);
hc->hw.r_irqmsk_misc |= V_TI_IRQMSK;
/* set E1 state machine IRQ */ if (hc->ctype == HFC_TYPE_E1)
hc->hw.r_irqmsk_misc |= V_STA_IRQMSK;
/* set DTMF detection */ if (test_bit(HFC_CHIP_DTMF, &hc->chip)) { if (debug & DEBUG_HFCMULTI_INIT)
printk(KERN_DEBUG "%s: enabling DTMF detection " "for all B-channel\n", __func__);
hc->hw.r_dtmf = V_DTMF_EN | V_DTMF_STOP; if (test_bit(HFC_CHIP_ULAW, &hc->chip))
hc->hw.r_dtmf |= V_ULAW_SEL;
HFC_outb(hc, R_DTMF_N, 102 - 1);
hc->hw.r_irqmsk_misc |= V_DTMF_IRQMSK;
}
staticvoid
hfcmulti_tx(struct hfc_multi *hc, int ch)
{ int i, ii, temp, tmp_len, len = 0; int Zspace, z1, z2; /* must be int for calculation */ int Fspace, f1, f2;
u_char *d; int *txpending, slot_tx; struct bchannel *bch; struct dchannel *dch; struct sk_buff **sp = NULL; int *idxp;
/* show activity */ if (dch)
hc->activity_tx |= 1 << hc->chan[ch].port;
/* fill fifo to what we have left */
ii = len; if (dch || test_bit(FLG_HDLC, &bch->Flags))
temp = 1; else
temp = 0;
i = *idxp;
d = (*sp)->data + i; if (ii - i > Zspace)
ii = Zspace + i; if (debug & DEBUG_HFCMULTI_FIFO)
printk(KERN_DEBUG "%s(card %d): fifo(%d) has %d bytes space " "left (z1=%04x, z2=%04x) sending %d of %d bytes %s\n",
__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);
hc->chan[ch].Zfill += ii - i;
*idxp = ii;
/* if not all data has been written */ if (ii != len) { /* NOTE: fifo is started by the calling function */ return;
}
/* if all data has been written, terminate frame */ if (dch || test_bit(FLG_HDLC, &bch->Flags)) { /* increment f-counter */
HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F);
HFC_wait_nodebug(hc);
}
tmp_len = (*sp)->len;
dev_kfree_skb(*sp); /* check for next frame */ if (bch && get_next_bframe(bch)) {
len = tmp_len; goto next_frame;
} if (dch && get_next_dframe(dch)) {
len = tmp_len; goto next_frame;
}
/* * now we have no more data, so in case of transparent, * we set the last byte in fifo to 'silence' in case we will get * 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, hc->silence);
}
/* NOTE: only called if E1 card is in active state */ staticvoid
hfcmulti_rx(struct hfc_multi *hc, int ch)
{ int temp; int Zsize, z1, z2 = 0; /* = 0, to make GCC happy */ int f1 = 0, f2 = 0; /* = 0, to make GCC happy */ int again = 0; struct bchannel *bch; struct dchannel *dch = NULL; struct sk_buff *skb, **sp = NULL; int maxlen;
bch = hc->chan[ch].bch; if (bch) { if (!test_bit(FLG_ACTIVE, &bch->Flags)) return;
} elseif (hc->chan[ch].dch) {
dch = hc->chan[ch].dch; if (!test_bit(FLG_ACTIVE, &dch->Flags)) return;
} else { return;
}
next_frame: /* on first AND before getting next valid frame, R_FIFO must be written
to. */ if (test_bit(HFC_CHIP_B410P, &hc->chip) &&
(hc->chan[ch].protocol == ISDN_P_B_RAW) &&
(hc->chan[ch].slot_rx < 0) &&
(hc->chan[ch].slot_tx < 0))
HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch << 1) | 1); else
HFC_outb_nodebug(hc, R_FIFO, (ch << 1) | 1);
HFC_wait_nodebug(hc);
/* ignore if rx is off BUT change fifo (above) to start pending TX */ if (hc->chan[ch].rx_off) { if (bch)
bch->dropcnt += poll; /* not exact but fair enough */ return;
}
if (dch || test_bit(FLG_HDLC, &bch->Flags)) {
f1 = HFC_inb_nodebug(hc, A_F1); while (f1 != (temp = HFC_inb_nodebug(hc, A_F1))) { if (debug & DEBUG_HFCMULTI_FIFO)
printk(KERN_DEBUG "%s(card %d): reread f1 because %d!=%d\n",
__func__, hc->id + 1, temp, f1);
f1 = temp; /* repeat until F1 is equal */
}
f2 = HFC_inb_nodebug(hc, A_F2);
}
z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin; while (z1 != (temp = (HFC_inw_nodebug(hc, A_Z1) - hc->Zmin))) { if (debug & DEBUG_HFCMULTI_FIFO)
printk(KERN_DEBUG "%s(card %d): reread z2 because " "%d!=%d\n", __func__, hc->id + 1, temp, z2);
z1 = temp; /* repeat until Z1 is equal */
}
z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin;
Zsize = z1 - z2; if ((dch || test_bit(FLG_HDLC, &bch->Flags)) && f1 != f2) /* complete hdlc frame */
Zsize++; if (Zsize < 0)
Zsize += hc->Zlen; /* if buffer is empty */ if (Zsize <= 0) return;
if (bch) {
maxlen = bchannel_get_rxbuf(bch, Zsize); if (maxlen < 0) {
pr_warn("card%d.B%d: No bufferspace for %d bytes\n",
hc->id + 1, bch->nr, Zsize); return;
}
sp = &bch->rx_skb;
maxlen = bch->maxlen;
} else { /* Dchannel */
sp = &dch->rx_skb;
maxlen = dch->maxlen + 3; if (*sp == NULL) {
*sp = mI_alloc_skb(maxlen, GFP_ATOMIC); if (*sp == NULL) {
pr_warn("card%d: No mem for dch rx_skb\n",
hc->id + 1); return;
}
}
} /* show activity */ if (dch)
hc->activity_rx |= 1 << hc->chan[ch].port;
/* empty fifo with what we have */ if (dch || test_bit(FLG_HDLC, &bch->Flags)) { if (debug & DEBUG_HFCMULTI_FIFO)
printk(KERN_DEBUG "%s(card %d): fifo(%d) reading %d " "bytes (z1=%04x, z2=%04x) HDLC %s (f1=%d, f2=%d) " "got=%d (again %d)\n", __func__, hc->id + 1, ch,
Zsize, z1, z2, (f1 == f2) ? "fragment" : "COMPLETE",
f1, f2, Zsize + (*sp)->len, again); /* HDLC */ if ((Zsize + (*sp)->len) > maxlen) { if (debug & DEBUG_HFCMULTI_FIFO)
printk(KERN_DEBUG "%s(card %d): hdlc-frame too large.\n",
__func__, hc->id + 1);
skb_trim(*sp, 0);
HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F);
HFC_wait_nodebug(hc); return;
}
hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize);
if (f1 != f2) { /* increment Z2,F2-counter */
HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F);
HFC_wait_nodebug(hc); /* check size */ if ((*sp)->len < 4) { if (debug & DEBUG_HFCMULTI_FIFO)
printk(KERN_DEBUG "%s(card %d): Frame below minimum " "size\n", __func__, hc->id + 1);
skb_trim(*sp, 0); goto next_frame;
} /* there is at least one complete frame, check crc */ if ((*sp)->data[(*sp)->len - 1]) { if (debug & DEBUG_HFCMULTI_CRC)
printk(KERN_DEBUG "%s: CRC-error\n", __func__);
skb_trim(*sp, 0); goto next_frame;
}
skb_trim(*sp, (*sp)->len - 3); if ((*sp)->len < MISDN_COPY_SIZE) {
skb = *sp;
*sp = mI_alloc_skb(skb->len, GFP_ATOMIC); if (*sp) {
skb_put_data(*sp, skb->data, skb->len);
skb_trim(skb, 0);
} else {
printk(KERN_DEBUG "%s: No mem\n",
__func__);
*sp = skb;
skb = NULL;
}
} else {
skb = NULL;
} if (debug & DEBUG_HFCMULTI_FIFO) {
printk(KERN_DEBUG "%s(card %d):",
__func__, hc->id + 1);
temp = 0; while (temp < (*sp)->len)
printk(" %02x", (*sp)->data[temp++]);
printk("\n");
} if (dch)
recv_Dchannel(dch); else
recv_Bchannel(bch, MISDN_ID_ANY, false);
*sp = skb;
again++; goto next_frame;
} /* there is an incomplete frame */
} else { /* transparent */
hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize); if (debug & DEBUG_HFCMULTI_FIFO)
printk(KERN_DEBUG "%s(card %d): fifo(%d) reading %d bytes " "(z1=%04x, z2=%04x) TRANS\n",
__func__, hc->id + 1, ch, Zsize, z1, z2); /* only bch is transparent */
recv_Bchannel(bch, hc->chan[ch].Zfill, false);
}
}
/* * Interrupt handler
*/ staticvoid
signal_state_up(struct dchannel *dch, int info, char *msg)
{ struct sk_buff *skb; int id, data = info;
if (debug & DEBUG_HFCMULTI_STATE)
printk(KERN_DEBUG "%s: %s\n", __func__, msg);
/* process queued resync jobs */ if (hc->e1_resync) { /* lock, so e1_resync gets not changed */
spin_lock_irqsave(&HFClock, flags); if (hc->e1_resync & 1) { if (debug & DEBUG_HFCMULTI_PLXSD)
printk(KERN_DEBUG "Enable SYNC_I\n");
HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC); /* disable JATT, if RX_SYNC is set */ if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip))
HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX);
} if (hc->e1_resync & 2) { if (debug & DEBUG_HFCMULTI_PLXSD)
printk(KERN_DEBUG "Enable jatt PLL\n");
HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS);
} if (hc->e1_resync & 4) {
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.53Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.