summaryrefslogtreecommitdiff
path: root/drivers/dahdi/wcte12xp/base.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dahdi/wcte12xp/base.c')
-rw-r--r--drivers/dahdi/wcte12xp/base.c57
1 files changed, 41 insertions, 16 deletions
diff --git a/drivers/dahdi/wcte12xp/base.c b/drivers/dahdi/wcte12xp/base.c
index 2d82e3f..1dcd4ae 100644
--- a/drivers/dahdi/wcte12xp/base.c
+++ b/drivers/dahdi/wcte12xp/base.c
@@ -112,6 +112,7 @@ static struct command *get_free_cmd(struct t1 *wc)
cmd = kmem_cache_alloc(cmd_cache, GFP_ATOMIC);
if (cmd) {
memset(cmd, 0, sizeof(*cmd));
+ init_completion(&cmd->complete);
INIT_LIST_HEAD(&cmd->node);
}
return cmd;
@@ -138,8 +139,6 @@ static struct command *get_pending_cmd(struct t1 *wc)
static void submit_cmd(struct t1 *wc, struct command *cmd)
{
unsigned long flags;
- if (cmd->flags & (__CMD_RD | __CMD_PINS))
- init_completion(&cmd->complete);
spin_lock_irqsave(&wc->cmd_list_lock, flags);
list_add_tail(&cmd->node, &wc->pending_cmds);
spin_unlock_irqrestore(&wc->cmd_list_lock, flags);
@@ -213,7 +212,7 @@ static inline void cmd_decipher(struct t1 *wc, const u8 *readchunk)
if (cmd->flags & (__CMD_WR | __CMD_LEDS)) {
/* Nobody is waiting on writes...so let's just
* free them here. */
- list_del(&cmd->node);
+ list_del_init(&cmd->node);
free_cmd(wc, cmd);
} else {
cmd->data |= readchunk[CMD_BYTE(cmd->cs_slot, 2, IS_VPM)];
@@ -577,17 +576,31 @@ static int t1_getreg(struct t1 *wc, int addr)
cmd->data = 0x00;
cmd->flags = __CMD_RD;
submit_cmd(wc, cmd);
- ret = wait_for_completion_timeout(&cmd->complete, HZ*2);
+ ret = wait_for_completion_timeout(&cmd->complete, HZ*10);
if (unlikely(!ret)) {
spin_lock_bh(&wc->cmd_list_lock);
- list_del(&cmd->node);
- spin_unlock_bh(&wc->cmd_list_lock);
- if (printk_ratelimit()) {
- dev_warn(&wc->vb.pdev->dev,
- "Timeout in %s\n", __func__);
+ if (!list_empty(&cmd->node)) {
+ /* Since we've removed this command from the list, we
+ * can go ahead and free it right away. */
+ list_del_init(&cmd->node);
+ spin_unlock_bh(&wc->cmd_list_lock);
+ if (printk_ratelimit()) {
+ dev_warn(&wc->vb.pdev->dev,
+ "Timeout in %s\n", __func__);
+ }
+ free_cmd(wc, cmd);
+ return -EIO;
+ } else {
+ /* Looks like this command was removed from the list by
+ * someone else already. Let's wait for them to complete
+ * it so that we don't free up the memory. */
+ spin_unlock_bh(&wc->cmd_list_lock);
+ ret = wait_for_completion_timeout(&cmd->complete, HZ*2);
+ WARN_ON(!ret);
+ ret = cmd->data;
+ free_cmd(wc, cmd);
+ return ret;
}
- free_cmd(wc, cmd);
- return -EIO;
}
ret = cmd->data;
free_cmd(wc, cmd);
@@ -623,7 +636,7 @@ static inline int t1_getpins(struct t1 *wc, int inisr)
ret = wait_for_completion_timeout(&cmd->complete, HZ*2);
if (unlikely(!ret)) {
spin_lock_bh(&wc->cmd_list_lock);
- list_del(&cmd->node);
+ list_del_init(&cmd->node);
spin_unlock_bh(&wc->cmd_list_lock);
if (printk_ratelimit()) {
dev_warn(&wc->vb.pdev->dev,
@@ -678,7 +691,7 @@ static void free_wc(struct t1 *wc)
spin_unlock_irqrestore(&wc->cmd_list_lock, flags);
while (!list_empty(&list)) {
cmd = list_entry(list.next, struct command, node);
- list_del(&cmd->node);
+ list_del_init(&cmd->node);
free_cmd(wc, cmd);
}
kfree(wc);
@@ -1115,6 +1128,8 @@ static int t1xxp_maint(struct dahdi_span *span, int cmd)
case DAHDI_MAINT_LOCALLOOP:
t1xxp_clear_maint(span);
reg = t1_getreg(wc, LIM0);
+ if (reg < 0)
+ return -EIO;
t1_setreg(wc, LIM0, reg | LIM0_LL);
break;
case DAHDI_MAINT_REMOTELOOP:
@@ -1135,17 +1150,23 @@ static int t1xxp_maint(struct dahdi_span *span, int cmd)
case DAHDI_MAINT_LOCALLOOP:
t1xxp_clear_maint(span);
reg = t1_getreg(wc, LIM0);
- t1_setreg(wc, LIM0, reg | LIM0_LL);
+ if (reg < 0)
+ return -EIO;
+ t1_setreg(wc, LIM0, reg | LIM0_LL);
break;
case DAHDI_MAINT_NETWORKLINELOOP:
t1xxp_clear_maint(span);
reg = t1_getreg(wc, LIM1);
- t1_setreg(wc, LIM1, reg | LIM1_RL);
+ if (reg < 0)
+ return -EIO;
+ t1_setreg(wc, LIM1, reg | LIM1_RL);
break;
case DAHDI_MAINT_NETWORKPAYLOADLOOP:
t1xxp_clear_maint(span);
reg = t1_getreg(wc, LIM1);
- t1_setreg(wc, LIM1, reg | (LIM1_RL | LIM1_JATT));
+ if (reg < 0)
+ return -EIO;
+ t1_setreg(wc, LIM1, reg | (LIM1_RL | LIM1_JATT));
break;
case DAHDI_MAINT_LOOPUP:
t1xxp_clear_maint(span);
@@ -1175,10 +1196,14 @@ static int t1xxp_clear_maint(struct dahdi_span *span)
/* Turn off local loop */
reg = t1_getreg(wc, LIM0);
+ if (reg < 0)
+ return -EIO;
t1_setreg(wc, LIM0, reg & ~LIM0_LL);
/* Turn off remote loop & jitter attenuator */
reg = t1_getreg(wc, LIM1);
+ if (reg < 0)
+ return -EIO;
t1_setreg(wc, LIM1, reg & ~(LIM1_RL | LIM1_JATT));
return 0;
}