diff options
Diffstat (limited to 'drivers/dahdi/wctdm24xxp/base.c')
-rw-r--r-- | drivers/dahdi/wctdm24xxp/base.c | 379 |
1 files changed, 216 insertions, 163 deletions
diff --git a/drivers/dahdi/wctdm24xxp/base.c b/drivers/dahdi/wctdm24xxp/base.c index b04a908..d13ec1b 100644 --- a/drivers/dahdi/wctdm24xxp/base.c +++ b/drivers/dahdi/wctdm24xxp/base.c @@ -279,16 +279,6 @@ static inline int CMD_BYTE(int card, int bit, int altcs) + ((card) >> 2) + (altcs) + ((altcs) ? -21 : 0)); } -static inline int empty_slot(struct wctdm_module *const mod) -{ - int x; - for (x = 0; x < USER_COMMANDS; x++) { - if (!mod->cmdq.cmds[x]) - return x; - } - return -1; -} - static void setchanconfig_from_state(struct vpmadt032 *vpm, int channel, GpakChannelConfig_t *chanconfig) @@ -584,11 +574,11 @@ static void cmd_dequeue_vpmadt032(struct wctdm *wc, u8 *eframe) } } +/* Call with wc->reglock held and local interrupts disabled */ static void _cmd_dequeue(struct wctdm *wc, u8 *eframe, int card, int pos) { struct wctdm_module *const mod = &wc->mods[card]; unsigned int curcmd=0; - int x; int subaddr = card & 0x3; /* QRV only use commands relating to the first channel */ @@ -602,13 +592,13 @@ static void _cmd_dequeue(struct wctdm *wc, u8 *eframe, int card, int pos) eframe += 24; /* Search for something waiting to transmit */ if (pos) { - for (x = 0; x < MAX_COMMANDS; x++) { - if ((mod->cmdq.cmds[x] & (__CMD_RD | __CMD_WR)) && - !(mod->cmdq.cmds[x] & (__CMD_TX | __CMD_FIN))) { - curcmd = mod->cmdq.cmds[x]; - mod->cmdq.cmds[x] |= (wc->txident << 24) | __CMD_TX; - break; - } + if (!list_empty(&mod->pending_cmds)) { + struct wctdm_cmd *const cmd = + list_entry(mod->pending_cmds.next, + struct wctdm_cmd, node); + curcmd = cmd->cmd; + cmd->ident = wc->txident; + list_move_tail(&cmd->node, &mod->active_cmds); } } @@ -742,71 +732,132 @@ static inline void cmd_decipher_vpmadt032(struct wctdm *wc, const u8 *eframe) */ static void _cmd_decipher(struct wctdm *wc, const u8 *eframe, int card) { + enum { TDM_BYTES = 24, }; struct wctdm_module *const mod = &wc->mods[card]; - unsigned char ident; - int x; + struct wctdm_cmd *cmd; + u8 address; + u8 value; - /* QRV modules only use commands relating to the first channel */ - if ((card & 0x03) && (mod->type == QRV)) + if (list_empty(&mod->active_cmds)) return; - /* Skip audio */ - eframe += 24; + cmd = list_entry(mod->active_cmds.next, struct wctdm_cmd, node); + if (cmd->ident != wc->rxident) + return; - /* Search for any pending results */ - for (x=0;x<MAX_COMMANDS;x++) { - if ((mod->cmdq.cmds[x] & (__CMD_RD | __CMD_WR)) && - (mod->cmdq.cmds[x] & (__CMD_TX)) && - !(mod->cmdq.cmds[x] & (__CMD_FIN))) { - ident = (mod->cmdq.cmds[x] >> 24) & 0xff; - if (ident == wc->rxident) { - /* Store result */ - mod->cmdq.cmds[x] |= eframe[CMD_BYTE(card, 2, mod->altcs)]; - mod->cmdq.cmds[x] |= __CMD_FIN; - if (mod->cmdq.cmds[x] & __CMD_WR) { - /* Go ahead and clear out writes since they need no acknowledgement */ - mod->cmdq.cmds[x] = 0x00000000; - } else if (x >= USER_COMMANDS) { - /* Clear out ISR reads */ - mod->cmdq.isrshadow[x - USER_COMMANDS] = mod->cmdq.cmds[x] & 0xff; - mod->cmdq.cmds[x] = 0x00000000; - } - break; - } + list_del(&cmd->node); + + if (cmd->cmd & __CMD_WR) { + kfree(cmd); + return; + } + + address = (cmd->cmd >> 8) & 0xff; + + cmd->cmd = eframe[TDM_BYTES + CMD_BYTE(card, 2, mod->altcs)]; + + value = (cmd->cmd & 0xff); + + if (cmd->complete) { + complete(cmd->complete); + return; + } + + switch (mod->type) { + case FXS: + if (68 == address) { + mod->isrshadow[0] = value; +#ifdef PAQ_DEBUG + } else if (19 == address) { + mod->isrshadow[1] = value; +#else + } else if (LINE_STATE == address) { + mod->isrshadow[1] = value; +#endif + } else { + dev_info(&wc->vb.pdev->dev, + "FXS isr read address: %d\n", address); + } + break; + case FXO: + if (5 == address) { /* Hook/Ring state */ + mod->isrshadow[0] = value; + } else if (29 == address) { /* Battery */ + mod->isrshadow[1] = value; + } else { + dev_info(&wc->vb.pdev->dev, + "FXO isr read address: %d %08x\n", + address, cmd->cmd); } + break; + case QRV: + /* wctdm_isr_getreg(wc, mod, 3); */ /* COR/CTCSS state */ + /* TODO: This looks broken to me, but I have no way to + * resolved it. */ + /* wc->mods[card & 0xfc].cmds[USER_COMMANDS + 1] = CMD_RD(3); */ + break; + default: + break; } + + kfree(cmd); } +/* Call with wc.reglock held and local interrupts disabled. */ +static void +wctdm_isr_getreg(struct wctdm *wc, struct wctdm_module *const mod, u8 address) +{ + struct wctdm_cmd *cmd; + + cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC); + if (unlikely(!cmd)) + return; + + cmd->cmd = CMD_RD(address); + cmd->complete = NULL; + + list_add(&cmd->node, &mod->pending_cmds); +} + +static inline void +wctdm_setreg_intr(struct wctdm *wc, struct wctdm_module *mod, + int addr, int val); + static void cmd_checkisr(struct wctdm *wc, struct wctdm_module *const mod) { - if (!mod->cmdq.cmds[USER_COMMANDS + 0]) { - if (mod->sethook) { - mod->cmdq.cmds[USER_COMMANDS + 0] = mod->sethook; - mod->sethook = 0; - } else if (mod->type == FXS) { - mod->cmdq.cmds[USER_COMMANDS + 0] = CMD_RD(68); /* Hook state */ - } else if (mod->type == FXO) { - mod->cmdq.cmds[USER_COMMANDS + 0] = CMD_RD(5); /* Hook/Ring state */ - } else if (mod->type == QRV) { - wc->mods[mod->card & 0xfc].cmdq.cmds[USER_COMMANDS + 0] = CMD_RD(3); /* COR/CTCSS state */ - } else if (mod->type == BRI) { - wctdm_bri_checkisr(wc, mod, 0); - } + if (mod->sethook) { + wctdm_setreg_intr(wc, mod, ((mod->sethook >> 8) & 0xff), + mod->sethook & 0xff); + mod->sethook = 0; + return; } - if (!mod->cmdq.cmds[USER_COMMANDS + 1]) { - if (mod->type == FXS) { + + switch (mod->type) { + case FXS: + wctdm_isr_getreg(wc, mod, 68); /* Hook state */ #ifdef PAQ_DEBUG - mod->cmdq.cmds[USER_COMMANDS + 1] = CMD_RD(19); /* Transistor interrupts */ + wctdm_isr_getreg(wc, mod, 19); /* Transistor interrupts */ #else - mod->cmdq.cmds[USER_COMMANDS + 1] = CMD_RD(LINE_STATE); + wctdm_isr_getreg(wc, mod, LINE_STATE); #endif - } else if (mod->type == FXO) { - mod->cmdq.cmds[USER_COMMANDS + 1] = CMD_RD(29); /* Battery */ - } else if (mod->type == QRV) { - wc->mods[mod->card & 0xfc].cmdq.cmds[USER_COMMANDS + 1] = CMD_RD(3); /* Battery */ - } else if (mod->type == BRI) { - wctdm_bri_checkisr(wc, mod, 1); - } + break; + case FXO: + wctdm_isr_getreg(wc, mod, 5); /* Hook/Ring state */ + wctdm_isr_getreg(wc, mod, 29); /* Battery */ + break; + case QRV: + wctdm_isr_getreg(wc, mod, 3); /* COR/CTCSS state */ + /* TODO: This looks broken to me, but I have no way to + * resolved it. */ + /* wc->mods[card & 0xfc].cmds[USER_COMMANDS + 1] = CMD_RD(3); */ + break; + case BRI: + /* TODO: Two calls needed here? */ + wctdm_bri_checkisr(wc, mod, 0); + wctdm_bri_checkisr(wc, mod, 1); + break; + default: + break; } } @@ -920,28 +971,26 @@ static inline void wctdm_transmitprep(struct wctdm *wc, unsigned char *sframe) spin_unlock_irqrestore(&wc->reglock, flags); } -static bool -stuff_command(struct wctdm *wc, struct wctdm_module *const mod, - unsigned int cmd, int *hit) +/* Must be called with wc.reglock held and local interrupts disabled */ +static inline void +wctdm_setreg_intr(struct wctdm *wc, struct wctdm_module *mod, int addr, int val) { - unsigned long flags; + struct wctdm_cmd *cmd; - spin_lock_irqsave(&wc->reglock, flags); - *hit = empty_slot(mod); - if (*hit > -1) - mod->cmdq.cmds[*hit] = cmd; - spin_unlock_irqrestore(&wc->reglock, flags); + cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC); + if (unlikely(!cmd)) + return; + + cmd->complete = NULL; + cmd->cmd = CMD_WR(addr, val); - return (*hit > -1); + list_add_tail(&cmd->node, &mod->pending_cmds); } -static int -wctdm_setreg_full(struct wctdm *wc, struct wctdm_module *mod, - int addr, int val, int inisr) +int wctdm_setreg(struct wctdm *wc, struct wctdm_module *mod, int addr, int val) { - const unsigned int cmd = CMD_WR(addr, val); - int ret; - int hit; + struct wctdm_cmd *cmd; + unsigned long flags; #if 0 /* TODO */ /* QRV and BRI cards are only addressed at their first "port" */ @@ -950,52 +999,25 @@ wctdm_setreg_full(struct wctdm *wc, struct wctdm_module *mod, return 0; #endif - if (inisr) { - stuff_command(wc, mod, cmd, &hit); - return 0; - } - - ret = wait_event_interruptible(wc->regq, - stuff_command(wc, mod, cmd, &hit)); - return ret; -} - -static inline void -wctdm_setreg_intr(struct wctdm *wc, struct wctdm_module *mod, int addr, int val) -{ - wctdm_setreg_full(wc, mod, addr, val, 1); -} - -int wctdm_setreg(struct wctdm *wc, struct wctdm_module *mod, int addr, int val) -{ - return wctdm_setreg_full(wc, mod, addr, val, 0); -} + cmd = kmalloc(sizeof(*cmd), GFP_KERNEL); + if (unlikely(!cmd)) + return -ENOMEM; -static bool -cmd_finished(struct wctdm *wc, struct wctdm_module *const mod, int hit, u8 *val) -{ - bool ret; - unsigned long flags; + cmd->complete = NULL; + cmd->cmd = CMD_WR(addr, val); spin_lock_irqsave(&wc->reglock, flags); - if (mod->cmdq.cmds[hit] & __CMD_FIN) { - *val = mod->cmdq.cmds[hit] & 0xff; - mod->cmdq.cmds[hit] = 0x00000000; - ret = true; - } else { - ret = false; - } + list_add_tail(&cmd->node, &mod->pending_cmds); spin_unlock_irqrestore(&wc->reglock, flags); - return ret; + return 0; } int wctdm_getreg(struct wctdm *wc, struct wctdm_module *const mod, int addr) { - const unsigned int cmd = CMD_RD(addr); - u8 val = 0; - int hit; - int ret; + unsigned long flags; + struct wctdm_cmd *cmd; + int val; #if 0 /* TODO */ /* if a QRV card, use only its first channel */ @@ -1005,15 +1027,29 @@ int wctdm_getreg(struct wctdm *wc, struct wctdm_module *const mod, int addr) } #endif - ret = wait_event_interruptible(wc->regq, - stuff_command(wc, mod, cmd, &hit)); - if (ret) - return ret; + cmd = kmalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; - ret = wait_event_interruptible(wc->regq, - cmd_finished(wc, mod, hit, &val)); - if (ret) - return ret; + cmd->complete = kmalloc(sizeof(*cmd->complete), GFP_KERNEL); + if (!cmd->complete) { + kfree(cmd); + return -ENOMEM; + } + + init_completion(cmd->complete); + + cmd->cmd = CMD_RD(addr); + + spin_lock_irqsave(&wc->reglock, flags); + list_add_tail(&cmd->node, &mod->pending_cmds); + spin_unlock_irqrestore(&wc->reglock, flags); + + wait_for_completion(cmd->complete); + val = cmd->cmd & 0xff; + + kfree(cmd->complete); + kfree(cmd); return val; } @@ -1023,17 +1059,15 @@ int wctdm_getreg(struct wctdm *wc, struct wctdm_module *const mod, int addr) */ static void cmd_retransmit(struct wctdm *wc) { - int x,y; - /* Force retransmissions */ - for (x=0;x<MAX_COMMANDS;x++) { - for (y = 0; y < wc->mods_per_board; y++) { - struct wctdm_module *const mod = &wc->mods[y]; - if (mod->type == BRI) - continue; - if (!(mod->cmdq.cmds[x] & __CMD_FIN)) - mod->cmdq.cmds[x] &= ~(__CMD_TX | (0xff << 24)); - } + int x; + + for (x = 0; x < wc->mods_per_board; x++) { + struct wctdm_module *const mod = &wc->mods[x]; + if (mod->type == BRI) + continue; + list_splice_init(&mod->active_cmds, &mod->pending_cmds); } + #ifdef VPM_SUPPORT if (wc->vpmadt032) vpmadt032_resend(wc->vpmadt032); @@ -1158,9 +1192,6 @@ static inline void wctdm_receiveprep(struct wctdm *wc, const u8 *sframe) } } } - - /* Wake up anyone sleeping to read/write a new register */ - wake_up_interruptible_all(&wc->regq); } static int wait_access(struct wctdm *wc, struct wctdm_module *const mod) @@ -1301,20 +1332,21 @@ static void wctdm_proslic_check_oppending(struct wctdm *wc, struct wctdm_module *const mod) { struct fxs *const fxs = &mod->mod.fxs; + unsigned long flags; int res; if (!(fxs->lasttxhook & SLIC_LF_OPPENDING)) return; /* Monitor the Pending LF state change, for the next 100ms */ - spin_lock(&fxs->lasttxhooklock); + spin_lock_irqsave(&fxs->lasttxhooklock, flags); if (!(fxs->lasttxhook & SLIC_LF_OPPENDING)) { - spin_unlock(&fxs->lasttxhooklock); + spin_unlock_irqrestore(&fxs->lasttxhooklock, flags); return; } - res = mod->cmdq.isrshadow[1]; + res = mod->isrshadow[1]; if ((res & SLIC_LF_SETMASK) == (fxs->lasttxhook & SLIC_LF_SETMASK)) { fxs->lasttxhook &= SLIC_LF_SETMASK; fxs->oppending_ms = 0; @@ -1325,8 +1357,7 @@ wctdm_proslic_check_oppending(struct wctdm *wc, struct wctdm_module *const mod) res, fxs->lasttxhook, wc->intcount); } } else if (fxs->oppending_ms && (--fxs->oppending_ms == 0)) { - /* Timed out, resend the linestate */ - mod->sethook = CMD_WR(LINE_STATE, fxs->lasttxhook); + wctdm_setreg_intr(wc, mod, LINE_STATE, fxs->lasttxhook); if (debug & DEBUG_CARD) { dev_info(&wc->vb.pdev->dev, "SLIC_LF RETRY: card=%d shadow=%02x " @@ -1336,7 +1367,7 @@ wctdm_proslic_check_oppending(struct wctdm *wc, struct wctdm_module *const mod) } else { /* Start 100ms Timeout */ fxs->oppending_ms = 100; } - spin_unlock(&fxs->lasttxhooklock); + spin_unlock_irqrestore(&fxs->lasttxhooklock, flags); } /* 256ms interrupt */ @@ -1347,16 +1378,16 @@ wctdm_proslic_recheck_sanity(struct wctdm *wc, struct wctdm_module *const mod) int res; unsigned long flags; #ifdef PAQ_DEBUG - res = mod->cmdq.isrshadow[1]; + res = mod->isrshadow[1]; res &= ~0x3; if (res) { - mod->cmdq.isrshadow[1] = 0; + mod->isrshadow[1] = 0; fxs->palarms++; if (fxs->palarms < MAX_ALARMS) { dev_notice(&wc->vb.pdev->dev, "Power alarm (%02x) on module %d, resetting!\n", res, card + 1); mod->sethook = CMD_WR(19, res); /* Update shadow register to avoid extra power alarms until next read */ - mod->cmdq.isrshadow[1] = 0; + mod->isrshadow[1] = 0; } else { if (fxs->palarms == MAX_ALARMS) dev_notice(&wc->vb.pdev->dev, "Too many power alarms on card %d, NOT resetting!\n", card + 1); @@ -1364,7 +1395,7 @@ wctdm_proslic_recheck_sanity(struct wctdm *wc, struct wctdm_module *const mod) } #else spin_lock_irqsave(&fxs->lasttxhooklock, flags); - res = mod->cmdq.isrshadow[1]; + res = mod->isrshadow[1]; #if 0 /* This makes sure the lasthook was put in reg 64 the linefeed reg */ @@ -1410,7 +1441,7 @@ wctdm_proslic_recheck_sanity(struct wctdm *wc, struct wctdm_module *const mod) spin_unlock_irqrestore(&fxs->lasttxhooklock, flags); /* Update shadow register to avoid extra power alarms until next read */ - mod->cmdq.isrshadow[1] = fxs->lasttxhook; + mod->isrshadow[1] = fxs->lasttxhook; } else { if (fxs->palarms == MAX_ALARMS) { dev_notice(&wc->vb.pdev->dev, @@ -1429,7 +1460,7 @@ static void wctdm_qrvdri_check_hook(struct wctdm *wc, int card) if (wc->mods[card].mod.qrv.debtime >= 2) wc->mods[card].mod.qrv.debtime--; - b = wc->mods[qrvcard].cmdq.isrshadow[0]; /* Hook/Ring state */ + b = wc->mods[qrvcard].isrshadow[0]; /* Hook/Ring state */ b &= 0xcc; /* use bits 3-4 and 6-7 only */ if (wc->mods[qrvcard].mod.qrv.radmode & RADMODE_IGNORECOR) @@ -1501,7 +1532,7 @@ wctdm_voicedaa_check_hook(struct wctdm *wc, struct wctdm_module *const mod) struct fxo *const fxo = &mod->mod.fxo; /* Try to track issues that plague slot one FXO's */ - b = mod->cmdq.isrshadow[0]; /* Hook/Ring state */ + b = mod->isrshadow[0]; /* Hook/Ring state */ b &= 0x9b; if (fxo->offhook) { if (b != 0x9) @@ -1521,7 +1552,7 @@ wctdm_voicedaa_check_hook(struct wctdm *wc, struct wctdm_module *const mod) * but not to have transitions between the two bits (i.e. no negative * to positive or positive to negative transversals ) */ - res = mod->cmdq.isrshadow[0] & 0x60; + res = mod->isrshadow[0] & 0x60; if (0 == fxo->wasringing) { if (res) { /* Look for positive/negative crossings in ring status reg */ @@ -1559,7 +1590,7 @@ wctdm_voicedaa_check_hook(struct wctdm *wc, struct wctdm_module *const mod) } } } else { - res = mod->cmdq.isrshadow[0]; + res = mod->isrshadow[0]; if ((res & 0x60) && (fxo->battery == BATTERY_PRESENT)) { fxo->ringdebounce += (DAHDI_CHUNKSIZE * 16); if (fxo->ringdebounce >= DAHDI_CHUNKSIZE * ringdebounce) { @@ -1587,7 +1618,7 @@ wctdm_voicedaa_check_hook(struct wctdm *wc, struct wctdm_module *const mod) } } - b = mod->cmdq.isrshadow[1]; /* Voltage */ + b = mod->isrshadow[1]; /* Voltage */ abs_voltage = abs(b); if (fxovoltage) { @@ -1895,7 +1926,7 @@ wctdm_proslic_check_hook(struct wctdm *wc, struct wctdm_module *const mod) /* For some reason we have to debounce the hook detector. */ - res = mod->cmdq.isrshadow[0]; /* Hook state */ + res = mod->isrshadow[0]; /* Hook state */ hook = (res & 1); if (hook != fxs->lastrxhook) { @@ -2256,6 +2287,10 @@ wctdm_proslic_powerleak_test(struct wctdm *wc, struct wctdm_module *const mod) /* Wait for one second */ origjiffies = jiffies; + /* TODO: Why is this sleep necessary. WIthout it, the first read + * comes back with a 0 value. */ + msleep(20); + while ((vbat = wctdm_getreg(wc, mod, 82)) > 0x6) { if ((jiffies - origjiffies) >= (HZ/2)) break;; @@ -4251,7 +4286,6 @@ static void wctdm_back_out_gracefully(struct wctdm *wc) { int i; unsigned long flags; - struct sframe_packet *frame; LIST_HEAD(local_list); voicebus_release(&wc->vb); @@ -4270,9 +4304,23 @@ static void wctdm_back_out_gracefully(struct wctdm *wc) wc->spans[i] = NULL; } + spin_lock_irqsave(&wc->reglock, flags); for (i = 0; i < ARRAY_SIZE(wc->mods); ++i) { - kfree(wc->mods[i].chan); - wc->mods[i].chan = NULL; + struct wctdm_module *const mod = &wc->mods[i]; + kfree(mod->chan); + mod->chan = NULL; + list_splice_init(&mod->pending_cmds, &local_list); + list_splice_init(&mod->active_cmds, &local_list); + } + spin_unlock_irqrestore(&wc->reglock, flags); + + while (!list_empty(&local_list)) { + struct wctdm_cmd *cmd; + cmd = list_entry(local_list.next, + struct wctdm_cmd, node); + list_del(&cmd->node); + kfree(cmd->complete); + kfree(cmd); } spin_lock_irqsave(&wc->frame_list_lock, flags); @@ -4280,12 +4328,15 @@ static void wctdm_back_out_gracefully(struct wctdm *wc) spin_unlock_irqrestore(&wc->frame_list_lock, flags); while (!list_empty(&local_list)) { + struct sframe_packet *frame; frame = list_entry(local_list.next, struct sframe_packet, node); list_del(&frame->node); kfree(frame); } + + kfree(wc->board_name); kfree(wc); } @@ -4910,7 +4961,9 @@ __wctdm_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* Auto detect this card's companding */ wc->companding = DAHDI_LAW_DEFAULT; - for (i = 0; i < NUM_MODULES; i++) { + for (i = 0; i < ARRAY_SIZE(wc->mods); i++) { + INIT_LIST_HEAD(&wc->mods[i].pending_cmds); + INIT_LIST_HEAD(&wc->mods[i].active_cmds); wc->mods[i].dacssrc = -1; wc->mods[i].card = i; } |