Skip to content

Commit

Permalink
Staging: comedi: amplc_dio200: Protect counter subdevice with spinlock
Browse files Browse the repository at this point in the history
The internal state of an 82C54 counter timer chip will get messed up if
several threads read, write, configure, or check the status of the chip
simultaneously.  Protect the register access sequences with a spin lock.

Signed-off-by: Ian Abbott <abbotti@mev.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
Ian Abbott authored and Greg Kroah-Hartman committed Jun 4, 2010
1 parent d4da77a commit ee4063f
Showing 1 changed file with 25 additions and 10 deletions.
35 changes: 25 additions & 10 deletions drivers/staging/comedi/drivers/amplc_dio200.c
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ struct dio200_subdev_8254 {
int has_clk_gat_sce;
unsigned clock_src[3]; /* Current clock sources */
unsigned gate_src[3]; /* Current gate sources */
spinlock_t spinlock;
};

struct dio200_subdev_intr {
Expand Down Expand Up @@ -1042,8 +1043,11 @@ dio200_subdev_8254_read(struct comedi_device *dev, struct comedi_subdevice *s,
{
struct dio200_subdev_8254 *subpriv = s->private;
int chan = CR_CHAN(insn->chanspec);
unsigned long flags;

spin_lock_irqsave(&subpriv->spinlock, flags);
data[0] = i8254_read(subpriv->iobase, 0, chan);
spin_unlock_irqrestore(&subpriv->spinlock, flags);

return 1;
}
Expand All @@ -1057,8 +1061,11 @@ dio200_subdev_8254_write(struct comedi_device *dev, struct comedi_subdevice *s,
{
struct dio200_subdev_8254 *subpriv = s->private;
int chan = CR_CHAN(insn->chanspec);
unsigned long flags;

spin_lock_irqsave(&subpriv->spinlock, flags);
i8254_write(subpriv->iobase, 0, chan, data[0]);
spin_unlock_irqrestore(&subpriv->spinlock, flags);

return 1;
}
Expand Down Expand Up @@ -1151,45 +1158,52 @@ dio200_subdev_8254_config(struct comedi_device *dev, struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
struct dio200_subdev_8254 *subpriv = s->private;
int ret;
int ret = 0;
int chan = CR_CHAN(insn->chanspec);
unsigned long flags;

spin_lock_irqsave(&subpriv->spinlock, flags);
switch (data[0]) {
case INSN_CONFIG_SET_COUNTER_MODE:
ret = i8254_set_mode(subpriv->iobase, 0, chan, data[1]);
if (ret < 0)
return -EINVAL;
ret = -EINVAL;
break;
case INSN_CONFIG_8254_READ_STATUS:
data[1] = i8254_status(subpriv->iobase, 0, chan);
break;
case INSN_CONFIG_SET_GATE_SRC:
ret = dio200_set_gate_src(subpriv, chan, data[2]);
if (ret < 0)
return -EINVAL;
ret = -EINVAL;
break;
case INSN_CONFIG_GET_GATE_SRC:
ret = dio200_get_gate_src(subpriv, chan);
if (ret < 0)
return -EINVAL;
if (ret < 0) {
ret = -EINVAL;
break;
}
data[2] = ret;
break;
case INSN_CONFIG_SET_CLOCK_SRC:
ret = dio200_set_clock_src(subpriv, chan, data[1]);
if (ret < 0)
return -EINVAL;
ret = -EINVAL;
break;
case INSN_CONFIG_GET_CLOCK_SRC:
ret = dio200_get_clock_src(subpriv, chan, &data[2]);
if (ret < 0)
return -EINVAL;
if (ret < 0) {
ret = -EINVAL;
break;
}
data[1] = ret;
break;
default:
return -EINVAL;
ret = -EINVAL;
break;
}
return insn->n;
spin_unlock_irqrestore(&subpriv->spinlock, flags);
return ret < 0 ? ret : insn->n;
}

/*
Expand Down Expand Up @@ -1222,6 +1236,7 @@ dio200_subdev_8254_init(struct comedi_device *dev, struct comedi_subdevice *s,
s->insn_write = dio200_subdev_8254_write;
s->insn_config = dio200_subdev_8254_config;

spin_lock_init(&subpriv->spinlock);
subpriv->iobase = offset + iobase;
subpriv->has_clk_gat_sce = has_clk_gat_sce;
if (has_clk_gat_sce) {
Expand Down

0 comments on commit ee4063f

Please sign in to comment.