diff options
Diffstat (limited to 'drivers/staging/comedi/drivers/8255.c')
-rw-r--r-- | drivers/staging/comedi/drivers/8255.c | 203 |
1 files changed, 104 insertions, 99 deletions
diff --git a/drivers/staging/comedi/drivers/8255.c b/drivers/staging/comedi/drivers/8255.c index 27e39e4eb6b3..4c9977b8a5ae 100644 --- a/drivers/staging/comedi/drivers/8255.c +++ b/drivers/staging/comedi/drivers/8255.c @@ -60,15 +60,15 @@ I/O port base address can be found in the output of 'lspci -v'. set up the subdevice in the attach function of the driver by calling: - subdev_8255_init(device, subdevice, callback_function, arg) + subdev_8255_init(device, subdevice, io_function, iobase) device and subdevice are pointers to the device and subdevice - structures. callback_function will be called to provide the + structures. io_function will be called to provide the low-level input/output to the device, i.e., actual register - access. callback_function will be called with the value of arg + access. io_function will be called with the value of iobase as the last parameter. If the 8255 device is mapped as 4 - consecutive I/O ports, you can use NULL for callback_function - and the I/O port base for arg, and an internal function will + consecutive I/O ports, you can use NULL for io_function + and the I/O port base for iobase, and an internal function will handle the register access. In addition, if the main driver handles interrupts, you can @@ -84,10 +84,10 @@ I/O port base address can be found in the output of 'lspci -v'. #include <linux/slab.h> #include "8255.h" -#define _8255_SIZE 4 +#define _8255_SIZE 4 -#define _8255_DATA 0 -#define _8255_CR 3 +#define _8255_DATA 0 +#define _8255_CR 3 #define CR_C_LO_IO 0x01 #define CR_B_IO 0x02 @@ -97,23 +97,30 @@ I/O port base address can be found in the output of 'lspci -v'. #define CR_A_MODE(a) ((a)<<5) #define CR_CW 0x80 -struct subdev_8255_struct { - unsigned long cb_arg; - int (*cb_func) (int, int, int, unsigned long); - int have_irq; +struct subdev_8255_private { + unsigned long iobase; + int (*io) (int, int, int, unsigned long); }; -#define CALLBACK_ARG (((struct subdev_8255_struct *)s->private)->cb_arg) -#define CALLBACK_FUNC (((struct subdev_8255_struct *)s->private)->cb_func) -#define subdevpriv ((struct subdev_8255_struct *)s->private) +static int subdev_8255_io(int dir, int port, int data, unsigned long iobase) +{ + if (dir) { + outb(data, iobase + port); + return 0; + } else { + return inb(iobase + port); + } +} void subdev_8255_interrupt(struct comedi_device *dev, struct comedi_subdevice *s) { + struct subdev_8255_private *spriv = s->private; + unsigned long iobase = spriv->iobase; short d; - d = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG); - d |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8); + d = spriv->io(0, _8255_DATA, 0, iobase); + d |= (spriv->io(0, _8255_DATA + 1, 0, iobase) << 8); comedi_buf_put(s->async, d); s->async->events |= COMEDI_CB_EOS; @@ -122,46 +129,48 @@ void subdev_8255_interrupt(struct comedi_device *dev, } EXPORT_SYMBOL(subdev_8255_interrupt); -static int subdev_8255_cb(int dir, int port, int data, unsigned long arg) -{ - unsigned long iobase = arg; - - if (dir) { - outb(data, iobase + port); - return 0; - } else { - return inb(iobase + port); - } -} - static int subdev_8255_insn(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_insn *insn, unsigned int *data) { - if (data[0]) { - s->state &= ~data[0]; - s->state |= (data[0] & data[1]); - - if (data[0] & 0xff) - CALLBACK_FUNC(1, _8255_DATA, s->state & 0xff, - CALLBACK_ARG); - if (data[0] & 0xff00) - CALLBACK_FUNC(1, _8255_DATA + 1, (s->state >> 8) & 0xff, - CALLBACK_ARG); - if (data[0] & 0xff0000) - CALLBACK_FUNC(1, _8255_DATA + 2, - (s->state >> 16) & 0xff, CALLBACK_ARG); + struct subdev_8255_private *spriv = s->private; + unsigned long iobase = spriv->iobase; + unsigned int mask; + unsigned int bits; + unsigned int v; + + mask = data[0]; + bits = data[1]; + + if (mask) { + v = s->state; + v &= ~mask; + v |= (bits & mask); + + if (mask & 0xff) + spriv->io(1, _8255_DATA, v & 0xff, iobase); + if (mask & 0xff00) + spriv->io(1, _8255_DATA + 1, (v >> 8) & 0xff, iobase); + if (mask & 0xff0000) + spriv->io(1, _8255_DATA + 2, (v >> 16) & 0xff, iobase); + + s->state = v; } - data[1] = CALLBACK_FUNC(0, _8255_DATA, 0, CALLBACK_ARG); - data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 1, 0, CALLBACK_ARG) << 8); - data[1] |= (CALLBACK_FUNC(0, _8255_DATA + 2, 0, CALLBACK_ARG) << 16); + v = spriv->io(0, _8255_DATA, 0, iobase); + v |= (spriv->io(0, _8255_DATA + 1, 0, iobase) << 8); + v |= (spriv->io(0, _8255_DATA + 2, 0, iobase) << 16); + + data[1] = v; - return 2; + return insn->n; } -static void do_config(struct comedi_device *dev, struct comedi_subdevice *s) +static void subdev_8255_do_config(struct comedi_device *dev, + struct comedi_subdevice *s) { + struct subdev_8255_private *spriv = s->private; + unsigned long iobase = spriv->iobase; int config; config = CR_CW; @@ -174,7 +183,8 @@ static void do_config(struct comedi_device *dev, struct comedi_subdevice *s) config |= CR_C_LO_IO; if (!(s->io_bits & 0xf00000)) config |= CR_C_HI_IO; - CALLBACK_FUNC(1, _8255_CR, config, CALLBACK_ARG); + + spriv->io(1, _8255_CR, config, iobase); } static int subdev_8255_insn_config(struct comedi_device *dev, @@ -209,7 +219,7 @@ static int subdev_8255_insn_config(struct comedi_device *dev, return -EINVAL; } - do_config(dev, s); + subdev_8255_do_config(dev, s); return 1; } @@ -307,50 +317,50 @@ static int subdev_8255_cancel(struct comedi_device *dev, } int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s, - int (*cb) (int, int, int, unsigned long), - unsigned long arg) + int (*io) (int, int, int, unsigned long), + unsigned long iobase) { - s->type = COMEDI_SUBD_DIO; - s->subdev_flags = SDF_READABLE | SDF_WRITABLE; - s->n_chan = 24; - s->range_table = &range_digital; - s->maxdata = 1; - - s->private = kmalloc(sizeof(struct subdev_8255_struct), GFP_KERNEL); - if (!s->private) + struct subdev_8255_private *spriv; + + spriv = kzalloc(sizeof(*spriv), GFP_KERNEL); + if (!spriv) return -ENOMEM; - CALLBACK_ARG = arg; - if (cb == NULL) - CALLBACK_FUNC = subdev_8255_cb; - else - CALLBACK_FUNC = cb; - s->insn_bits = subdev_8255_insn; - s->insn_config = subdev_8255_insn_config; + spriv->iobase = iobase; + spriv->io = io ? io : subdev_8255_io; + + s->private = spriv; + + s->type = COMEDI_SUBD_DIO; + s->subdev_flags = SDF_READABLE | SDF_WRITABLE; + s->n_chan = 24; + s->range_table = &range_digital; + s->maxdata = 1; + s->insn_bits = subdev_8255_insn; + s->insn_config = subdev_8255_insn_config; - s->state = 0; - s->io_bits = 0; - do_config(dev, s); + s->state = 0; + s->io_bits = 0; + + subdev_8255_do_config(dev, s); return 0; } EXPORT_SYMBOL(subdev_8255_init); int subdev_8255_init_irq(struct comedi_device *dev, struct comedi_subdevice *s, - int (*cb) (int, int, int, unsigned long), - unsigned long arg) + int (*io) (int, int, int, unsigned long), + unsigned long iobase) { int ret; - ret = subdev_8255_init(dev, s, cb, arg); - if (ret < 0) + ret = subdev_8255_init(dev, s, io, iobase); + if (ret) return ret; - s->do_cmdtest = subdev_8255_cmdtest; - s->do_cmd = subdev_8255_cmd; - s->cancel = subdev_8255_cancel; - - subdevpriv->have_irq = 1; + s->do_cmdtest = subdev_8255_cmdtest; + s->do_cmd = subdev_8255_cmd; + s->cancel = subdev_8255_cancel; return 0; } @@ -371,6 +381,7 @@ EXPORT_SYMBOL(subdev_8255_cleanup); static int dev_8255_attach(struct comedi_device *dev, struct comedi_devconfig *it) { + struct comedi_subdevice *s; int ret; unsigned long iobase; int i; @@ -383,51 +394,45 @@ static int dev_8255_attach(struct comedi_device *dev, break; } if (i == 0) { - printk(KERN_WARNING - "comedi%d: 8255: no devices specified\n", dev->minor); + dev_warn(dev->class_dev, "no devices specified\n"); return -EINVAL; } - ret = alloc_subdevices(dev, i); - if (ret < 0) { - /* FIXME this printk call should give a proper message, the - * below line just maintains previous functionality */ - printk("comedi%d: 8255:", dev->minor); + ret = comedi_alloc_subdevices(dev, i); + if (ret) return ret; - } - - printk(KERN_INFO "comedi%d: 8255:", dev->minor); for (i = 0; i < dev->n_subdevices; i++) { + s = dev->subdevices + i; iobase = it->options[i]; - printk(" 0x%04lx", iobase); if (!request_region(iobase, _8255_SIZE, "8255")) { - printk(" (I/O port conflict)"); + dev_warn(dev->class_dev, + "0x%04lx (I/O port conflict)\n", iobase); - dev->subdevices[i].type = COMEDI_SUBD_UNUSED; + s->type = COMEDI_SUBD_UNUSED; } else { - subdev_8255_init(dev, dev->subdevices + i, NULL, - iobase); + ret = subdev_8255_init(dev, s, NULL, iobase); + if (ret) + return ret; + dev_info(dev->class_dev, "0x%04lx\n", iobase); } } - printk("\n"); - return 0; } static void dev_8255_detach(struct comedi_device *dev) { - int i; - unsigned long iobase; struct comedi_subdevice *s; + struct subdev_8255_private *spriv; + int i; for (i = 0; i < dev->n_subdevices; i++) { s = dev->subdevices + i; if (s->type != COMEDI_SUBD_UNUSED) { - iobase = CALLBACK_ARG; - release_region(iobase, _8255_SIZE); + spriv = s->private; + release_region(spriv->iobase, _8255_SIZE); } subdev_8255_cleanup(dev, s); } |