diff options
Diffstat (limited to 'drivers/dahdi/wctdm24xxp/base.c')
-rw-r--r-- | drivers/dahdi/wctdm24xxp/base.c | 1817 |
1 files changed, 1349 insertions, 468 deletions
diff --git a/drivers/dahdi/wctdm24xxp/base.c b/drivers/dahdi/wctdm24xxp/base.c index ce6799e..5cf9e85 100644 --- a/drivers/dahdi/wctdm24xxp/base.c +++ b/drivers/dahdi/wctdm24xxp/base.c @@ -4,6 +4,9 @@ * Written by Mark Spencer <markster@digium.com> * Support for TDM800P and VPM150M by Matthew Fredrickson <creslin@digium.com> * + * Support for Hx8 by Andrew Kohlsmith <akohlsmith@mixdown.ca> and Matthew + * Fredrickson <creslin@digium.com> + * * Copyright (C) 2005 - 2010 Digium, Inc. * All rights reserved. * @@ -34,6 +37,8 @@ Tx Gain - No Pre-Emphasis: -35.99 to 12.00 db Tx Gain - W/Pre-Emphasis: -23.99 to 0.00 db */ +#define DEBUG + #include <linux/version.h> #include <linux/kernel.h> #include <linux/errno.h> @@ -46,11 +51,13 @@ Tx Gain - W/Pre-Emphasis: -23.99 to 0.00 db #include <linux/workqueue.h> #include <linux/delay.h> #include <linux/moduleparam.h> +#include <linux/firmware.h> #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) #include <linux/semaphore.h> #else #include <asm/semaphore.h> #endif +#include <linux/crc32.h> #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) /* Define this if you would like to load the modules in parallel. While this @@ -68,6 +75,7 @@ Tx Gain - W/Pre-Emphasis: -23.99 to 0.00 db #include "proslic.h" #include "wctdm24xxp.h" +#include "xhfc.h" #include "adt_lec.h" @@ -187,10 +195,24 @@ static const struct wctdm_desc wctdm410 = { "Wildcard TDM410P", 0, 4 }; static const struct wctdm_desc wcaex2400 = { "Wildcard AEX2400", FLAG_EXPRESS, 24 }; static const struct wctdm_desc wcaex800 = { "Wildcard AEX800", FLAG_EXPRESS, 8 }; static const struct wctdm_desc wcaex410 = { "Wildcard AEX410", FLAG_EXPRESS, 4 }; +static const struct wctdm_desc wcha80000 = { "HA8-0000", 0, 8 }; +static const struct wctdm_desc wchb80000 = { "HB8-0000", FLAG_EXPRESS, 8 }; +/** + * Returns true if the card is one of the Hybrid Digital Analog Cards. + */ +static inline bool is_hx8(const struct wctdm *wc) +{ + return (&wcha80000 == wc->desc) || (&wchb80000 == wc->desc); +} + +static inline bool is_digital_span(const struct wctdm_span *wspan) +{ + return (wspan->span.linecompat > 0); +} struct wctdm *ifaces[WC_MAX_IFACES]; -spinlock_t ifacelock = SPIN_LOCK_UNLOCKED; +DECLARE_MUTEX(ifacelock); static void wctdm_release(struct wctdm *wc); @@ -215,6 +237,7 @@ static int nativebridge = 0; static int ringdebounce = DEFAULT_RING_DEBOUNCE; static int fwringdetect = 0; static int latency = VOICEBUS_DEFAULT_LATENCY; +static int forceload; #define MS_PER_HOOKCHECK (1) #define NEONMWI_ON_DEBOUNCE (100/MS_PER_HOOKCHECK) @@ -291,7 +314,7 @@ int schluffen(wait_queue_head_t *q) static inline int empty_slot(struct wctdm *wc, int card) { int x; - for (x=0;x<USER_COMMANDS;x++) { + for (x = 0; x < USER_COMMANDS; x++) { if (!wc->cmdq[card].cmds[x]) return x; } @@ -438,8 +461,7 @@ static int config_vpmadt032(struct vpmadt032 *vpm, struct wctdm *wc) vpm->curecstate[i].nlp_type = vpm->options.vpmnlptype; vpm->curecstate[i].nlp_threshold = vpm->options.vpmnlpthresh; vpm->curecstate[i].nlp_max_suppress = vpm->options.vpmnlpmaxsupp; - vpm->curecstate[i].companding = (wc->span.deflaw == DAHDI_LAW_MULAW) ? ADT_COMP_ULAW : ADT_COMP_ALAW; - + vpm->curecstate[i].companding = (wc->chans[i]->chan.span->deflaw == DAHDI_LAW_ALAW) ? ADT_COMP_ALAW : ADT_COMP_ULAW; /* set_vpmadt032_chanconfig_from_state(&vpm->curecstate[i], &vpm->options, i, &chanconfig); !!! */ vpm->setchanconfig_from_state(vpm, i, &chanconfig); if ((res = gpakConfigureChannel(vpm->dspid, i, tdmToTdm, &chanconfig, &cstatus))) { @@ -485,6 +507,7 @@ static inline bool is_good_frame(const u8 *sframe) static inline void cmd_dequeue_vpmadt032(struct wctdm *wc, u8 *writechunk, int whichframe) { + unsigned long flags; struct vpmadt032_cmd *curcmd = NULL; struct vpmadt032 *vpmadt032 = wc->vpmadt032; int x; @@ -496,6 +519,7 @@ static inline void cmd_dequeue_vpmadt032(struct wctdm *wc, u8 *writechunk, int w if (test_bit(VPM150M_SPIRESET, &vpmadt032->control) || test_bit(VPM150M_HPIRESET, &vpmadt032->control)) { if (debug & DEBUG_ECHOCAN) dev_info(&wc->vb.pdev->dev, "HW Resetting VPMADT032...\n"); + spin_lock_irqsave(&wc->reglock, flags); for (x = 24; x < 28; x++) { if (x == 24) { if (test_and_clear_bit(VPM150M_SPIRESET, &vpmadt032->control)) @@ -507,6 +531,7 @@ static inline void cmd_dequeue_vpmadt032(struct wctdm *wc, u8 *writechunk, int w writechunk[CMD_BYTE(x, 1, 0)] = 0; writechunk[CMD_BYTE(x, 2, 0)] = 0x00; } + spin_unlock_irqrestore(&wc->reglock, flags); return; } @@ -603,39 +628,37 @@ static inline void cmd_dequeue(struct wctdm *wc, unsigned char *writechunk, int ecval = ecval % EC_SIZE; #endif - /* if a QRV card, map it to its first channel */ - if ((wc->modtype[card] == MOD_TYPE_QRV) && (card & 3)) - { - return; - } - if (wc->altcs[card]) - subaddr = 0; - + /* QRV and BRI modules only use commands relating to the first channel */ + if ((card & 0x03) && (wc->modtype[card] == MOD_TYPE_QRV)) { + return; + } + + if (wc->altcs[card]) + subaddr = 0; - /* Skip audio */ writechunk += 24; spin_lock_irqsave(&wc->reglock, flags); /* Search for something waiting to transmit */ if (pos) { - for (x=0;x<MAX_COMMANDS;x++) { + for (x = 0; x < MAX_COMMANDS; x++) { if ((wc->cmdq[card].cmds[x] & (__CMD_RD | __CMD_WR)) && !(wc->cmdq[card].cmds[x] & (__CMD_TX | __CMD_FIN))) { curcmd = wc->cmdq[card].cmds[x]; -#if 0 - dev_info(&wc->vb.pdev->dev, "Transmitting command '%08x' in slot %d\n", wc->cmdq[card].cmds[x], wc->txident); -#endif wc->cmdq[card].cmds[x] |= (wc->txident << 24) | __CMD_TX; break; } } } + if (!curcmd) { /* If nothing else, use filler */ if (wc->modtype[card] == MOD_TYPE_FXS) curcmd = CMD_RD(LINE_STATE); else if (wc->modtype[card] == MOD_TYPE_FXO) curcmd = CMD_RD(12); + else if (wc->modtype[card] == MOD_TYPE_BRI) + curcmd = 0x101010; else if (wc->modtype[card] == MOD_TYPE_QRV) curcmd = CMD_RD(3); else if (wc->modtype[card] == MOD_TYPE_VPM) { @@ -649,13 +672,15 @@ static inline void cmd_dequeue(struct wctdm *wc, unsigned char *writechunk, int curcmd = CMD_RD(0x1a0); } } + if (wc->modtype[card] == MOD_TYPE_FXS) { - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = (1 << (subaddr)); + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = (1 << (subaddr)); if (curcmd & __CMD_WR) - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0x7f; + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0x7f; else - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x80 | ((curcmd >> 8) & 0x7f); - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x80 | ((curcmd >> 8) & 0x7f); + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; + } else if (wc->modtype[card] == MOD_TYPE_FXO) { static const int FXO_ADDRS[4] = { 0x00, 0x08, 0x04, 0x0c }; int idx = CMD_BYTE(card, 0, wc->altcs[card]); @@ -663,48 +688,53 @@ static inline void cmd_dequeue(struct wctdm *wc, unsigned char *writechunk, int writechunk[idx] = 0x20 | FXO_ADDRS[subaddr]; else writechunk[idx] = 0x60 | FXO_ADDRS[subaddr]; - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0xff; - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0xff; + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; + } else if (wc->modtype[card] == MOD_TYPE_FXSINIT) { /* Special case, we initialize the FXS's into the three-byte command mode then switch to the regular mode. To send it into thee byte mode, treat the path as 6 two-byte commands and in the last one we initialize register 0 to 0x80. All modules read this as the command to switch to daisy chain mode and we're done. */ - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00; - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00; + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00; + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00; if ((card & 0x1) == 0x1) - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x80; + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x80; else - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00; + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00; + + } else if (wc->modtype[card] == MOD_TYPE_BRI) { + + if (unlikely((curcmd != 0x101010) && (curcmd & 0x1010) == 0x1010)) /* b400m CPLD */ + writechunk[CMD_BYTE(card, 0, 0)] = 0x55; + else /* xhfc */ + writechunk[CMD_BYTE(card, 0, 0)] = 0x10; + writechunk[CMD_BYTE(card, 1, 0)] = (curcmd >> 8) & 0xff; + writechunk[CMD_BYTE(card, 2, 0)] = curcmd & 0xff; } else if (wc->modtype[card] == MOD_TYPE_VPM) { if (curcmd & __CMD_WR) - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = ((card & 0x3) << 4) | 0xc | ((curcmd >> 16) & 0x1); + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = ((card & 0x3) << 4) | 0xc | ((curcmd >> 16) & 0x1); else - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = ((card & 0x3) << 4) | 0xa | ((curcmd >> 16) & 0x1); - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0xff; - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; - } else if (wc->modtype[card] == MOD_TYPE_VPM150M) { - ; - } else if (wc->modtype[card] == MOD_TYPE_QRV) { + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = ((card & 0x3) << 4) | 0xa | ((curcmd >> 16) & 0x1); + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = (curcmd >> 8) & 0xff; + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; + } else if (wc->modtype[card] == MOD_TYPE_QRV) { - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00; - if (!curcmd) - { - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00; - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00; - } - else - { - if (curcmd & __CMD_WR) - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x40 | ((curcmd >> 8) & 0x3f); - else - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0xc0 | ((curcmd >> 8) & 0x3f); - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; - } + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00; + if (!curcmd) { + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00; + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00; + } else { + if (curcmd & __CMD_WR) + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x40 | ((curcmd >> 8) & 0x3f); + else + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0xc0 | ((curcmd >> 8) & 0x3f); + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = curcmd & 0xff; + } } else if (wc->modtype[card] == MOD_TYPE_NONE) { - writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x00; - writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x00; - writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x00; + writechunk[CMD_BYTE(card, 0, wc->altcs[card])] = 0x10; + writechunk[CMD_BYTE(card, 1, wc->altcs[card])] = 0x10; + writechunk[CMD_BYTE(card, 2, wc->altcs[card])] = 0x10; } #if 0 /* XXX */ @@ -715,10 +745,10 @@ static inline void cmd_dequeue(struct wctdm *wc, unsigned char *writechunk, int #if 0 /* XXX */ cmddesc++; -#endif +#endif } -static inline void cmd_decipher_vpmadt032(struct wctdm *wc, unsigned char *readchunk) +static inline void cmd_decipher_vpmadt032(struct wctdm *wc, const u8 *readchunk) { unsigned long flags; struct vpmadt032 *vpm = wc->vpmadt032; @@ -747,6 +777,7 @@ static inline void cmd_decipher_vpmadt032(struct wctdm *wc, unsigned char *readc /* Skip audio */ readchunk += 24; + /* Store result */ cmd->data = (0xff & readchunk[CMD_BYTE(25, 1, 0)]) << 8; cmd->data |= readchunk[CMD_BYTE(25, 2, 0)]; @@ -757,26 +788,23 @@ static inline void cmd_decipher_vpmadt032(struct wctdm *wc, unsigned char *readc cmd->desc |= __VPM150M_FIN; complete(&cmd->complete); } -#if 0 - // if (printk_ratelimit()) - dev_info(&wc->vb.pdev->dev, "Received txident = %d, desc = 0x%x, addr = 0x%x, data = 0x%x\n", cmd->txident, cmd->desc, cmd->address, cmd->data); -#endif } -static inline void cmd_decipher(struct wctdm *wc, u8 *readchunk, int card) +static inline void cmd_decipher(struct wctdm *wc, const u8 *readchunk, int card) { unsigned long flags; unsigned char ident; int x; - /* if a QRV card, map it to its first channel */ - if ((wc->modtype[card] == MOD_TYPE_QRV) && (card & 3)) - { + /* QRV and BRI modules only use commands relating to the first channel */ + if ((card & 0x03) && (wc->modtype[card] == MOD_TYPE_QRV)) { /* || (wc->modtype[card] == MOD_TYPE_BRI))) { */ return; } + /* Skip audio */ readchunk += 24; spin_lock_irqsave(&wc->reglock, flags); + /* Search for any pending results */ for (x=0;x<MAX_COMMANDS;x++) { if ((wc->cmdq[card].cmds[x] & (__CMD_RD | __CMD_WR)) && @@ -787,6 +815,11 @@ static inline void cmd_decipher(struct wctdm *wc, u8 *readchunk, int card) /* Store result */ wc->cmdq[card].cmds[x] |= readchunk[CMD_BYTE(card, 2, wc->altcs[card])]; wc->cmdq[card].cmds[x] |= __CMD_FIN; +/* + if (card == 0 && wc->cmdq[card].cmds[x] & __CMD_RD) { + dev_info(&wc->vb.pdev->dev, "decifer: got response %02x\n", wc->cmdq[card].cmds[x] & 0xff); + } +*/ if (wc->cmdq[card].cmds[x] & __CMD_WR) { /* Go ahead and clear out writes since they need no acknowledgement */ wc->cmdq[card].cmds[x] = 0x00000000; @@ -819,6 +852,8 @@ static inline void cmd_checkisr(struct wctdm *wc, int card) wc->cmdq[card].cmds[USER_COMMANDS + 0] = CMD_RD(5); /* Hook/Ring state */ } else if (wc->modtype[card] == MOD_TYPE_QRV) { wc->cmdq[card & 0xfc].cmds[USER_COMMANDS + 0] = CMD_RD(3); /* COR/CTCSS state */ + } else if (wc->modtype[card] == MOD_TYPE_BRI) { + wc->cmdq[card].cmds[USER_COMMANDS + 0] = wctdm_bri_checkisr(wc, card, 0); #ifdef VPM_SUPPORT } else if (wc->modtype[card] == MOD_TYPE_VPM) { wc->cmdq[card].cmds[USER_COMMANDS + 0] = CMD_RD(0xb9); /* DTMF interrupt */ @@ -836,6 +871,8 @@ static inline void cmd_checkisr(struct wctdm *wc, int card) wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(29); /* Battery */ } else if (wc->modtype[card] == MOD_TYPE_QRV) { wc->cmdq[card & 0xfc].cmds[USER_COMMANDS + 1] = CMD_RD(3); /* Battery */ + } else if (wc->modtype[card] == MOD_TYPE_BRI) { + wc->cmdq[card].cmds[USER_COMMANDS + 1] = wctdm_bri_checkisr(wc, card, 1); #ifdef VPM_SUPPORT } else if (wc->modtype[card] == MOD_TYPE_VPM) { wc->cmdq[card].cmds[USER_COMMANDS + 1] = CMD_RD(0xbd); /* DTMF interrupt */ @@ -853,8 +890,8 @@ static void insert_tdm_data(const struct wctdm *wc, u8 *sframe) int i; register u8 *chanchunk; - for (i = 0; i < wc->cards; i += 4) { - chanchunk = &wc->chans[0 + i]->writechunk[0]; + for (i = 0; i < wc->avchannels; i += 4) { + chanchunk = &wc->chans[0 + i]->chan.writechunk[0]; sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*0] = chanchunk[0]; sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*1] = chanchunk[1]; sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*2] = chanchunk[2]; @@ -864,7 +901,7 @@ static void insert_tdm_data(const struct wctdm *wc, u8 *sframe) sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*6] = chanchunk[6]; sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*7] = chanchunk[7]; - chanchunk = &wc->chans[1 + i]->writechunk[0]; + chanchunk = &wc->chans[1 + i]->chan.writechunk[0]; sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*0] = chanchunk[0]; sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*1] = chanchunk[1]; sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*2] = chanchunk[2]; @@ -874,7 +911,7 @@ static void insert_tdm_data(const struct wctdm *wc, u8 *sframe) sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*6] = chanchunk[6]; sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*7] = chanchunk[7]; - chanchunk = &wc->chans[2 + i]->writechunk[0]; + chanchunk = &wc->chans[2 + i]->chan.writechunk[0]; sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*0] = chanchunk[0]; sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*1] = chanchunk[1]; sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*2] = chanchunk[2]; @@ -884,7 +921,7 @@ static void insert_tdm_data(const struct wctdm *wc, u8 *sframe) sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*6] = chanchunk[6]; sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*7] = chanchunk[7]; - chanchunk = &wc->chans[3 + i]->writechunk[0]; + chanchunk = &wc->chans[3 + i]->chan.writechunk[0]; sframe[3 + i + (EFRAME_SIZE + EFRAME_GAP)*0] = chanchunk[0]; sframe[3 + i + (EFRAME_SIZE + EFRAME_GAP)*1] = chanchunk[1]; sframe[3 + i + (EFRAME_SIZE + EFRAME_GAP)*2] = chanchunk[2]; @@ -899,30 +936,40 @@ static void insert_tdm_data(const struct wctdm *wc, u8 *sframe) static inline void wctdm_transmitprep(struct wctdm *wc, unsigned char *writechunk) { int x,y; + struct dahdi_span *s; /* Calculate Transmission */ if (likely(wc->initialized)) { - dahdi_transmit(&wc->span); + for (x = 0; x < MAX_SPANS; x++) { + if (wc->spans[x]) { + s = &wc->spans[x]->span; + dahdi_transmit(s); + } + } insert_tdm_data(wc, writechunk); } - for (x=0;x<DAHDI_CHUNKSIZE;x++) { + for (x = 0; x < DAHDI_CHUNKSIZE; x++) { /* Send a sample, as a 32-bit word */ - for (y=0;y < wc->cards;y++) { - if (!x) { + + /* TODO: ABK: hmm, this was originally mods_per_board, but we + * need to worry about all the active "voice" timeslots, since + * BRI modules have a different number of TDM channels than + * installed modules. */ + for (y = 0; y < wc->avchannels; y++) { + if (!x && y < wc->mods_per_board) { cmd_checkisr(wc, y); } - if (x < 3) + if ((x < 3) && (y < wc->mods_per_board)) cmd_dequeue(wc, writechunk, y, x); } if (!x) wc->blinktimer++; if (wc->vpm100) { - for (y=24;y<28;y++) { - if (!x) { + for (y = NUM_MODULES; y < NUM_MODULES + NUM_EC; y++) { + if (!x) cmd_checkisr(wc, y); - } cmd_dequeue(wc, writechunk, y, x); } #ifdef FANCY_ECHOCAN @@ -939,7 +986,7 @@ static inline void wctdm_transmitprep(struct wctdm *wc, unsigned char *writechun writechunk[EFRAME_SIZE] = wc->ctlreg; writechunk[EFRAME_SIZE + 1] = wc->txident++; - if ((wc->desc->ports == 4) && ((wc->ctlreg & 0x10) || (wc->modtype[NUM_CARDS] == MOD_TYPE_NONE))) { + if ((wc->desc->ports == 4) && ((wc->ctlreg & 0x10) || (wc->modtype[NUM_MODULES] == MOD_TYPE_NONE))) { writechunk[EFRAME_SIZE + 2] = 0; for (y = 0; y < 4; y++) { if (wc->modtype[y] == MOD_TYPE_NONE) @@ -958,11 +1005,11 @@ static inline int wctdm_setreg_full(struct wctdm *wc, int card, int addr, int va int hit=0; int ret; - /* if a QRV card, use only its first channel */ - if (wc->modtype[card] == MOD_TYPE_QRV) - { - if (card & 3) return(0); - } + /* QRV and BRI cards are only addressed at their first "port" */ + if ((card & 0x03) && ((wc->modtype[card] == MOD_TYPE_QRV) || + (wc->modtype[card] == MOD_TYPE_BRI))) + return 0; + do { spin_lock_irqsave(&wc->reglock, flags); hit = empty_slot(wc, card); @@ -984,12 +1031,12 @@ static inline int wctdm_setreg_intr(struct wctdm *wc, int card, int addr, int va { return wctdm_setreg_full(wc, card, addr, val, 1); } -static inline int wctdm_setreg(struct wctdm *wc, int card, int addr, int val) +inline int wctdm_setreg(struct wctdm *wc, int card, int addr, int val) { return wctdm_setreg_full(wc, card, addr, val, 0); } -static inline int wctdm_getreg(struct wctdm *wc, int card, int addr) +inline int wctdm_getreg(struct wctdm *wc, int card, int addr) { unsigned long flags; int hit; @@ -1028,14 +1075,15 @@ static inline int wctdm_getreg(struct wctdm *wc, int card, int addr) return ret; } + static inline unsigned char wctdm_vpm_in(struct wctdm *wc, int unit, const unsigned int addr) { - return wctdm_getreg(wc, unit + NUM_CARDS, addr); + return wctdm_getreg(wc, unit + NUM_MODULES, addr); } static inline void wctdm_vpm_out(struct wctdm *wc, int unit, const unsigned int addr, const unsigned char val) { - wctdm_setreg(wc, unit + NUM_CARDS, addr, val); + wctdm_setreg(wc, unit + NUM_MODULES, addr, val); } /* TODO: this should go in the dahdi_voicebus module... */ @@ -1055,6 +1103,7 @@ static inline void cmd_vpmadt032_retransmit(struct wctdm *wc) list_move_tail(&cmd->node, &vpmadt032->pending_cmds); } spin_unlock_irqrestore(&vpmadt032->list_lock, flags); + } static inline void cmd_retransmit(struct wctdm *wc) @@ -1064,9 +1113,11 @@ static inline void cmd_retransmit(struct wctdm *wc) /* Force retransmissions */ spin_lock_irqsave(&wc->reglock, flags); for (x=0;x<MAX_COMMANDS;x++) { - for (y=0;y<wc->cards;y++) { - if (!(wc->cmdq[y].cmds[x] & __CMD_FIN)) - wc->cmdq[y].cmds[x] &= ~(__CMD_TX | (0xff << 24)); + for (y = 0; y < wc->mods_per_board; y++) { + if (wc->modtype[y] != MOD_TYPE_BRI) { + if (!(wc->cmdq[y].cmds[x] & __CMD_FIN)) + wc->cmdq[y].cmds[x] &= ~(__CMD_TX | (0xff << 24)); + } } } spin_unlock_irqrestore(&wc->reglock, flags); @@ -1085,8 +1136,8 @@ static void extract_tdm_data(struct wctdm *wc, const u8 *sframe) int i; register u8 *chanchunk; - for (i = 0; i < wc->cards; i += 4) { - chanchunk = &wc->chans[0 + i]->readchunk[0]; + for (i = 0; i < wc->avchannels; i += 4) { + chanchunk = &wc->chans[0 + i]->chan.readchunk[0]; chanchunk[0] = sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*0]; chanchunk[1] = sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*1]; chanchunk[2] = sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*2]; @@ -1096,7 +1147,7 @@ static void extract_tdm_data(struct wctdm *wc, const u8 *sframe) chanchunk[6] = sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*6]; chanchunk[7] = sframe[0 + i + (EFRAME_SIZE + EFRAME_GAP)*7]; - chanchunk = &wc->chans[1 + i]->readchunk[0]; + chanchunk = &wc->chans[1 + i]->chan.readchunk[0]; chanchunk[0] = sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*0]; chanchunk[1] = sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*1]; chanchunk[2] = sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*2]; @@ -1106,7 +1157,7 @@ static void extract_tdm_data(struct wctdm *wc, const u8 *sframe) chanchunk[6] = sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*6]; chanchunk[7] = sframe[1 + i + (EFRAME_SIZE + EFRAME_GAP)*7]; - chanchunk = &wc->chans[2 + i]->readchunk[0]; + chanchunk = &wc->chans[2 + i]->chan.readchunk[0]; chanchunk[0] = sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*0]; chanchunk[1] = sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*1]; chanchunk[2] = sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*2]; @@ -1116,7 +1167,7 @@ static void extract_tdm_data(struct wctdm *wc, const u8 *sframe) chanchunk[6] = sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*6]; chanchunk[7] = sframe[2 + i + (EFRAME_SIZE + EFRAME_GAP)*7]; - chanchunk = &wc->chans[3 + i]->readchunk[0]; + chanchunk = &wc->chans[3 + i]->chan.readchunk[0]; chanchunk[0] = sframe[3 + i + (EFRAME_SIZE + EFRAME_GAP)*0]; chanchunk[1] = sframe[3 + i + (EFRAME_SIZE + EFRAME_GAP)*1]; chanchunk[2] = sframe[3 + i + (EFRAME_SIZE + EFRAME_GAP)*2]; @@ -1128,9 +1179,10 @@ static void extract_tdm_data(struct wctdm *wc, const u8 *sframe) } } -static inline void wctdm_receiveprep(struct wctdm *wc, unsigned char *readchunk) +static inline void wctdm_receiveprep(struct wctdm *wc, const u8 *readchunk) { int x,y; + bool irqmiss = 0; unsigned char expected; if (unlikely(!is_good_frame(readchunk))) @@ -1139,38 +1191,56 @@ static inline void wctdm_receiveprep(struct wctdm *wc, unsigned char *readchunk) if (likely(wc->initialized)) extract_tdm_data(wc, readchunk); - for (x=0;x<DAHDI_CHUNKSIZE;x++) { + for (x = 0; x < DAHDI_CHUNKSIZE; x++) { if (x < DAHDI_CHUNKSIZE - 1) { expected = wc->rxident+1; wc->rxident = readchunk[EFRAME_SIZE + 1]; if (wc->rxident != expected) { - wc->span.irqmisses++; + irqmiss = 1; cmd_retransmit(wc); } } - for (y=0;y < wc->cards;y++) { + for (y = 0; y < wc->avchannels; y++) { if (x < 3) cmd_decipher(wc, readchunk, y); } if (wc->vpm100) { - for (y=NUM_CARDS;y < NUM_CARDS + NUM_EC; y++) + for (y = NUM_MODULES; y < NUM_MODULES + NUM_EC; y++) cmd_decipher(wc, readchunk, y); - } else if (wc->vpmadt032) { + } else if (wc->vpmadt032) cmd_decipher_vpmadt032(wc, readchunk); - } readchunk += (EFRAME_SIZE + EFRAME_GAP); } + /* XXX We're wasting 8 taps. We should get closer :( */ if (likely(wc->initialized)) { - for (x = 0; x < wc->desc->ports; x++) { - if (wc->cardflag & (1 << x)) - dahdi_ec_chunk(wc->chans[x], wc->chans[x]->readchunk, wc->chans[x]->writechunk); + for (x = 0; x < wc->avchannels; x++) { + struct dahdi_chan *c = &wc->chans[x]->chan; + dahdi_ec_chunk(c, c->readchunk, c->writechunk); + } + + for (x = 0; x < MAX_SPANS; x++) { + if (wc->spans[x]) { + struct dahdi_span *s = &wc->spans[x]->span; +#if 1 + /* Check for digital spans */ + if (s->chanconfig == b400m_chanconfig) { + BUG_ON(!is_hx8(wc)); + if (s->flags & DAHDI_FLAG_RUNNING) + b400m_dchan(s); + + } +#endif + dahdi_receive(s); + if (unlikely(irqmiss)) + ++s->irqmisses; + } } - dahdi_receive(&wc->span); } + /* Wake up anyone sleeping to read/write a new register */ - wake_up_interruptible(&wc->regq); + wake_up_interruptible_all(&wc->regq); } static int wait_access(struct wctdm *wc, int card) @@ -1193,7 +1263,8 @@ static int wait_access(struct wctdm *wc, int card) } - if(count > (MAX-1)) dev_notice(&wc->vb.pdev->dev, " ##### Loop error (%02x) #####\n", data); + if (count > (MAX-1)) + dev_notice(&wc->vb.pdev->dev, " ##### Loop error (%02x) #####\n", data); return 0; } @@ -1219,7 +1290,7 @@ static int wctdm_proslic_setreg_indirect(struct wctdm *wc, int card, unsigned ch if (address == 255) return 0; } - if(!wait_access(wc, card)) { + if (!wait_access(wc, card)) { wctdm_setreg(wc, card, IDA_LO,(unsigned char)(data & 0xFF)); wctdm_setreg(wc, card, IDA_HI,(unsigned char)((data & 0xFF00)>>8)); wctdm_setreg(wc, card, IAA,address); @@ -1258,9 +1329,10 @@ static int wctdm_proslic_init_indirect_regs(struct wctdm *wc, int card) { unsigned char i; - for (i = 0; i < ARRAY_SIZE(indirect_regs); i++) - { - if(wctdm_proslic_setreg_indirect(wc, card, indirect_regs[i].address,indirect_regs[i].initial)) + for (i = 0; i < ARRAY_SIZE(indirect_regs); i++) { + if (wctdm_proslic_setreg_indirect(wc, card, + indirect_regs[i].address, + indirect_regs[i].initial)) return -1; } @@ -1275,7 +1347,8 @@ static int wctdm_proslic_verify_indirect_regs(struct wctdm *wc, int card) for (i = 0; i < ARRAY_SIZE(indirect_regs); i++) { - if((j = wctdm_proslic_getreg_indirect(wc, card, (unsigned char) indirect_regs[i].address)) < 0) { + j = wctdm_proslic_getreg_indirect(wc, card, (unsigned char) indirect_regs[i].address); + if (j < 0) { dev_notice(&wc->vb.pdev->dev, "Failed to read indirect register %d\n", i); return -1; } @@ -1389,7 +1462,7 @@ static inline void wctdm_qrvdri_check_hook(struct wctdm *wc, int card) { b1 = wc->qrvhook[qrvcard + 2]; if (debug) dev_info(&wc->vb.pdev->dev, "QRV channel %d rx state changed to %d\n",qrvcard,wc->qrvhook[qrvcard + 2]); - dahdi_hooksig(wc->chans[qrvcard], + dahdi_hooksig(wc->aspan->span.chans[qrvcard], (b1) ? DAHDI_RXSIG_OFFHOOK : DAHDI_RXSIG_ONHOOK); wc->qrvdebtime[card] = 0; } @@ -1403,7 +1476,7 @@ if (debug) dev_info(&wc->vb.pdev->dev, "QRV channel %d rx state changed to %d\n" { b1 = wc->qrvhook[qrvcard + 3]; if (debug) dev_info(&wc->vb.pdev->dev, "QRV channel %d rx state changed to %d\n",qrvcard + 1,wc->qrvhook[qrvcard + 3]); - dahdi_hooksig(wc->chans[qrvcard + 1], + dahdi_hooksig(wc->aspan->span.chans[qrvcard + 1], (b1) ? DAHDI_RXSIG_OFFHOOK : DAHDI_RXSIG_ONHOOK); wc->qrvdebtime[card] = 0; } @@ -1430,7 +1503,7 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) wctdm_setreg_intr(wc, card, 5, 0x8); } if (!fxo->offhook) { - if(fwringdetect || neonmwi_monitor) { + if (fwringdetect || neonmwi_monitor) { /* Look for ring status bits (Ring Detect Signal Negative and * Ring Detect Signal Positive) to transition back and forth * some number of times to indicate that a ring is occurring. @@ -1453,11 +1526,11 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) /* If ring detect signal has transversed */ if (res && res != fxo->lastrdtx) { /* if there are at least 3 ring polarity transversals */ - if(++fxo->lastrdtx_count >= 2) { + if (++fxo->lastrdtx_count >= 2) { fxo->wasringing = 1; if (debug) - dev_info(&wc->vb.pdev->dev, "FW RING on %d/%d!\n", wc->span.spanno, card + 1); - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); + dev_info(&wc->vb.pdev->dev, "FW RING on %d/%d!\n", wc->aspan->span.spanno, card + 1); + dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_RING); fxo->ringdebounce = ringdebounce / 16; } else { fxo->lastrdtx = res; @@ -1473,8 +1546,8 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) } else if (--fxo->ringdebounce == 0) { fxo->wasringing = 0; if (debug) - dev_info(&wc->vb.pdev->dev, "FW NO RING on %d/%d!\n", wc->span.spanno, card + 1); - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + dev_info(&wc->vb.pdev->dev, "FW NO RING on %d/%d!\n", wc->aspan->span.spanno, card + 1); + dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_OFFHOOK); } } } else { @@ -1484,9 +1557,9 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) if (fxo->ringdebounce >= DAHDI_CHUNKSIZE * ringdebounce) { if (!fxo->wasringing) { fxo->wasringing = 1; - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_RING); + dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_RING); if (debug) - dev_info(&wc->vb.pdev->dev, "RING on %d/%d!\n", wc->span.spanno, card + 1); + dev_info(&wc->vb.pdev->dev, "RING on %d/%d!\n", wc->aspan->span.spanno, card + 1); } fxo->ringdebounce = DAHDI_CHUNKSIZE * ringdebounce; } @@ -1495,9 +1568,9 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) if (fxo->ringdebounce <= 0) { if (fxo->wasringing) { fxo->wasringing = 0; - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_OFFHOOK); if (debug) - dev_info(&wc->vb.pdev->dev, "NO RING on %d/%d!\n", wc->span.spanno, card + 1); + dev_info(&wc->vb.pdev->dev, "NO RING on %d/%d!\n", wc->aspan->span.spanno, card + 1); } fxo->ringdebounce = 0; } @@ -1511,12 +1584,11 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) if (fxovoltage) { if (!(wc->intcount % 100)) { - dev_info(&wc->vb.pdev->dev, "Port %d: Voltage: %d Debounce %d\n", card + 1, - b, fxo->battdebounce); + dev_info(&wc->vb.pdev->dev, "Port %d: Voltage: %d Debounce %d\n", card + 1, b, fxo->battdebounce); } } - if (unlikely(DAHDI_RXSIG_INITIAL == wc->chans[card]->rxhooksig)) { + if (unlikely(DAHDI_RXSIG_INITIAL == wc->aspan->span.chans[card]->rxhooksig)) { /* * dahdi-base will set DAHDI_RXSIG_INITIAL after a * DAHDI_STARTUP or DAHDI_CHANCONFIG ioctl so that new events @@ -1551,10 +1623,10 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) if (--fxo->battdebounce == 0) { fxo->battery = BATTERY_LOST; if (debug) - dev_info(&wc->vb.pdev->dev, "NO BATTERY on %d/%d!\n", wc->span.spanno, card + 1); + dev_info(&wc->vb.pdev->dev, "NO BATTERY on %d/%d!\n", wc->aspan->span.spanno, card + 1); #ifdef JAPAN if (!wc->ohdebounce && wc->offhook) { - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK); + dahdi_hooksig(wc->aspan->chans[card], DAHDI_RXSIG_ONHOOK); if (debug) dev_info(&wc->vb.pdev->dev, "Signalled On Hook\n"); #ifdef ZERO_BATT_RING @@ -1562,7 +1634,7 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) #endif } #else - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK); + dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_ONHOOK); /* set the alarm timer, taking into account that part of its time period has already passed while debouncing occurred */ fxo->battalarm = (battalarm - battdebounce) / MS_PER_CHECK_HOOK; @@ -1592,18 +1664,22 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) /* going to BATTERY_PRESENT, see if we are there yet */ if (--fxo->battdebounce == 0) { fxo->battery = BATTERY_PRESENT; - if (debug) - dev_info(&wc->vb.pdev->dev, "BATTERY on %d/%d (%s)!\n", wc->span.spanno, card + 1, - (b < 0) ? "-" : "+"); + if (debug) { + dev_info(&wc->vb.pdev->dev, + "BATTERY on %d/%d (%s)!\n", + wc->aspan->span.spanno, + card + 1, + (b < 0) ? "-" : "+"); + } #ifdef ZERO_BATT_RING if (wc->onhook) { wc->onhook = 0; - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + dahdi_hooksig(wc->aspan->chans[card], DAHDI_RXSIG_OFFHOOK); if (debug) dev_info(&wc->vb.pdev->dev, "Signalled Off Hook\n"); } #else - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_OFFHOOK); #endif /* set the alarm timer, taking into account that part of its time period has already passed while debouncing occurred */ @@ -1633,7 +1709,7 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) if (--fxo->battalarm == 0) { /* the alarm timer has expired, so update the battery alarm state for this channel */ - dahdi_alarm_channel(wc->chans[card], fxo->battery == BATTERY_LOST ? DAHDI_ALARM_RED : DAHDI_ALARM_NONE); + dahdi_alarm_channel(wc->aspan->span.chans[card], fxo->battery == BATTERY_LOST ? DAHDI_ALARM_RED : DAHDI_ALARM_NONE); } } @@ -1646,7 +1722,7 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) fxo->polarity, fxo->lastpol); if (fxo->polarity) - dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_POLARITY); + dahdi_qevent_lock(wc->aspan->span.chans[card], DAHDI_EVENT_POLARITY); fxo->polarity = fxo->lastpol; } } @@ -1665,8 +1741,8 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) fxo->neonmwi_last_voltage = b; if (NEONMWI_ON_DEBOUNCE == fxo->neonmwi_debounce) { fxo->neonmwi_offcounter = neonmwi_offlimit_cycles; - if(0 == fxo->neonmwi_state) { - dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_NEONMWI_ACTIVE); + if (0 == fxo->neonmwi_state) { + dahdi_qevent_lock(wc->aspan->span.chans[card], DAHDI_EVENT_NEONMWI_ACTIVE); fxo->neonmwi_state = 1; if (debug) dev_info(&wc->vb.pdev->dev, "NEON MWI active for card %d\n", card+1); @@ -1685,7 +1761,7 @@ static inline void wctdm_voicedaa_check_hook(struct wctdm *wc, int card) if (fxo->neonmwi_state && 0 < fxo->neonmwi_offcounter ) { fxo->neonmwi_offcounter--; if (0 == fxo->neonmwi_offcounter) { - dahdi_qevent_lock(wc->chans[card], DAHDI_EVENT_NEONMWI_INACTIVE); + dahdi_qevent_lock(wc->aspan->span.chans[card], DAHDI_EVENT_NEONMWI_INACTIVE); fxo->neonmwi_state = 0; if (debug) dev_info(&wc->vb.pdev->dev, "NEON MWI cleared for card %d\n", card+1); @@ -1711,7 +1787,7 @@ static void wctdm_fxs_off_hook(struct wctdm *wc, const int card) SLIC_LF_ACTIVE_FWD; break; } - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_OFFHOOK); + dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_OFFHOOK); if (robust) wctdm_init_proslic(wc, card, 1, 0, 1); fxs->oldrxhook = 1; @@ -1722,7 +1798,7 @@ static void wctdm_fxs_on_hook(struct wctdm *wc, const int card) struct fxs *const fxs = &wc->mods[card].fxs; if (debug & DEBUG_CARD) dev_info(&wc->vb.pdev->dev, "wctdm: Card %d Going on hook\n", card); - dahdi_hooksig(wc->chans[card], DAHDI_RXSIG_ONHOOK); + dahdi_hooksig(wc->aspan->span.chans[card], DAHDI_RXSIG_ONHOOK); fxs->oldrxhook = 0; } @@ -1772,14 +1848,14 @@ static inline void wctdm_vpm_check(struct wctdm *wc, int x) { if (wc->cmdq[x].isrshadow[0]) { if (debug & DEBUG_ECHOCAN) - dev_info(&wc->vb.pdev->dev, "VPM: Detected dtmf ON channel %02x on chip %d!\n", wc->cmdq[x].isrshadow[0], x - NUM_CARDS); + dev_info(&wc->vb.pdev->dev, "VPM: Detected dtmf ON channel %02x on chip %d!\n", wc->cmdq[x].isrshadow[0], x - NUM_MODULES); wc->sethook[x] = CMD_WR(0xb9, wc->cmdq[x].isrshadow[0]); wc->cmdq[x].isrshadow[0] = 0; /* Cancel most recent lookup, if there is one */ wc->cmdq[x].cmds[USER_COMMANDS+0] = 0x00000000; } else if (wc->cmdq[x].isrshadow[1]) { if (debug & DEBUG_ECHOCAN) - dev_info(&wc->vb.pdev->dev, "VPM: Detected dtmf OFF channel %02x on chip %d!\n", wc->cmdq[x].isrshadow[1], x - NUM_CARDS); + dev_info(&wc->vb.pdev->dev, "VPM: Detected dtmf OFF channel %02x on chip %d!\n", wc->cmdq[x].isrshadow[1], x - NUM_MODULES); wc->sethook[x] = CMD_WR(0xbd, wc->cmdq[x].isrshadow[1]); wc->cmdq[x].isrshadow[1] = 0; /* Cancel most recent lookup, if there is one */ @@ -1791,6 +1867,7 @@ static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *e struct dahdi_echocanparam *p, struct dahdi_echocan_state **ec) { struct wctdm *wc = chan->pvt; + struct wctdm_chan *wchan = container_of(chan, struct wctdm_chan, chan); const struct dahdi_echocan_ops *ops; const struct dahdi_echocan_features *features; @@ -1810,7 +1887,7 @@ static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *e return -EINVAL; } - *ec = wc->ec[chan->chanpos - 1]; + *ec = &wchan->ec; (*ec)->ops = ops; (*ec)->features = *features; @@ -1818,8 +1895,8 @@ static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *e int channel; int unit; - channel = (chan->chanpos - 1); - unit = (chan->chanpos - 1) & 0x3; + channel = wchan->timeslot; + unit = wchan->timeslot & 0x3; if (wc->vpm100 < 2) channel >>= 2; @@ -1835,7 +1912,7 @@ static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *e ADT_COMP_ALAW : ADT_COMP_ULAW; return vpmadt032_echocan_create(wc->vpmadt032, - chan->chanpos-1, comp, ecp, p); + wchan->timeslot, comp, ecp, p); } else { return -ENODEV; } @@ -1844,14 +1921,15 @@ static int echocan_create(struct dahdi_chan *chan, struct dahdi_echocanparams *e static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec) { struct wctdm *wc = chan->pvt; + struct wctdm_chan *wchan = container_of(chan, struct wctdm_chan, chan); memset(ec, 0, sizeof(*ec)); if (wc->vpm100) { int channel; int unit; - channel = (chan->chanpos - 1); - unit = (chan->chanpos - 1) & 0x3; + channel = wchan->timeslot; + unit = wchan->timeslot & 0x3; if (wc->vpm100 < 2) channel >>= 2; @@ -1860,7 +1938,7 @@ static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec unit, channel); wctdm_vpm_out(wc, unit, channel, 0x01); } else if (wc->vpmadt032) { - vpmadt032_echocan_free(wc->vpmadt032, chan->chanpos - 1, ec); + vpmadt032_echocan_free(wc->vpmadt032, wchan->timeslot, ec); } } @@ -1915,8 +1993,8 @@ static inline void wctdm_isr_misc(struct wctdm *wc) return; } - for (x=0;x<wc->cards;x++) { - if (wc->cardflag & (1 << x)) { + for (x = 0; x < wc->mods_per_board; x++) { + if (wc->modmap & (1 << x)) { if (wc->modtype[x] == MOD_TYPE_FXS) { wctdm_isr_misc_fxs(wc, x); } else if (wc->modtype[x] == MOD_TYPE_FXO) { @@ -1927,9 +2005,8 @@ static inline void wctdm_isr_misc(struct wctdm *wc) } } if (wc->vpm100 > 0) { - for (x=NUM_CARDS;x<NUM_CARDS+NUM_EC;x++) { + for (x = NUM_MODULES; x < NUM_MODULES+NUM_EC; x++) wctdm_vpm_check(wc, x); - } } } @@ -1954,6 +2031,55 @@ static void handle_transmit(struct voicebus *vb, struct list_head *buffers) } } +struct sframe_packet { + struct list_head node; + u8 sframe[SFRAME_SIZE]; +}; + +/** + * handle_hx8_bootmode_receive() - queue up the receive packet for later... + * + * This function is called from interrupt context and isn't optimal, but it's + * not the main code path. + */ +static void handle_hx8_bootmode_receive(struct wctdm *wc, const void *vbb) +{ + struct sframe_packet *frame; + + frame = kzalloc(sizeof(*frame), GFP_ATOMIC); + if (unlikely(!frame)) { + WARN_ON(1); + return; + } + + memcpy(frame->sframe, vbb, sizeof(frame->sframe)); + spin_lock(&wc->frame_list_lock); + list_add_tail(&frame->node, &wc->frame_list); + spin_unlock(&wc->frame_list_lock); + + /* Wake up anyone waiting for a new packet. */ + wake_up(&wc->regq); + return; +} + +static void handle_hx8_receive(struct voicebus *vb, struct list_head *buffers) +{ + struct wctdm *wc = container_of(vb, struct wctdm, vb); + struct vbb *vbb; + list_for_each_entry(vbb, buffers, entry) + handle_hx8_bootmode_receive(wc, vbb->data); +} + +static void handle_hx8_transmit(struct voicebus *vb, struct list_head *buffers) +{ + struct vbb *vbb, *n; + + list_for_each_entry_safe(vbb, n, buffers, entry) { + list_del(&vbb->entry); + kmem_cache_free(voicebus_vbb_cache, vbb); + } +} + static int wctdm_voicedaa_insane(struct wctdm *wc, int card) { int blah; @@ -1972,7 +2098,7 @@ static int wctdm_proslic_insane(struct wctdm *wc, int card) insane_report=0; blah = wctdm_getreg(wc, card, 0); - if (debug & DEBUG_CARD) + if (blah != 0xff && (debug & DEBUG_CARD)) dev_info(&wc->vb.pdev->dev, "ProSLIC on module %d, product %d, version %d\n", card, (blah & 0x30) >> 4, (blah & 0xf)); #if 0 @@ -2045,7 +2171,7 @@ static int wctdm_proslic_powerleak_test(struct wctdm *wc, int card) /* Wait for one second */ origjiffies = jiffies; - while((vbat = wctdm_getreg(wc, card, 82)) > 0x6) { + while ((vbat = wctdm_getreg(wc, card, 82)) > 0x6) { if ((jiffies - origjiffies) >= (HZ/2)) break;; } @@ -2079,7 +2205,7 @@ static int wctdm_powerup_proslic(struct wctdm *wc, int card, int fast) if (fast) return 0; - while((vbat = wctdm_getreg(wc, card, 82)) < 0xc0) { + while ((vbat = wctdm_getreg(wc, card, 82)) < 0xc0) { /* Wait no more than 500ms */ if ((jiffies - origjiffies) > HZ/2) { break; @@ -2110,25 +2236,6 @@ static int wctdm_powerup_proslic(struct wctdm *wc, int card, int fast) /* Engage DC-DC converter */ wctdm_setreg(wc, card, 93, 0x19 /* was 0x19 */); -#if 0 - origjiffies = jiffies; - while(0x80 & wctdm_getreg(wc, card, 93)) { - if ((jiffies - origjiffies) > 2 * HZ) { - dev_info(&wc->vb.pdev->dev, "Timeout waiting for DC-DC calibration on module %d\n", card); - return -1; - } - } - -#if 0 - /* Wait a full two seconds */ - while((jiffies - origjiffies) < 2 * HZ); - - /* Just check to be sure */ - vbat = wctdm_getreg(wc, card, 82); - dev_info(&wc->vb.pdev->dev, "ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n", - card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ))); -#endif -#endif return 0; } @@ -2147,8 +2254,8 @@ static int wctdm_proslic_manual_calibrate(struct wctdm *wc, int card) wctdm_setreg(wc, card, 96, 0x47); //(0x47) Calibrate common mode and differential DAC mode DAC + ILIM origjiffies=jiffies; - while( wctdm_getreg(wc,card,96)!=0 ){ - if((jiffies-origjiffies)>80) + while (wctdm_getreg(wc, card, 96) != 0) { + if ((jiffies-origjiffies) > 80) return -1; } //Initialized DR 98 and 99 to get consistant results. @@ -2158,7 +2265,7 @@ static int wctdm_proslic_manual_calibrate(struct wctdm *wc, int card) /*******************************This is also available as a function *******************************************/ // Delay 10ms origjiffies=jiffies; - while((jiffies-origjiffies)<1); + while ((jiffies-origjiffies) < 1); wctdm_proslic_setreg_indirect(wc, card, 88,0); wctdm_proslic_setreg_indirect(wc,card,89,0); wctdm_proslic_setreg_indirect(wc,card,90,0); @@ -2173,8 +2280,8 @@ static int wctdm_proslic_manual_calibrate(struct wctdm *wc, int card) { wctdm_setreg(wc, card, 98,i); origjiffies=jiffies; - while((jiffies-origjiffies)<4); - if((wctdm_getreg(wc,card,88)) == 0) + while ((jiffies-origjiffies) < 4); + if ((wctdm_getreg(wc, card, 88)) == 0) break; } // for @@ -2182,15 +2289,16 @@ static int wctdm_proslic_manual_calibrate(struct wctdm *wc, int card) { wctdm_setreg(wc, card, 99,i); origjiffies=jiffies; - while((jiffies-origjiffies)<4); - if((wctdm_getreg(wc,card,89)) == 0) + while ((jiffies-origjiffies) < 4); + if ((wctdm_getreg(wc, card, 89)) == 0) break; }//for /*******************************The preceding is the manual gain mismatch calibration****************************/ /**********************************The following is the longitudinal Balance Cal***********************************/ wctdm_setreg(wc,card,64,1); - while((jiffies-origjiffies)<10); // Sleep 100? + while ((jiffies-origjiffies) < 10) /* Sleep 100? */ + ; wctdm_setreg(wc, card, 64, 0); wctdm_setreg(wc, card, 23, 0x4); // enable interrupt for the balance Cal @@ -2220,7 +2328,7 @@ static int wctdm_proslic_calibrate(struct wctdm *wc, int card) /* Wait for it to finish */ origjiffies = jiffies; - while(wctdm_getreg(wc, card, 96)) { + while (wctdm_getreg(wc, card, 96)) { if ((jiffies - origjiffies) > 2 * HZ) { dev_notice(&wc->vb.pdev->dev, "Timeout waiting for calibration of module %d\n", card); return -1; @@ -2237,11 +2345,12 @@ static int wctdm_proslic_calibrate(struct wctdm *wc, int card) return 0; } -static void wait_just_a_bit(int foo) +void wait_just_a_bit(int foo) { long newjiffies; newjiffies = jiffies + foo; - while(jiffies < newjiffies); + while (jiffies < newjiffies) + ; } /********************************************************************* @@ -2355,18 +2464,36 @@ static int set_vmwi(struct wctdm *wc, int chan_idx) return 0; } +static void wctdm_voicedaa_set_ts(struct wctdm *wc, int card, int ts) +{ + wctdm_setreg(wc, card, 34, (ts * 8) & 0xff); + wctdm_setreg(wc, card, 35, (ts * 8) >> 8); + wctdm_setreg(wc, card, 36, (ts * 8) & 0xff); + wctdm_setreg(wc, card, 37, (ts * 8) >> 8); + + if (debug) + dev_info(&wc->vb.pdev->dev, "voicedaa: card %d new timeslot: %d\n", card + 1, ts); +} + static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual, int sane) { unsigned char reg16=0, reg26=0, reg30=0, reg31=0; + unsigned long flags; long newjiffies; - if (wc->modtype[card & 0xfc] == MOD_TYPE_QRV) return -2; + if ((wc->modtype[card & 0xfc] == MOD_TYPE_QRV) || + (wc->modtype[card & 0xfc] == MOD_TYPE_BRI)) + return -2; + spin_lock_irqsave(&wc->reglock, flags); wc->modtype[card] = MOD_TYPE_NONE; + spin_unlock_irqrestore(&wc->reglock, flags); /* Wait just a bit */ wait_just_a_bit(HZ/10); + spin_lock_irqsave(&wc->reglock, flags); wc->modtype[card] = MOD_TYPE_FXO; + spin_unlock_irqrestore(&wc->reglock, flags); wait_just_a_bit(HZ/10); if (!sane && wctdm_voicedaa_insane(wc, card)) @@ -2391,7 +2518,7 @@ static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual, reg16 |= (fxo_modes[_opermode].rt); wctdm_setreg(wc, card, 16, reg16); - if(fwringdetect || neonmwi_monitor) { + if (fwringdetect || neonmwi_monitor) { /* Enable ring detector full-wave rectifier mode */ wctdm_setreg(wc, card, 18, 2); wctdm_setreg(wc, card, 24, 0); @@ -2421,11 +2548,7 @@ static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual, reg31 |= (fxo_modes[_opermode].ohs2 << 3); wctdm_setreg(wc, card, 31, reg31); - /* Set Transmit/Receive timeslot */ - wctdm_setreg(wc, card, 34, (card * 8) & 0xff); - wctdm_setreg(wc, card, 35, (card * 8) >> 8); - wctdm_setreg(wc, card, 36, (card * 8) & 0xff); - wctdm_setreg(wc, card, 37, (card * 8) >> 8); + wctdm_voicedaa_set_ts(wc, card, card); /* Enable ISO-Cap */ wctdm_setreg(wc, card, 6, 0x00); @@ -2433,7 +2556,7 @@ static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual, /* Wait 1000ms for ISO-cap to come up */ newjiffies = jiffies; newjiffies += 2 * HZ; - while((jiffies < newjiffies) && !(wctdm_getreg(wc, card, 11) & 0xf0)) + while ((jiffies < newjiffies) && !(wctdm_getreg(wc, card, 11) & 0xf0)) wait_just_a_bit(HZ/10); if (!(wctdm_getreg(wc, card, 11) & 0xf0)) { @@ -2451,23 +2574,41 @@ static int wctdm_init_voicedaa(struct wctdm *wc, int card, int fast, int manual, wctdm_set_hwgain(wc, card, fxotxgain, 1); wctdm_set_hwgain(wc, card, fxorxgain, 0); - if(debug) + if (debug) dev_info(&wc->vb.pdev->dev, "DEBUG fxotxgain:%i.%i fxorxgain:%i.%i\n", (wctdm_getreg(wc, card, 38)/16) ? -(wctdm_getreg(wc, card, 38) - 16) : wctdm_getreg(wc, card, 38), (wctdm_getreg(wc, card, 40)/16) ? -(wctdm_getreg(wc, card, 40) - 16) : wctdm_getreg(wc, card, 40), (wctdm_getreg(wc, card, 39)/16) ? -(wctdm_getreg(wc, card, 39) - 16): wctdm_getreg(wc, card, 39), (wctdm_getreg(wc, card, 41)/16)?-(wctdm_getreg(wc, card, 41) - 16) : wctdm_getreg(wc, card, 41)); return 0; } +static void wctdm_proslic_set_ts(struct wctdm *wc, int card, int ts) +{ + wctdm_setreg(wc, card, 2, (ts * 8) & 0xff); /* Tx Start count low byte 0 */ + wctdm_setreg(wc, card, 3, (ts * 8) >> 8); /* Tx Start count high byte 0 */ + wctdm_setreg(wc, card, 4, (ts * 8) & 0xff); /* Rx Start count low byte 0 */ + wctdm_setreg(wc, card, 5, (ts * 8) >> 8); /* Rx Start count high byte 0 */ + + if (debug) + dev_info(&wc->vb.pdev->dev, "proslic: card %d new timeslot: %d\n", card + 1, ts); +} + static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, int sane) { unsigned short tmp[5]; + unsigned long flags; unsigned char r19,r9; int x; int fxsmode=0; if (wc->modtype[card & 0xfc] == MOD_TYPE_QRV) return -2; + spin_lock_irqsave(&wc->reglock, flags); + wc->modtype[card] = MOD_TYPE_FXS; + spin_unlock_irqrestore(&wc->reglock, flags); + + wait_just_a_bit(HZ/10); + /* Sanity check the ProSLIC */ if (!sane && wctdm_proslic_insane(wc, card)) return -2; @@ -2537,7 +2678,7 @@ static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, } #ifndef NO_CALIBRATION /* Perform calibration */ - if(manual) { + if (manual) { if (wctdm_proslic_manual_calibrate(wc, card)) { //dev_notice(&wc->vb.pdev->dev, "Proslic failed on Manual Calibration\n"); if (wctdm_proslic_manual_calibrate(wc, card)) { @@ -2548,7 +2689,7 @@ static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, } } else { - if(wctdm_proslic_calibrate(wc, card)) { + if (wctdm_proslic_calibrate(wc, card)) { //dev_notice(&wc->vb.pdev->dev, "ProSlic died on Auto Calibration.\n"); if (wctdm_proslic_calibrate(wc, card)) { dev_notice(&wc->vb.pdev->dev, "Proslic Failed on Second Attempt to Auto Calibrate\n"); @@ -2603,11 +2744,10 @@ static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, wctdm_setreg(wc, card, 1, 0x20); else wctdm_setreg(wc, card, 1, 0x28); - // U-Law 8-bit interface - wctdm_setreg(wc, card, 2, (card * 8) & 0xff); // Tx Start count low byte 0 - wctdm_setreg(wc, card, 3, (card * 8) >> 8); // Tx Start count high byte 0 - wctdm_setreg(wc, card, 4, (card * 8) & 0xff); // Rx Start count low byte 0 - wctdm_setreg(wc, card, 5, (card * 8) >> 8); // Rx Start count high byte 0 + + /* U-Law 8-bit interface */ + wctdm_proslic_set_ts(wc, card, card); + wctdm_setreg(wc, card, 18, 0xff); // clear all interrupt wctdm_setreg(wc, card, 19, 0xff); wctdm_setreg(wc, card, 20, 0xff); @@ -2707,10 +2847,23 @@ static int wctdm_init_proslic(struct wctdm *wc, int card, int fast, int manual, return 0; } +static void wctdm_qrvdri_set_ts(struct wctdm *wc, int card, int ts) +{ + wctdm_setreg(wc, card, 0x13, ts + 0x80); /* codec 2 tx, ts0 */ + wctdm_setreg(wc, card, 0x17, ts + 0x80); /* codec 0 rx, ts0 */ + wctdm_setreg(wc, card, 0x14, ts + 0x81); /* codec 1 tx, ts1 */ + wctdm_setreg(wc, card, 0x18, ts + 0x81); /* codec 1 rx, ts1 */ + + if (debug) + dev_info(&wc->vb.pdev->dev, "qrvdri: card %d new timeslot: %d\n", card + 1, ts); +} + static int wctdm_init_qrvdri(struct wctdm *wc, int card) { unsigned char x,y; - unsigned long endjif; + + if (MOD_TYPE_BRI == wc->modtype[card & 0xfc]) + return -2; /* have to set this, at least for now */ wc->modtype[card] = MOD_TYPE_QRV; @@ -2743,18 +2896,16 @@ static int wctdm_init_qrvdri(struct wctdm *wc, int card) wc->modtype[card] = MOD_TYPE_NONE; return 1; } - for(x = 0; x < 0x30; x++) + for (x = 0; x < 0x30; x++) { if ((x >= 0x1c) && (x <= 0x1e)) wctdm_setreg(wc,card,x,0xff); else wctdm_setreg(wc,card,x,0); } wctdm_setreg(wc,card,0,0x80); - endjif = jiffies + (HZ/10); - while(endjif > jiffies); + msleep(100); wctdm_setreg(wc,card,0,0x10); wctdm_setreg(wc,card,0,0x10); - endjif = jiffies + (HZ/10); - while(endjif > jiffies); + msleep(100); /* set up modes */ wctdm_setreg(wc,card,0,0x1c); /* set up I/O directions */ @@ -2765,11 +2916,9 @@ static int wctdm_init_qrvdri(struct wctdm *wc, int card) wctdm_setreg(wc,card,3,0x11); /* D0-7 */ wctdm_setreg(wc,card,4,0xa); /* D8-11 */ wctdm_setreg(wc,card,7,0); /* CS outputs */ - /* set up timeslots */ - wctdm_setreg(wc,card,0x13,card + 0x80); /* codec 2 tx, ts0 */ - wctdm_setreg(wc,card,0x17,card + 0x80); /* codec 0 rx, ts0 */ - wctdm_setreg(wc,card,0x14,card + 0x81); /* codec 1 tx, ts1 */ - wctdm_setreg(wc,card,0x18,card + 0x81); /* codec 1 rx, ts1 */ + + wctdm_qrvdri_set_ts(wc, card, card); + /* set up for max gains */ wctdm_setreg(wc,card,0x26,0x24); wctdm_setreg(wc,card,0x27,0x24); @@ -2782,9 +2931,9 @@ static int wctdm_init_qrvdri(struct wctdm *wc, int card) static void qrv_dosetup(struct dahdi_chan *chan,struct wctdm *wc) { -int qrvcard; -unsigned char r; -long l; + int qrvcard; + unsigned char r; + long l; /* actually do something with the values */ qrvcard = (chan->chanpos - 1) & 0xfc; @@ -2839,6 +2988,7 @@ long l; return; } + static int wctdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data) { struct wctdm_stats stats; @@ -3159,19 +3309,29 @@ static int wctdm_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long static int wctdm_open(struct dahdi_chan *chan) { - struct wctdm *wc = chan->pvt; - int channo = chan->chanpos - 1; + struct wctdm *wc; + int channo; unsigned long flags; - if (!(wc->cardflag & (1 << (chan->chanpos - 1)))) + wc = chan->pvt; + channo = chan->chanpos - 1; + +#if 0 + if (!(wc->modmap & (1 << (chan->chanpos - 1)))) + return -ENODEV; + if (wc->dead) return -ENODEV; +#endif + wc->usecount++; - /* Reset the mwi indicators */ - spin_lock_irqsave(&wc->reglock, flags); - wc->mods[channo].fxo.neonmwi_debounce = 0; - wc->mods[channo].fxo.neonmwi_offcounter = 0; - wc->mods[channo].fxo.neonmwi_state = 0; - spin_unlock_irqrestore(&wc->reglock, flags); + if (wc->modtype[channo] == MOD_TYPE_FXO) { + /* Reset the mwi indicators */ + spin_lock_irqsave(&wc->reglock, flags); + wc->mods[channo].fxo.neonmwi_debounce = 0; + wc->mods[channo].fxo.neonmwi_offcounter = 0; + wc->mods[channo].fxo.neonmwi_state = 0; + spin_unlock_irqrestore(&wc->reglock, flags); + } return 0; } @@ -3185,17 +3345,17 @@ static int wctdm_watchdog(struct dahdi_span *span, int event) static int wctdm_close(struct dahdi_chan *chan) { - struct wctdm *wc = chan->pvt; + struct wctdm *wc; int x; signed char reg; - for (x=0;x<wc->cards;x++) { - if (wc->modtype[x] == MOD_TYPE_FXS) { + + wc = chan->pvt; + for (x = 0; x < wc->mods_per_board; x++) { + if (MOD_TYPE_FXS == wc->modtype[x]) { wc->mods[x].fxs.idletxhookstate = POLARITY_XOR(x) ? SLIC_LF_ACTIVE_REV : SLIC_LF_ACTIVE_FWD; - } - if (wc->modtype[x] == MOD_TYPE_QRV) - { + } else if (MOD_TYPE_QRV == wc->modtype[x]) { int qrvcard = x & 0xfc; wc->qrvhook[x] = 0; @@ -3206,8 +3366,10 @@ static int wctdm_close(struct dahdi_chan *chan) wc->txgain[x] = 3599; wc->rxgain[x] = 1199; reg = 0; - if (!wc->qrvhook[qrvcard]) reg |= 1; - if (!wc->qrvhook[qrvcard + 1]) reg |= 0x10; + if (!wc->qrvhook[qrvcard]) + reg |= 1; + if (!wc->qrvhook[qrvcard + 1]) + reg |= 0x10; wc->sethook[qrvcard] = CMD_WR(3, reg); qrv_dosetup(chan,wc); } @@ -3219,8 +3381,8 @@ static int wctdm_close(struct dahdi_chan *chan) static int wctdm_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) { struct wctdm *wc = chan->pvt; + int reg = 0, qrvcard; - int reg=0,qrvcard; if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_QRV) { qrvcard = (chan->chanpos - 1) & 0xfc; switch(txsig) { @@ -3255,7 +3417,7 @@ static int wctdm_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) default: dev_notice(&wc->vb.pdev->dev, "wctdm24xxp: Can't set tx state to %d\n", txsig); } - } else { /* Else this is an fxs port */ + } else if (wc->modtype[chan->chanpos - 1] == MOD_TYPE_FXS) { unsigned long flags; struct fxs *const fxs = &wc->mods[chan->chanpos - 1].fxs; spin_lock_irqsave(&fxs->lasttxhooklock, flags); @@ -3309,6 +3471,7 @@ static int wctdm_hooksig(struct dahdi_chan *chan, enum dahdi_txsig txsig) spin_unlock_irqrestore(&fxs->lasttxhooklock, flags); if (debug & DEBUG_CARD) dev_info(&wc->vb.pdev->dev, "Setting FXS hook state to %d (%02x)\n", txsig, reg); + } else { } return 0; } @@ -3337,7 +3500,7 @@ static void wctdm_dacs_connect(struct wctdm *wc, int srccard, int dstcard) /* proslic */ wctdm_setreg(wc, srccard, PCM_XMIT_START_COUNT_LSB, ((srccard+24) * 8) & 0xff); wctdm_setreg(wc, srccard, PCM_XMIT_START_COUNT_MSB, ((srccard+24) * 8) >> 8); - } else if(wc->modtype[srccard] == MOD_TYPE_FXO) { + } else if (wc->modtype[srccard] == MOD_TYPE_FXO) { /* daa */ wctdm_setreg(wc, srccard, 34, ((srccard+24) * 8) & 0xff); /* TX */ wctdm_setreg(wc, srccard, 35, ((srccard+24) * 8) >> 8); /* TX */ @@ -3348,7 +3511,7 @@ static void wctdm_dacs_connect(struct wctdm *wc, int srccard, int dstcard) /* proslic */ wctdm_setreg(wc, dstcard, PCM_RCV_START_COUNT_LSB, ((srccard+24) * 8) & 0xff); wctdm_setreg(wc, dstcard, PCM_RCV_START_COUNT_MSB, ((srccard+24) * 8) >> 8); - } else if(wc->modtype[dstcard] == MOD_TYPE_FXO) { + } else if (wc->modtype[dstcard] == MOD_TYPE_FXO) { /* daa */ wctdm_setreg(wc, dstcard, 36, ((srccard+24) * 8) & 0xff); /* RX */ wctdm_setreg(wc, dstcard, 37, ((srccard+24) * 8) >> 8); /* RX */ @@ -3363,10 +3526,10 @@ static void wctdm_dacs_disconnect(struct wctdm *wc, int card) dev_info(&wc->vb.pdev->dev, "wctdm_dacs_disconnect: restoring TX for %d and RX for %d\n",wc->dacssrc[card], card); /* restore TX (source card) */ - if(wc->modtype[wc->dacssrc[card]] == MOD_TYPE_FXS){ + if (wc->modtype[wc->dacssrc[card]] == MOD_TYPE_FXS) { wctdm_setreg(wc, wc->dacssrc[card], PCM_XMIT_START_COUNT_LSB, (wc->dacssrc[card] * 8) & 0xff); wctdm_setreg(wc, wc->dacssrc[card], PCM_XMIT_START_COUNT_MSB, (wc->dacssrc[card] * 8) >> 8); - } else if(wc->modtype[wc->dacssrc[card]] == MOD_TYPE_FXO){ + } else if (wc->modtype[wc->dacssrc[card]] == MOD_TYPE_FXO) { wctdm_setreg(wc, card, 34, (card * 8) & 0xff); wctdm_setreg(wc, card, 35, (card * 8) >> 8); } else { @@ -3374,10 +3537,10 @@ static void wctdm_dacs_disconnect(struct wctdm *wc, int card) } /* restore RX (this card) */ - if(wc->modtype[card] == MOD_TYPE_FXS){ - wctdm_setreg(wc, card, PCM_RCV_START_COUNT_LSB, (card * 8) & 0xff); - wctdm_setreg(wc, card, PCM_RCV_START_COUNT_MSB, (card * 8) >> 8); - } else if(wc->modtype[card] == MOD_TYPE_FXO){ + if (MOD_TYPE_FXS == wc->modtype[card]) { + wctdm_setreg(wc, card, PCM_RCV_START_COUNT_LSB, (card * 8) & 0xff); + wctdm_setreg(wc, card, PCM_RCV_START_COUNT_MSB, (card * 8) >> 8); + } else if (MOD_TYPE_FXO == wc->modtype[card]) { wctdm_setreg(wc, card, 36, (card * 8) & 0xff); wctdm_setreg(wc, card, 37, (card * 8) >> 8); } else { @@ -3392,12 +3555,12 @@ static int wctdm_dacs(struct dahdi_chan *dst, struct dahdi_chan *src) { struct wctdm *wc; - if(!nativebridge) + if (!nativebridge) return 0; /* should this return -1 since unsuccessful? */ wc = dst->pvt; - if(src) { + if (src) { wctdm_dacs_connect(wc, src->chanpos - 1, dst->chanpos - 1); if (debug) dev_info(&wc->vb.pdev->dev, "dacs connecct: %d -> %d!\n\n", src->chanpos, dst->chanpos); @@ -3409,77 +3572,177 @@ static int wctdm_dacs(struct dahdi_chan *dst, struct dahdi_chan *src) return 0; } -static int wctdm_initialize(struct wctdm *wc) +static struct wctdm_chan *wctdm_init_chan(struct wctdm *wc, struct wctdm_span *s, int chanoffset, int channo) +{ + struct wctdm_chan *c; + + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + return NULL; + + /* Do not change the procfs representation for non-hx8 cards. */ + if (is_digital_span(s)) { + sprintf(c->chan.name, "WCBRI/%d/%d/%d", wc->pos, s->spanno, + channo); + } else { + sprintf(c->chan.name, "WCTDM/%d/%d", wc->pos, channo); + } + + c->chan.chanpos = channo+1; + c->chan.span = &s->span; + c->chan.pvt = wc; + c->timeslot = chanoffset + channo; + return c; +} + +#if 0 +/** + * wctdm_span_count() - Return the number of spans exported by this board. + * + * This is only called during initialization so let's just count the spans each + * time we need this information as opposed to storing another variable in the + * wctdm structure. + */ +static int wctdm_span_count(const struct wctdm *wc) +{ + int i; + int count = 0; + for (i = 0; i < MAX_SPANS; ++i) { + if (wc->spans[i]) + ++count; + } + return count; +} +#endif + +static struct wctdm_span *wctdm_init_span(struct wctdm *wc, int spanno, int chanoffset, int chancount, int digital_span) { int x; + static int first = 1; struct pci_dev *pdev = wc->vb.pdev; + struct wctdm_chan *c; + struct wctdm_span *s; + static int spancount; + + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) + return NULL; /* DAHDI stuff */ - sprintf(wc->span.name, "WCTDM/%d", wc->pos); - snprintf(wc->span.desc, sizeof(wc->span.desc) - 1, - "%s Board %d", wc->desc->name, wc->pos + 1); - snprintf(wc->span.location, sizeof(wc->span.location) - 1, + s->span.owner = THIS_MODULE; + s->span.offset = spanno; + + s->spanno = spancount++; + + /* Do not change the procfs representation for non-hx8 cards. */ + if (digital_span) + sprintf(s->span.name, "WCBRI/%d/%d", wc->pos, s->spanno); + else + sprintf(s->span.name, "WCTDM/%d", wc->pos); + + snprintf(s->span.desc, sizeof(s->span.desc) - 1, "%s Board %d", wc->desc->name, wc->pos + 1); + snprintf(s->span.location, sizeof(s->span.location) - 1, "PCI%s Bus %02d Slot %02d", (wc->flags[0] & FLAG_EXPRESS) ? " Express" : "", pdev->bus->number, PCI_SLOT(pdev->devfn) + 1); - wc->span.manufacturer = "Digium"; - strncpy(wc->span.devicetype, wc->desc->name, - sizeof(wc->span.devicetype) - 1); + s->span.manufacturer = "Digium"; + strncpy(s->span.devicetype, wc->desc->name, sizeof(s->span.devicetype) - 1); + if (alawoverride) { - dev_info(&wc->vb.pdev->dev, "ALAW override parameter detected. Device will be operating in ALAW\n"); - wc->span.deflaw = DAHDI_LAW_ALAW; + s->span.deflaw = DAHDI_LAW_ALAW; + if (first) { + dev_info(&wc->vb.pdev->dev, "ALAW override parameter detected. Device will be operating in ALAW\n"); + first = 0; + } + } else if (digital_span) { + /* BRIs are in A-law */ + s->span.deflaw = DAHDI_LAW_ALAW; + } else { + /* Analog mods are ulaw unless alawoverride is used */ + s->span.deflaw = DAHDI_LAW_MULAW; + } + + if (digital_span) { + s->span.hdlc_hard_xmit = wctdm_hdlc_hard_xmit; + s->span.spanconfig = b400m_spanconfig; + s->span.chanconfig = b400m_chanconfig; + s->span.linecompat = DAHDI_CONFIG_AMI | DAHDI_CONFIG_B8ZS | DAHDI_CONFIG_D4; + s->span.linecompat |= DAHDI_CONFIG_ESF | DAHDI_CONFIG_HDB3 | DAHDI_CONFIG_CCS | DAHDI_CONFIG_CRC4; + s->span.linecompat |= DAHDI_CONFIG_NTTE | DAHDI_CONFIG_TERM; } else { - wc->span.deflaw = DAHDI_LAW_MULAW; - } - for (x=0;x<wc->cards;x++) { - sprintf(wc->chans[x]->name, "WCTDM/%d/%d", wc->pos, x); - wc->chans[x]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; - wc->chans[x]->sigcap |= DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; - wc->chans[x]->chanpos = x+1; - wc->chans[x]->pvt = wc; - } - wc->span.owner = THIS_MODULE; - wc->span.chans = wc->chans; - wc->span.channels = wc->desc->ports; - wc->span.irq = pdev->irq; - wc->span.hooksig = wctdm_hooksig; - wc->span.open = wctdm_open; - wc->span.close = wctdm_close; - wc->span.flags = DAHDI_FLAG_RBS; - wc->span.ioctl = wctdm_ioctl; - wc->span.watchdog = wctdm_watchdog; - wc->span.dacs= wctdm_dacs; + s->span.hooksig = wctdm_hooksig; + s->span.flags = DAHDI_FLAG_RBS; + /* analog sigcap handled in fixup_analog_span() */ + } + + s->span.chans = kmalloc(sizeof(struct dahdi_chan *) * chancount, GFP_KERNEL); + if (!s->span.chans) + return NULL; + + /* allocate channels for the span */ + for (x = 0; x < chancount; x++) { + c = wctdm_init_chan(wc, s, chanoffset, x); + if (!c) + return NULL; + wc->chans[chanoffset + x] = c; + s->span.chans[x] = &c->chan; + } + + s->span.channels = chancount; + s->span.irq = pdev->irq; + s->span.open = wctdm_open; + s->span.close = wctdm_close; + s->span.ioctl = wctdm_ioctl; + s->span.watchdog = wctdm_watchdog; + s->span.dacs = wctdm_dacs; + + if (digital_span) { + wc->chans[chanoffset + 0]->chan.sigcap = DAHDI_SIG_CLEAR; + wc->chans[chanoffset + 1]->chan.sigcap = DAHDI_SIG_CLEAR; + wc->chans[chanoffset + 2]->chan.sigcap = DAHDI_SIG_HARDHDLC; + } #ifdef VPM_SUPPORT if (vpmsupport) - wc->span.echocan_create = echocan_create; + s->span.echocan_create = echocan_create; #endif - init_waitqueue_head(&wc->span.maintq); - wc->span.pvt = wc; - return 0; + init_waitqueue_head(&s->span.maintq); + wc->spans[spanno] = s; + return s; } -static void wctdm_post_initialize(struct wctdm *wc) +static void wctdm_fixup_analog_span(struct wctdm *wc, int spanno) { - int x; + struct dahdi_span *s; + int x, y; /* Finalize signalling */ - for (x = 0; x <wc->cards; x++) { - if (wc->cardflag & (1 << x)) { - if (wc->modtype[x] == MOD_TYPE_FXO) - wc->chans[x]->sigcap = DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; - else if (wc->modtype[x] == MOD_TYPE_FXS) - wc->chans[x]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; - else if (wc->modtype[x] == MOD_TYPE_QRV) - wc->chans[x]->sigcap = DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; - } else if (!(wc->chans[x]->sigcap & DAHDI_SIG_BROKEN)) { - wc->chans[x]->sigcap = 0; + y = 0; + s = &wc->spans[spanno]->span; + + for (x = 0; x < wc->desc->ports; x++) { + if (debug) { + dev_info(&wc->vb.pdev->dev, + "fixup_analog: x=%d, y=%d modtype=%d, " + "s->chans[%d]=%p\n", x, y, wc->modtype[x], + y, s->chans[y]); } + if (wc->modtype[x] == MOD_TYPE_FXO) + s->chans[y++]->sigcap = DAHDI_SIG_FXSKS | DAHDI_SIG_FXSLS | DAHDI_SIG_SF | DAHDI_SIG_CLEAR; + else if (wc->modtype[x] == MOD_TYPE_FXS) + s->chans[y++]->sigcap = DAHDI_SIG_FXOKS | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; + else if (wc->modtype[x] == MOD_TYPE_QRV) + s->chans[y++]->sigcap = DAHDI_SIG_SF | DAHDI_SIG_EM | DAHDI_SIG_CLEAR; + else + s->chans[y++]->sigcap = 0; } - if (wc->vpm100) { - strncat(wc->span.devicetype, " (VPM100M)", sizeof(wc->span.devicetype) - 1); - } else if (wc->vpmadt032) { - strncat(wc->span.devicetype, " (VPMADT032)", sizeof(wc->span.devicetype) - 1); + for (x = 0; x < MAX_SPANS; x++) { + if (!wc->spans[x]) + continue; + if (wc->vpm100) + strncat(wc->spans[x]->span.devicetype, " (VPM100M)", sizeof(wc->spans[x]->span.devicetype) - 1); + else if (wc->vpmadt032) + strncat(wc->spans[x]->span.devicetype, " (VPMADT032)", sizeof(wc->spans[x]->span.devicetype) - 1); } } @@ -3684,173 +3947,659 @@ static void get_default_portconfig(GpakPortConfig_t *portconfig) portconfig->EightSlotMask3 = 0x0000; } -static int wctdm_locate_modules(struct wctdm *wc) +static int wctdm_initialize_vpmadt032(struct wctdm *wc) +{ + int x; + int res; + unsigned long flags; + struct vpmadt032_options options; + + GpakPortConfig_t portconfig; + + spin_lock_irqsave(&wc->reglock, flags); + for (x = NUM_MODULES; x < NUM_MODULES + NUM_EC; x++) + wc->modtype[x] = MOD_TYPE_NONE; + spin_unlock_irqrestore(&wc->reglock, flags); + + options.debug = debug; + options.vpmnlptype = vpmnlptype; + options.vpmnlpthresh = vpmnlpthresh; + options.vpmnlpmaxsupp = vpmnlpmaxsupp; + options.channels = wc->avchannels; + + wc->vpmadt032 = vpmadt032_alloc(&options, wc->board_name); + if (!wc->vpmadt032) + return -ENOMEM; + + wc->vpmadt032->setchanconfig_from_state = setchanconfig_from_state; + /* wc->vpmadt032->context = wc; */ + /* Pull the configuration information from the span holding + * the analog channels. */ + get_default_portconfig(&portconfig); + res = vpmadt032_init(wc->vpmadt032, &wc->vb); + if (res) { + vpmadt032_free(wc->vpmadt032); + wc->vpmadt032 = NULL; + return res; + } + + /* Now we need to configure the VPMADT032 module for this + * particular board. */ + res = config_vpmadt032(wc->vpmadt032, wc); + if (res) { + vpmadt032_free(wc->vpmadt032); + wc->vpmadt032 = NULL; + return res; + } + + return 0; +} + +static int wctdm_initialize_vpm(struct wctdm *wc) +{ + int res = 0; + + if (!vpmsupport) { + dev_notice(&wc->vb.pdev->dev, "VPM: Support Disabled\n"); + } else if (!wctdm_vpm_init(wc)) { + dev_info(&wc->vb.pdev->dev, "VPM: Present and operational (Rev %c)\n", + 'A' + wc->vpm100 - 1); + wc->ctlreg |= 0x10; + } else { + res = wctdm_initialize_vpmadt032(wc); + if (!res) + wc->ctlreg |= 0x10; + } + return res; +} + +static int wctdm_identify_modules(struct wctdm *wc) { int x; unsigned long flags; wc->ctlreg = 0x00; - + /* Make sure all units go into daisy chain mode */ spin_lock_irqsave(&wc->reglock, flags); - wc->span.irqmisses = 0; - for (x=0;x<wc->cards;x++) + +/* + * This looks a little weird. + * + * There are only 8 physical ports on the TDM/AEX800, but the code immediately + * below sets 24 modules up. This has to do with the altcs magic that allows us + * to have single-port and quad-port modules on these products. + * The variable "mods_per_board" is set to the appropriate value just below the + * next code block. + * + * Now why this is important: + * The FXS modules come out of reset in a two-byte, non-chainable SPI mode. + * This is currently incompatible with how we do things, so we need to set + * them to a chained, 3-byte command mode. This is done by setting the module + * type to MOD_TYPE_FXSINIT for a little while so that cmd_dequeue will + * initialize the SLIC into the appropriate mode. + * + * This "go to 3-byte chained mode" command, however, wreaks havoc with HybridBRI. + * + * The solution: + * Since HybridBRI is only designed to work in an 8-port card, and since the single-port + * modules "show up" in SPI slots >= 8 in these cards, we only set SPI slots 8-23 to + * MOD_TYPE_FXSINIT. The HybridBRI will never see the command that causes it to freak + * out and the single-port FXS cards get what they need so that when we probe with altcs + * we see them. + */ + + for (x = 0; x < wc->mods_per_board; x++) wc->modtype[x] = MOD_TYPE_FXSINIT; + wc->vpm100 = -1; - for (x = wc->cards; x < wc->cards+NUM_EC; x++) + for (x = wc->mods_per_board; x < wc->mods_per_board+NUM_EC; x++) wc->modtype[x] = MOD_TYPE_VPM; + spin_unlock_irqrestore(&wc->reglock, flags); - /* Wait just a bit */ - for (x=0;x<10;x++) + +/* Wait just a bit; this makes sure that cmd_dequeue is emitting SPI commands in the appropriate mode(s). */ + for (x = 0; x < 10; x++) schluffen(&wc->regq); - spin_lock_irqsave(&wc->reglock, flags); - for (x=0;x<wc->cards;x++) - wc->modtype[x] = MOD_TYPE_FXS; - spin_unlock_irqrestore(&wc->reglock, flags); -#if 0 - /* XXX */ - cmddesc = 0; -#endif - /* Now that all the cards have been reset, we can stop checking them all if there aren't as many */ +/* Now that all the cards have been reset, we can stop checking them all if there aren't as many */ spin_lock_irqsave(&wc->reglock, flags); - wc->cards = wc->desc->ports; + wc->mods_per_board = wc->desc->ports; spin_unlock_irqrestore(&wc->reglock, flags); /* Reset modules */ - for (x=0;x<wc->cards;x++) { - int sane=0,ret=0,readi=0; + for (x = 0; x < wc->mods_per_board; x++) { + int sane = 0, ret = 0, readi = 0; if (fatal_signal_pending(current)) break; retry: - /* Init with Auto Calibration */ if (!(ret = wctdm_init_proslic(wc, x, 0, 0, sane))) { - wc->cardflag |= (1 << x); + wc->modmap |= (1 << x); if (debug & DEBUG_CARD) { readi = wctdm_getreg(wc,x,LOOP_I_LIMIT); - dev_info(&wc->vb.pdev->dev, "Proslic module %d loop current is %dmA\n",x, - ((readi*3)+20)); + dev_info(&wc->vb.pdev->dev, "Proslic module %d loop current is %dmA\n", x, ((readi*3)+20)); } dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- AUTO FXS/DPO\n", x + 1); } else { - if(ret!=-2) { - sane=1; + if (ret != -2) { + sane = 1; /* Init with Manual Calibration */ if (!wctdm_init_proslic(wc, x, 0, 1, sane)) { - wc->cardflag |= (1 << x); - if (debug & DEBUG_CARD) { - readi = wctdm_getreg(wc,x,LOOP_I_LIMIT); - dev_info(&wc->vb.pdev->dev, "Proslic module %d loop current is %dmA\n",x, - ((readi*3)+20)); - } + wc->modmap |= (1 << x); + + if (debug & DEBUG_CARD) { + readi = wctdm_getreg(wc, x, LOOP_I_LIMIT); + dev_info(&wc->vb.pdev->dev, "Proslic module %d loop current is %dmA\n", x, ((readi*3)+20)); + } + dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- MANUAL FXS\n",x + 1); } else { dev_notice(&wc->vb.pdev->dev, "Port %d: FAILED FXS (%s)\n", x + 1, fxshonormode ? fxo_modes[_opermode].name : "FCC"); - wc->chans[x]->sigcap = DAHDI_SIG_BROKEN | __DAHDI_SIG_FXO; - } + } + } else if (!(ret = wctdm_init_voicedaa(wc, x, 0, 0, sane))) { - wc->cardflag |= (1 << x); - dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- AUTO FXO (%s mode)\n",x + 1, fxo_modes[_opermode].name); - } else if (!wctdm_init_qrvdri(wc,x)) { - wc->cardflag |= 1 << x; - dev_info(&wc->vb.pdev->dev, "Port %d: Installed -- QRV DRI card\n",x + 1); + wc->modmap |= (1 << x); + dev_info(&wc->vb.pdev->dev, + "Port %d: Installed -- AUTO FXO " + "(%s mode)\n", x + 1, + fxo_modes[_opermode].name); + } else if (!wctdm_init_qrvdri(wc, x)) { + wc->modmap |= 1 << x; + dev_info(&wc->vb.pdev->dev, + "Port %d: Installed -- QRV DRI card\n", x + 1); + } else if (is_hx8(wc) && !wctdm_init_b400m(wc, x)) { + wc->modmap |= (1 << x); + dev_info(&wc->vb.pdev->dev, + "Port %d: Installed -- XHFC " + "quad-span module\n", x + 1); } else { - if ((wc->desc->ports != 24) && - ((x & 0x3) == 1) && !wc->altcs[x]) { + if ((wc->desc->ports != 24) && ((x & 0x3) == 1) && !wc->altcs[x]) { spin_lock_irqsave(&wc->reglock, flags); wc->altcs[x] = 2; + if (wc->desc->ports == 4) { wc->altcs[x+1] = 3; wc->altcs[x+2] = 3; } - wc->modtype[x] = MOD_TYPE_FXSINIT; - spin_unlock_irqrestore(&wc->reglock, flags); - - schluffen(&wc->regq); - schluffen(&wc->regq); - spin_lock_irqsave(&wc->reglock, flags); - wc->modtype[x] = MOD_TYPE_FXS; - spin_unlock_irqrestore(&wc->reglock, flags); - if (debug & DEBUG_CARD) - dev_info(&wc->vb.pdev->dev, "Trying port %d with alternate chip select\n", x + 1); - goto retry; + + wc->modtype[x] = MOD_TYPE_FXSINIT; + spin_unlock_irqrestore(&wc->reglock, flags); + + schluffen(&wc->regq); + schluffen(&wc->regq); + + spin_lock_irqsave(&wc->reglock, flags); + wc->modtype[x] = MOD_TYPE_FXS; + spin_unlock_irqrestore(&wc->reglock, flags); + + if (debug & DEBUG_CARD) + dev_info(&wc->vb.pdev->dev, "Trying port %d with alternate chip select\n", x + 1); + goto retry; + } else { - dev_notice(&wc->vb.pdev->dev, "Port %d: Not installed\n", x + 1); - wc->modtype[x] = MOD_TYPE_NONE; - wc->cardflag |= (1 << x); - } + dev_notice(&wc->vb.pdev->dev, "Port %d: Not installed\n", x + 1); + wc->modtype[x] = MOD_TYPE_NONE; + } } } + } /* for (x...) */ + + return 0; +} + +static struct pci_driver wctdm_driver; + +static void wctdm_back_out_gracefully(struct wctdm *wc) +{ + int i; + unsigned long flags; + struct sframe_packet *frame; + LIST_HEAD(local_list); + + for (i = 0; i < ARRAY_SIZE(wc->spans); ++i) { + if (wc->spans[i] && wc->spans[i]->span.chans) + kfree(wc->spans[i]->span.chans); + + kfree(wc->spans[i]); + wc->spans[i] = NULL; } - if (fatal_signal_pending(current)) - return -EINTR; + for (i = 0; i < ARRAY_SIZE(wc->chans); ++i) { + kfree(wc->chans[i]); + wc->chans[i] = NULL; + } - if (!vpmsupport) { - dev_notice(&wc->vb.pdev->dev, "VPM: Support Disabled\n"); - } else if (!wctdm_vpm_init(wc)) { - dev_info(&wc->vb.pdev->dev, "VPM: Present and operational (Rev %c)\n", 'A' + wc->vpm100 - 1); - wc->ctlreg |= 0x10; - } else { - int res; - struct vpmadt032_options options; - GpakPortConfig_t portconfig; + voicebus_release(&wc->vb); + + spin_lock_irqsave(&wc->frame_list_lock, flags); + list_splice(&wc->frame_list, &local_list); + spin_unlock_irqrestore(&wc->frame_list_lock, flags); + + while (!list_empty(&local_list)) { + frame = list_entry(local_list.next, + struct sframe_packet, node); + list_del(&frame->node); + kfree(frame); + } + + kfree(wc); +} + +static const struct voicebus_operations voicebus_operations = { + .handle_receive = handle_receive, + .handle_transmit = handle_transmit, +}; + +static const struct voicebus_operations hx8_voicebus_operations = { + .handle_receive = handle_hx8_receive, + .handle_transmit = handle_hx8_transmit, +}; + +struct cmd_results { + u8 results[8]; +}; + +static int hx8_send_command(struct wctdm *wc, const u8 *command, + size_t count, int checksum, + int application, int bootloader, + struct cmd_results *results) +{ + int ret = 0; + struct vbb *vbb; + struct sframe_packet *frame; + const int MAX_COMMAND_LENGTH = 264 + 4; + unsigned long flags; + + might_sleep(); + + /* can't boot both into the application and the bootloader at once. */ + WARN_ON((application > 0) && (bootloader > 0)); + if ((application > 0) && (bootloader > 0)) + return -EINVAL; + + WARN_ON(count > MAX_COMMAND_LENGTH); + if (count > MAX_COMMAND_LENGTH) + return -EINVAL; + + vbb = kmem_cache_alloc(voicebus_vbb_cache, GFP_KERNEL); + WARN_ON(!vbb); + if (!vbb) + return -ENOMEM; - spin_lock_irqsave(&wc->reglock, flags); - for (x = NUM_CARDS; x < NUM_CARDS + NUM_EC; x++) - wc->modtype[x] = MOD_TYPE_NONE; - spin_unlock_irqrestore(&wc->reglock, flags); + memset(vbb->data, 0, SFRAME_SIZE); + memcpy(&vbb->data[EFRAME_SIZE + EFRAME_GAP], command, count); - options.debug = debug; - options.vpmnlptype = vpmnlptype; - options.vpmnlpthresh = vpmnlpthresh; - options.vpmnlpmaxsupp = vpmnlpmaxsupp; + vbb->data[EFRAME_SIZE] = 0x80 | ((application) ? 0 : 0x40) | + ((checksum) ? 0x20 : 0) | ((count & 0x100) >> 4); + vbb->data[EFRAME_SIZE + 1] = count & 0xff; - wc->vpmadt032 = vpmadt032_alloc(&options, wc->board_name); - if (!wc->vpmadt032) - return -ENOMEM; + if (bootloader) + vbb->data[EFRAME_SIZE + 3] = 0xAA; - wc->vpmadt032->setchanconfig_from_state = setchanconfig_from_state; - wc->vpmadt032->options.channels = wc->span.channels; - get_default_portconfig(&portconfig); - res = vpmadt032_init(wc->vpmadt032, &wc->vb); - if (res) { - vpmadt032_free(wc->vpmadt032); - wc->vpmadt032 = NULL; - return res; - } + spin_lock_bh(&wc->vb.lock); + voicebus_transmit(&wc->vb, vbb); + spin_unlock_bh(&wc->vb.lock); - /* Now we need to configure the VPMADT032 module for this - * particular board. */ - res = config_vpmadt032(wc->vpmadt032, wc); - if (res) { - vpmadt032_free(wc->vpmadt032); - wc->vpmadt032 = NULL; - return res; + /* Do not wait for the response if the caller doesn't care about the + * results. */ + if (NULL == results) + return 0; + + if (!wait_event_timeout(wc->regq, !list_empty(&wc->frame_list), 2*HZ)) { + dev_err(&wc->vb.pdev->dev, "Timeout waiting " + "for receive frame.\n"); + ret = -EIO; + } + + /* We only want the last packet received. Throw away anything else on + * the list */ + frame = NULL; + spin_lock_irqsave(&wc->frame_list_lock, flags); + while (!list_empty(&wc->frame_list)) { + frame = list_entry(wc->frame_list.next, + struct sframe_packet, node); + list_del(&frame->node); + if (!list_empty(&wc->frame_list)) { + kfree(frame); + frame = NULL; } + } + spin_unlock_irqrestore(&wc->frame_list_lock, flags); - dev_info(&wc->vb.pdev->dev, "VPMADT032: Present and operational (Firmware version %x)\n", wc->vpmadt032->version); - /* TODO what is 0x10 in this context? */ - wc->ctlreg |= 0x10; + if (frame) { + memcpy(results->results, &frame->sframe[EFRAME_SIZE], + sizeof(results->results)); + } else { + ret = -EIO; } + return ret; + +} + +static int hx8_get_fpga_version(struct wctdm *wc, u8 *major, u8 *minor) +{ + int ret; + struct cmd_results results; + u8 command[] = {0xD7, 0x00}; + + ret = hx8_send_command(wc, command, ARRAY_SIZE(command), + 0, 0, 0, &results); + if (ret) + return ret; + + *major = results.results[0]; + *minor = results.results[2]; return 0; } -static struct pci_driver wctdm_driver; +static void hx8_cleanup_frame_list(struct wctdm *wc) +{ + unsigned long flags; + LIST_HEAD(local_list); + struct sframe_packet *frame; + + spin_lock_irqsave(&wc->frame_list_lock, flags); + list_splice_init(&wc->frame_list, &local_list); + spin_unlock_irqrestore(&wc->frame_list_lock, flags); -static void free_wc(struct wctdm *wc) + while (!list_empty(&local_list)) { + frame = list_entry(local_list.next, struct sframe_packet, node); + list_del(&frame->node); + kfree(frame); + } +} + +static int hx8_switch_to_application(struct wctdm *wc) +{ + int ret; + u8 command[] = {0xD7, 0x00}; + + ret = hx8_send_command(wc, command, ARRAY_SIZE(command), + 0, 1, 0, NULL); + if (ret) + return ret; + + msleep(1000); + hx8_cleanup_frame_list(wc); + + return 0; +} + +/** + * hx8_switch_to_bootloader() - Send packet to switch hx8 into bootloader + * + */ +static int hx8_switch_to_bootloader(struct wctdm *wc) +{ + int ret; + u8 command[] = {0xD7, 0x00}; + + ret = hx8_send_command(wc, command, ARRAY_SIZE(command), + 0, 0, 1, NULL); + if (ret) + return ret; + + /* It takes some time for the FPGA to reload and switch it's + * configuration. */ + msleep(300); + hx8_cleanup_frame_list(wc); + + return 0; +} + +struct ha80000_firmware { + u8 header[6]; + u8 major_ver; + u8 minor_ver; + u8 data[54648]; + u32 chksum; +} __attribute__((packed)); + +static void hx8_send_dummy(struct wctdm *wc) { - unsigned int x; + u8 command[] = {0xD7, 0x00}; + + hx8_send_command(wc, command, ARRAY_SIZE(command), + 0, 0, 0, NULL); +} + +static int hx8_read_status_register(struct wctdm *wc, u8 *status) +{ + int ret; + struct cmd_results results; + u8 command[] = {0xD7, 0x00}; + + ret = hx8_send_command(wc, command, ARRAY_SIZE(command), + 0, 0, 0, &results); + if (ret) + return ret; + + *status = results.results[3]; + return 0; +} + +static const unsigned int HYBRID_PAGE_SIZE = 264; - for (x = 0; x < ARRAY_SIZE(wc->chans); x++) { - if (wc->chans[x]) { - kfree(wc->chans[x]); +static int hx8_write_buffer(struct wctdm *wc, const u8 *buffer, size_t size) +{ + int ret = 0; + struct cmd_results results; + int padding_bytes = 0; + u8 *local_data; + u8 command[] = {0x84, 0, 0, 0}; + + if (size > HYBRID_PAGE_SIZE) + return -EINVAL; + + if (size < HYBRID_PAGE_SIZE) + padding_bytes = HYBRID_PAGE_SIZE - size; + + local_data = kmalloc(sizeof(command) + size + padding_bytes, GFP_KERNEL); + if (!local_data) + return -ENOMEM; + + memcpy(local_data, command, sizeof(command)); + memcpy(&local_data[sizeof(command)], buffer, size); + memset(&local_data[sizeof(command) + size], 0xff, padding_bytes); + + ret = hx8_send_command(wc, local_data, + sizeof(command) + size + padding_bytes, + 1, 0, 0, &results); + if (ret) + goto cleanup; + +cleanup: + kfree(local_data); + return ret; +} + +static int hx8_buffer_to_page(struct wctdm *wc, const unsigned int page) +{ + int ret; + struct cmd_results results; + u8 command[] = {0x83, (page & 0x180) >> 7, (page & 0x7f) << 1, 0x00}; + + ret = hx8_send_command(wc, command, sizeof(command), + 1, 0, 0, &results); + if (ret) + return ret; + + return 0; +} + +static int hx8_wait_for_ready(struct wctdm *wc, const int timeout) +{ + int ret; + u8 status; + unsigned long local_timeout = jiffies + timeout; + + do { + ret = hx8_read_status_register(wc, &status); + if (ret) + return ret; + if ((status & 0x80) > 0) + break; + } while (time_after(local_timeout, jiffies)); + + if (time_after(jiffies, local_timeout)) + return -EIO; + + return 0; +} + +/** + * hx8_reload_application - reload the application firmware + * + * NOTE: The caller should ensure that the board is in bootloader mode before + * calling this function. + */ +static int hx8_reload_application(struct wctdm *wc, const struct ha80000_firmware *ha8_fw) +{ + unsigned int cur_page; + const u8 *data; + u8 status; + int ret = 0; + const int HYBRID_PAGE_COUNT = (sizeof(ha8_fw->data)) / HYBRID_PAGE_SIZE; + + BUG_ON(!ha8_fw); + might_sleep(); + + data = &ha8_fw->data[0]; + ret = hx8_read_status_register(wc, &status); + if (ret) + return ret; + + for (cur_page = 0; cur_page < HYBRID_PAGE_COUNT; ++cur_page) { + /* dev_dbg(&wc->vb.pdev->dev, "PAGE: %d\n", cur_page); */ + ret = hx8_write_buffer(wc, data, HYBRID_PAGE_SIZE); + if (ret) + return ret; + /* The application starts out at page 0x100 */ + ret = hx8_buffer_to_page(wc, 0x100 + cur_page); + if (ret) + return ret; + + /* wait no more than a second for the write to the page to + * finish */ + ret = hx8_wait_for_ready(wc, HZ); + if (ret) + return ret; + + data += HYBRID_PAGE_SIZE; + } + + return ret; +} + +static void print_hx8_recovery_message(struct device *dev) +{ + dev_warn(dev, "The firmware may be corrupted. Please completely " + "power off your system, power on, and then reload the driver " + "with the 'forceload' module parameter set to 1 to attempt " + "recovery.\n"); +} + +/** + * hx8_check_firmware - Check the firmware version and load a new one possibly. + * + */ +static int hx8_check_firmware(struct wctdm *wc) +{ + int ret; + u8 major; + u8 minor; + const struct firmware *fw; + const struct ha80000_firmware *ha8_fw; + struct device *dev = &wc->vb.pdev->dev; + int retries = 10; + + BUG_ON(!is_hx8(wc)); + + might_sleep(); + + do { + hx8_send_dummy(wc); + ret = hx8_get_fpga_version(wc, &major, &minor); + if (!ret) + break; + if (fatal_signal_pending(current)) + return -EINTR; + } while (--retries); + + if (ret) { + print_hx8_recovery_message(dev); + return ret; + } + + /* If we're in the bootloader, try to jump into the application. */ + if ((1 == major) && (0x80 == minor) && !forceload) { + dev_dbg(dev, "Switching to application.\n"); + hx8_switch_to_application(wc); + ret = hx8_get_fpga_version(wc, &major, &minor); + if (ret) { + print_hx8_recovery_message(dev); + return ret; } - if (wc->ec[x]) - kfree(wc->ec[x]); } - kfree(wc); + + dev_dbg(dev, "FPGA VERSION: %02x.%02x\n", major, minor); + + ret = request_firmware(&fw, "dahdi-fw-ha80000.bin", dev); + if (ret) { + dev_warn(dev, "Failed to load firmware from userspace, skipping " + "check. (%d)\n", ret); + return 0; + } + ha8_fw = (const struct ha80000_firmware *)fw->data; + + if ((fw->size != sizeof(*ha8_fw)) || + (0 != memcmp("DIGIUM", ha8_fw->header, sizeof(ha8_fw->header))) || + ((crc32(~0, (void *)ha8_fw, sizeof(*ha8_fw) - sizeof(u32)) ^ ~0) != + ha8_fw->chksum)) { + dev_warn(dev, "Firmware file is invalid. Skipping load.\n"); + ret = 0; + goto cleanup; + } + + dev_dbg(dev, "FIRMWARE: %02x.%02x\n", ha8_fw->major_ver, ha8_fw->minor_ver); + + if (ha8_fw->major_ver == major && + ha8_fw->minor_ver == minor) { + dev_dbg(dev, "Firmware versions match, skipping load.\n"); + ret = 0; + goto cleanup; + } + + if (2 == major) { + hx8_switch_to_bootloader(wc); + ret = hx8_get_fpga_version(wc, &major, &minor); + if (ret) + goto cleanup; + } + + /* so now we're in boot loader mode, ready to load the new firmware. */ + ret = hx8_reload_application(wc, ha8_fw); + if (ret) + goto cleanup; + + dev_dbg(dev, "Firmware reloaded. Booting into application.\n"); + + hx8_switch_to_application(wc); + ret = hx8_get_fpga_version(wc, &major, &minor); + if (ret) + goto cleanup; + + dev_dbg(dev, "FPGA VERSION AFTER LOAD: %02x.%02x\n", major, minor); + + if (forceload) { + dev_warn(dev, "Please unset forceload if your card is able to " + "detect the installed modules.\n"); + } + +cleanup: + release_firmware(fw); + return ret; } #ifdef CONFIG_VOICEBUS_SYSFS @@ -3872,11 +4621,6 @@ static DEVICE_ATTR(voicebus_current_latency, 0400, #endif -static const struct voicebus_operations voicebus_operations = { - .handle_receive = handle_receive, - .handle_transmit = handle_transmit, -}; - #ifdef USE_ASYNC_INIT struct async_data { struct pci_dev *pdev; @@ -3891,19 +4635,17 @@ __wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) #endif { struct wctdm *wc; - int i; - int y; - int ret; + int i, ret; + + int anamods, digimods, curchan, curspan; - neonmwi_offlimit_cycles = neonmwi_offlimit /MS_PER_HOOKCHECK; + neonmwi_offlimit_cycles = neonmwi_offlimit / MS_PER_HOOKCHECK; - if (!(wc = kmalloc(sizeof(*wc), GFP_KERNEL))) { + wc = kzalloc(sizeof(*wc), GFP_KERNEL); + if (!wc) return -ENOMEM; - } - memset(wc, 0, sizeof(*wc)); - wc->desc = (struct wctdm_desc *)ent->driver_data; - spin_lock(&ifacelock); + down(&ifacelock); /* \todo this is a candidate for removal... */ for (i = 0; i < WC_MAX_IFACES; ++i) { if (!ifaces[i]) { @@ -3911,23 +4653,38 @@ __wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) break; } } - spin_unlock(&ifacelock); + up(&ifacelock); + + wc->desc = (struct wctdm_desc *)ent->driver_data; - snprintf(wc->board_name, sizeof(wc->board_name)-1, "%s%d", - wctdm_driver.name, i); + /* This is to insure that the analog span is given lowest priority */ + wc->oldsync = -1; + init_MUTEX(&wc->syncsem); + INIT_LIST_HEAD(&wc->frame_list); + spin_lock_init(&wc->frame_list_lock); + + snprintf(wc->board_name, sizeof(wc->board_name)-1, "%s%d", wctdm_driver.name, i); pci_set_drvdata(pdev, wc); wc->vb.ops = &voicebus_operations; wc->vb.pdev = pdev; wc->vb.debug = &debug; - ret = voicebus_init(&wc->vb, wc->board_name); + + if (is_hx8(wc)) { + wc->vb.ops = &hx8_voicebus_operations; + ret = voicebus_no_idle_init(&wc->vb, wc->board_name); + } else { + wc->vb.ops = &voicebus_operations; + ret = voicebus_init(&wc->vb, wc->board_name); + voicebus_set_minlatency(&wc->vb, latency); + } + if (ret) { kfree(wc); return ret; } #ifdef CONFIG_VOICEBUS_SYSFS - dev_dbg(&wc->vb.pdev->dev, "Creating sysfs attributes.\n"); ret = device_create_file(&wc->vb.pdev->dev, &dev_attr_voicebus_current_latency); if (ret) { @@ -3935,70 +4692,160 @@ __wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) "Failed to create device attributes.\n"); } #endif - if (VOICEBUS_DEFAULT_LATENCY != latency) { - voicebus_set_minlatency(&wc->vb, latency); - } + voicebus_lock_latency(&wc->vb); + + init_waitqueue_head(&wc->regq); spin_lock_init(&wc->reglock); - wc->cards = NUM_CARDS; + wc->mods_per_board = NUM_MODULES; wc->pos = i; wc->txident = 1; - for (y=0;y<NUM_CARDS;y++) { - wc->flags[y] = wc->desc->flags; - wc->dacssrc[y] = -1; - } - init_waitqueue_head(&wc->regq); + for (i = 0; i < NUM_MODULES; i++) { + wc->flags[i] = wc->desc->flags; + wc->dacssrc[i] = -1; + } - for (i = 0; i < wc->cards; i++) { - if (!(wc->chans[i] = kmalloc(sizeof(*wc->chans[i]), GFP_KERNEL))) { - free_wc(wc); - return -ENOMEM; - } - memset(wc->chans[i], 0, sizeof(*wc->chans[i])); - if (!(wc->ec[i] = kmalloc(sizeof(*wc->ec[i]), GFP_KERNEL))) { - free_wc(wc); - return -ENOMEM; - } - memset(wc->ec[i], 0, sizeof(*wc->ec[i])); + /* Start the hardware processing. */ + if (voicebus_start(&wc->vb)) { + BUG_ON(1); } + if (is_hx8(wc)) { + ret = hx8_check_firmware(wc); + if (ret) { + voicebus_release(&wc->vb); + kfree(wc); + return -EIO; + } - if (wctdm_initialize(wc)) { - voicebus_release(&wc->vb); - kfree(wc); - return -EIO; + /* Switch back to the normal mode of operation */ + voicebus_stop(&wc->vb); + wc->vb.ops = &voicebus_operations; + voicebus_set_minlatency(&wc->vb, latency); + voicebus_set_normal_mode(&wc->vb); + if (voicebus_start(&wc->vb)) + BUG_ON(1); } - voicebus_lock_latency(&wc->vb); +/* first we have to make sure that we process all module data, we'll fine-tune it later in this routine. */ + wc->avchannels = NUM_MODULES; - if (voicebus_start(&wc->vb)) - BUG_ON(1); - /* Now track down what modules are installed */ - wctdm_locate_modules(wc); - - /* Final initialization */ - wctdm_post_initialize(wc); + wctdm_identify_modules(wc); + + if (fatal_signal_pending(current)) { + wctdm_back_out_gracefully(wc); + return -EINTR; + } +/* + * Walk the module list and create a 3-channel span for every BRI module found. + * Empty and analog modules get a common span which is allocated outside of this loop. + */ + anamods = digimods = 0; + curchan = curspan = 0; + for (i = 0; i < wc->mods_per_board; i++) { + struct b400m *b4; + + if (wc->modtype[i] == MOD_TYPE_NONE) { + ++curspan; + continue; + } else if (wc->modtype[i] == MOD_TYPE_BRI) { + wc->spans[curspan] = wctdm_init_span(wc, curspan, curchan, 3, 1); + if (!wc->spans[curspan]) { + wctdm_back_out_gracefully(wc); + return -EIO; + } + b4 = wc->mods[i].bri; + b400m_set_dahdi_span(b4, i & 0x03, &wc->spans[curspan]->span); + + ++curspan; + curchan += 3; + if (!(i & 0x03)) { + b400m_post_init(b4); + ++digimods; + } + } else { +/* + * FIXME: ABK: + * create a wctdm_chan for every analog module and link them into a span of their own down below. + * then evaluate all of the callbacks and hard-code whether they are receiving a dahdi_chan or wctdm_chan *. + * Finally, move the union from the wctdm structure to the dahdi_chan structure, and we should have something + * resembling a clean dynamic # of channels/dynamic # of spans driver. + */ + ++curspan; + ++anamods; + } + + if (digimods > 2) { + dev_info(&wc->vb.pdev->dev, "More than two digital modules detected. This is unsupported.\n"); + wctdm_back_out_gracefully(wc); + return -EIO; + } + } + +/* create an analog span if there are analog modules, or if there are no digital ones. */ + if (anamods || !digimods) { + if (!digimods) { + curspan = 0; + } + wctdm_init_span(wc, curspan, curchan, wc->desc->ports, 0); + wctdm_fixup_analog_span(wc, curspan); + wc->aspan = wc->spans[curspan]; + wc->aspan->span.pvt = wc; + curchan += wc->desc->ports; + ++curspan; + } + + /* Now fix up the timeslots for the analog modules, since the digital + * modules are always first */ + for (i = 0; i < wc->mods_per_board; i++) { + if (wc->modtype[i] == MOD_TYPE_FXS) { + wctdm_proslic_set_ts(wc, i, (digimods * 12) + i); + } else if (wc->modtype[i] == MOD_TYPE_FXO) { + wctdm_voicedaa_set_ts(wc, i, (digimods * 12) + i); + } else if (wc->modtype[i] == MOD_TYPE_QRV) { + wctdm_qrvdri_set_ts(wc, i, (digimods * 12) + i); + } + } + + wc->digi_mods = digimods; + + /* This shouldn't ever occur, but if we don't try to trap it, the driver + * will be scribbling into memory it doesn't own. */ + BUG_ON(curchan > 24); + + wc->avchannels = curchan; + + wctdm_initialize_vpm(wc); + #ifdef USE_ASYNC_INIT - async_synchronize_cookie(cookie); + async_synchronize_cookie(cookie); #endif - /* We should be ready for DAHDI to come in now. */ - if (dahdi_register(&wc->span, 0)) { - dev_info(&wc->vb.pdev->dev, - "Unable to register span with DAHDI\n"); - voicebus_release(&wc->vb); - kfree(wc); - return -1; + for (i = 0; i < MAX_SPANS; ++i) { + if (!wc->spans[i]) + continue; + + if (dahdi_register(&wc->spans[i]->span, 0)) { + dev_notice(&wc->vb.pdev->dev, "Unable to register span %d with DAHDI\n", i); + while (i) + dahdi_unregister(&wc->spans[i--]->span); + wctdm_back_out_gracefully(wc); + return -1; + } } wc->initialized = 1; - dev_info(&wc->vb.pdev->dev, "Found a Wildcard TDM: %s (%d modules)\n", - wc->desc->name, wc->desc->ports); - + dev_info(&wc->vb.pdev->dev, + "Found a Wildcard TDM: %s (%d digital %s, %d analog %s)\n", + wc->desc->name, digimods, + (digimods == 1) ? "module" : "modules", anamods, + (anamods == 1) ? "module" : "modules"); + ret = 0; + voicebus_unlock_latency(&wc->vb); return 0; } @@ -4043,25 +4890,27 @@ static void wctdm_release(struct wctdm *wc) int i; if (wc->initialized) { - dahdi_unregister(&wc->span); + for (i = 0; i < MAX_SPANS; i++) { + if (wc->spans[i]) + dahdi_unregister(&wc->spans[i]->span); + } } - voicebus_release(&wc->vb); - - spin_lock(&ifacelock); + down(&ifacelock); for (i = 0; i < WC_MAX_IFACES; i++) if (ifaces[i] == wc) break; ifaces[i] = NULL; - spin_unlock(&ifacelock); + up(&ifacelock); - free_wc(wc); + wctdm_back_out_gracefully(wc); } static void __devexit wctdm_remove_one(struct pci_dev *pdev) { struct wctdm *wc = pci_get_drvdata(pdev); struct vpmadt032 *vpm = wc->vpmadt032; + int i; #ifdef CONFIG_VOICEBUS_SYSFS device_remove_file(&wc->vb.pdev->dev, @@ -4075,6 +4924,12 @@ static void __devexit wctdm_remove_one(struct pci_dev *pdev) flush_scheduled_work(); } + /* shut down any BRI modules */ + for (i = 0; i < wc->mods_per_board; i += 4) { + if (wc->modtype[i] == MOD_TYPE_BRI) + wctdm_unload_b400m(wc, i); + } + voicebus_stop(&wc->vb); if (vpm) { @@ -4095,6 +4950,8 @@ static struct pci_device_id wctdm_pci_tbl[] = { { 0xd161, 0x8003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wcaex2400 }, { 0xd161, 0x8005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wctdm410 }, { 0xd161, 0x8006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wcaex410 }, + { 0xd161, 0x8007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wcha80000 }, + { 0xd161, 0x8008, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &wchb80000 }, { 0 } }; @@ -4146,6 +5003,8 @@ static int __init wctdm_init(void) battthresh = fxo_modes[_opermode].battthresh; } + b400m_module_init(); + res = dahdi_pci_module(&wctdm_driver); if (res) return -ENODEV; @@ -4195,6 +5054,28 @@ module_param(vpmnlpthresh, int, 0600); module_param(vpmnlpmaxsupp, int, 0600); #endif +/* Module parameters backed by code in xhfc.c */ +module_param(bri_debug, int, 0600); +MODULE_PARM_DESC(bri_debug, "bitmap: 1=general 2=dtmf 4=regops 8=fops 16=ec 32=st state 64=hdlc 128=alarm"); + +module_param(bri_spanfilter, int, 0600); +MODULE_PARM_DESC(bri_spanfilter, "debug filter for spans. bitmap: 1=port 1, 2=port 2, 4=port 3, 8=port 4"); + +module_param(bri_alarmdebounce, int, 0600); +MODULE_PARM_DESC(bri_alarmdebounce, "msec to wait before set/clear alarm condition"); + +module_param(bri_teignorered, int, 0600); +MODULE_PARM_DESC(bri_teignorered, "1=ignore (do not inform DAHDI) if a red alarm exists in TE mode"); + +module_param(bri_persistentlayer1, int, 0600); +MODULE_PARM_DESC(bri_persistentlayer1, "Set to 0 for disabling automatic layer 1 reactivation (when other end deactivates it)"); + +module_param(timingcable, int, 0600); +MODULE_PARM_DESC(timingcable, "Set to 1 for enabling timing cable. This means that *all* cards in the system are linked together with a single timing cable"); + +module_param(forceload, int, 0600); +MODULE_PARM_DESC(forceload, "Set to 1 in order to force an FPGA reload after power on (currently only for HA8/HB8 cards)."); + MODULE_DESCRIPTION("Wildcard VoiceBus Analog Card Driver"); MODULE_AUTHOR("Digium Incorporated <support@digium.com>"); MODULE_ALIAS("wctdm8xxp"); |