1/* 2 * comedi/drivers/pcl730.c 3 * Driver for Advantech PCL-730 and clones 4 * José Luis Sánchez 5 */ 6/* 7Driver: pcl730 8Description: Advantech PCL-730 (& compatibles) 9Author: José Luis Sánchez (jsanchezv@teleline.es) 10Status: untested 11Devices: [Advantech] PCL-730 (pcl730), [ICP] ISO-730 (iso730), 12 [Adlink] ACL-7130 (acl7130) 13 14Interrupts are not supported. 15The ACL-7130 card have an 8254 timer/counter not supported by this driver. 16*/ 17 18#include "../comedidev.h" 19 20#include <linux/ioport.h> 21 22#define PCL730_SIZE 4 23#define ACL7130_SIZE 8 24#define PCL730_IDIO_LO 0 /* Isolated Digital I/O low byte (ID0-ID7) */ 25#define PCL730_IDIO_HI 1 /* Isolated Digital I/O high byte (ID8-ID15) */ 26#define PCL730_DIO_LO 2 /* TTL Digital I/O low byte (D0-D7) */ 27#define PCL730_DIO_HI 3 /* TTL Digital I/O high byte (D8-D15) */ 28 29static int pcl730_attach(struct comedi_device *dev, 30 struct comedi_devconfig *it); 31static int pcl730_detach(struct comedi_device *dev); 32 33struct pcl730_board { 34 35 const char *name; /* board name */ 36 unsigned int io_range; /* len of I/O space */ 37}; 38 39static const struct pcl730_board boardtypes[] = { 40 {"pcl730", PCL730_SIZE,}, 41 {"iso730", PCL730_SIZE,}, 42 {"acl7130", ACL7130_SIZE,}, 43}; 44 45#define n_boardtypes (sizeof(boardtypes)/sizeof(struct pcl730_board)) 46#define this_board ((const struct pcl730_board *)dev->board_ptr) 47 48static struct comedi_driver driver_pcl730 = { 49 .driver_name = "pcl730", 50 .module = THIS_MODULE, 51 .attach = pcl730_attach, 52 .detach = pcl730_detach, 53 .board_name = &boardtypes[0].name, 54 .num_names = n_boardtypes, 55 .offset = sizeof(struct pcl730_board), 56}; 57 58static int __init driver_pcl730_init_module(void) 59{ 60 return comedi_driver_register(&driver_pcl730); 61} 62 63static void __exit driver_pcl730_cleanup_module(void) 64{ 65 comedi_driver_unregister(&driver_pcl730); 66} 67 68module_init(driver_pcl730_init_module); 69module_exit(driver_pcl730_cleanup_module); 70 71static int pcl730_do_insn(struct comedi_device *dev, struct comedi_subdevice *s, 72 struct comedi_insn *insn, unsigned int *data) 73{ 74 if (insn->n != 2) 75 return -EINVAL; 76 77 if (data[0]) { 78 s->state &= ~data[0]; 79 s->state |= (data[0] & data[1]); 80 } 81 if (data[0] & 0x00ff) 82 outb(s->state & 0xff, 83 dev->iobase + ((unsigned long)s->private)); 84 if (data[0] & 0xff00) 85 outb((s->state >> 8), 86 dev->iobase + ((unsigned long)s->private) + 1); 87 88 data[1] = s->state; 89 90 return 2; 91} 92 93static int pcl730_di_insn(struct comedi_device *dev, struct comedi_subdevice *s, 94 struct comedi_insn *insn, unsigned int *data) 95{ 96 if (insn->n != 2) 97 return -EINVAL; 98 99 data[1] = inb(dev->iobase + ((unsigned long)s->private)) | 100 (inb(dev->iobase + ((unsigned long)s->private) + 1) << 8); 101 102 return 2; 103} 104 105static int pcl730_attach(struct comedi_device *dev, struct comedi_devconfig *it) 106{ 107 struct comedi_subdevice *s; 108 unsigned long iobase; 109 unsigned int iorange; 110 111 iobase = it->options[0]; 112 iorange = this_board->io_range; 113 printk(KERN_INFO "comedi%d: pcl730: board=%s 0x%04lx ", dev->minor, 114 this_board->name, iobase); 115 if (!request_region(iobase, iorange, "pcl730")) { 116 printk("I/O port conflict\n"); 117 return -EIO; 118 } 119 dev->board_name = this_board->name; 120 dev->iobase = iobase; 121 dev->irq = 0; 122 123 if (alloc_subdevices(dev, 4) < 0) 124 return -ENOMEM; 125 126 s = dev->subdevices + 0; 127 /* Isolated do */ 128 s->type = COMEDI_SUBD_DO; 129 s->subdev_flags = SDF_WRITABLE; 130 s->maxdata = 1; 131 s->n_chan = 16; 132 s->insn_bits = pcl730_do_insn; 133 s->range_table = &range_digital; 134 s->private = (void *)PCL730_IDIO_LO; 135 136 s = dev->subdevices + 1; 137 /* Isolated di */ 138 s->type = COMEDI_SUBD_DI; 139 s->subdev_flags = SDF_READABLE; 140 s->maxdata = 1; 141 s->n_chan = 16; 142 s->insn_bits = pcl730_di_insn; 143 s->range_table = &range_digital; 144 s->private = (void *)PCL730_IDIO_LO; 145 146 s = dev->subdevices + 2; 147 /* TTL do */ 148 s->type = COMEDI_SUBD_DO; 149 s->subdev_flags = SDF_WRITABLE; 150 s->maxdata = 1; 151 s->n_chan = 16; 152 s->insn_bits = pcl730_do_insn; 153 s->range_table = &range_digital; 154 s->private = (void *)PCL730_DIO_LO; 155 156 s = dev->subdevices + 3; 157 /* TTL di */ 158 s->type = COMEDI_SUBD_DI; 159 s->subdev_flags = SDF_READABLE; 160 s->maxdata = 1; 161 s->n_chan = 16; 162 s->insn_bits = pcl730_di_insn; 163 s->range_table = &range_digital; 164 s->private = (void *)PCL730_DIO_LO; 165 166 printk(KERN_INFO "\n"); 167 168 return 0; 169} 170 171static int pcl730_detach(struct comedi_device *dev) 172{ 173 printk(KERN_INFO "comedi%d: pcl730: remove\n", dev->minor); 174 175 if (dev->iobase) 176 release_region(dev->iobase, this_board->io_range); 177 178 return 0; 179} 180 181MODULE_AUTHOR("Comedi http://www.comedi.org"); 182MODULE_DESCRIPTION("Comedi low-level driver"); 183MODULE_LICENSE("GPL"); 184