amplc_pc263.c revision abdedefed70a6b5f20e40d3d5847a6d9c605bf4c
1/* 2 comedi/drivers/amplc_pc263.c 3 Driver for Amplicon PC263 and PCI263 relay boards. 4 5 Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/> 6 7 COMEDI - Linux Control and Measurement Device Interface 8 Copyright (C) 2000 David A. Schleef <ds@schleef.org> 9 10 This program is free software; you can redistribute it and/or modify 11 it under the terms of the GNU General Public License as published by 12 the Free Software Foundation; either version 2 of the License, or 13 (at your option) any later version. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program; if not, write to the Free Software 22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 24*/ 25/* 26Driver: amplc_pc263 27Description: Amplicon PC263, PCI263 28Author: Ian Abbott <abbotti@mev.co.uk> 29Devices: [Amplicon] PC263 (pc263), PCI263 (pci263 or amplc_pc263) 30Updated: Wed, 22 Oct 2008 14:10:53 +0100 31Status: works 32 33Configuration options - PC263: 34 [0] - I/O port base address 35 36Configuration options - PCI263: 37 [0] - PCI bus of device (optional) 38 [1] - PCI slot of device (optional) 39 If bus/slot is not specified, the first available PCI device will be 40 used. 41 42Each board appears as one subdevice, with 16 digital outputs, each 43connected to a reed-relay. Relay contacts are closed when output is 1. 44The state of the outputs can be read. 45*/ 46 47#include "../comedidev.h" 48 49#include "comedi_pci.h" 50 51#define PC263_DRIVER_NAME "amplc_pc263" 52 53/* PCI263 PCI configuration register information */ 54#define PCI_VENDOR_ID_AMPLICON 0x14dc 55#define PCI_DEVICE_ID_AMPLICON_PCI263 0x000c 56#define PCI_DEVICE_ID_INVALID 0xffff 57 58/* PC263 / PCI263 registers */ 59#define PC263_IO_SIZE 2 60 61/* 62 * Board descriptions for Amplicon PC263 / PCI263. 63 */ 64 65enum pc263_bustype { isa_bustype, pci_bustype }; 66enum pc263_model { pc263_model, pci263_model, anypci_model }; 67 68struct pc263_board { 69 const char *name; 70 const char *fancy_name; 71 unsigned short devid; 72 enum pc263_bustype bustype; 73 enum pc263_model model; 74}; 75static const struct pc263_board pc263_boards[] = { 76 { 77 .name = "pc263", 78 .fancy_name = "PC263", 79 .bustype = isa_bustype, 80 .model = pc263_model, 81 }, 82#ifdef CONFIG_COMEDI_PCI 83 { 84 .name = "pci263", 85 .fancy_name = "PCI263", 86 .devid = PCI_DEVICE_ID_AMPLICON_PCI263, 87 .bustype = pci_bustype, 88 .model = pci263_model, 89 }, 90#endif 91#ifdef CONFIG_COMEDI_PCI 92 { 93 .name = PC263_DRIVER_NAME, 94 .fancy_name = PC263_DRIVER_NAME, 95 .devid = PCI_DEVICE_ID_INVALID, 96 .bustype = pci_bustype, 97 .model = anypci_model, /* wildcard */ 98 }, 99#endif 100}; 101 102#ifdef CONFIG_COMEDI_PCI 103static DEFINE_PCI_DEVICE_TABLE(pc263_pci_table) = { 104 { 105 PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI263, 106 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { 107 0} 108}; 109 110MODULE_DEVICE_TABLE(pci, pc263_pci_table); 111#endif /* CONFIG_COMEDI_PCI */ 112 113/* 114 * Useful for shorthand access to the particular board structure 115 */ 116#define thisboard ((const struct pc263_board *)dev->board_ptr) 117 118/* this structure is for data unique to this hardware driver. If 119 several hardware drivers keep similar information in this structure, 120 feel free to suggest moving the variable to the struct comedi_device struct. 121*/ 122#ifdef CONFIG_COMEDI_PCI 123struct pc263_private { 124 /* PCI device. */ 125 struct pci_dev *pci_dev; 126}; 127 128#define devpriv ((struct pc263_private *)dev->private) 129#endif /* CONFIG_COMEDI_PCI */ 130 131/* 132 * The struct comedi_driver structure tells the Comedi core module 133 * which functions to call to configure/deconfigure (attach/detach) 134 * the board, and also about the kernel module that contains 135 * the device code. 136 */ 137static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it); 138static int pc263_detach(struct comedi_device *dev); 139static struct comedi_driver driver_amplc_pc263 = { 140 .driver_name = PC263_DRIVER_NAME, 141 .module = THIS_MODULE, 142 .attach = pc263_attach, 143 .detach = pc263_detach, 144 .board_name = &pc263_boards[0].name, 145 .offset = sizeof(struct pc263_board), 146 .num_names = ARRAY_SIZE(pc263_boards), 147}; 148 149static int pc263_request_region(unsigned minor, unsigned long from, 150 unsigned long extent); 151static int pc263_dio_insn_bits(struct comedi_device *dev, 152 struct comedi_subdevice *s, 153 struct comedi_insn *insn, unsigned int *data); 154static int pc263_dio_insn_config(struct comedi_device *dev, 155 struct comedi_subdevice *s, 156 struct comedi_insn *insn, unsigned int *data); 157 158/* 159 * This function looks for a PCI device matching the requested board name, 160 * bus and slot. 161 */ 162#ifdef CONFIG_COMEDI_PCI 163static int 164pc263_find_pci(struct comedi_device *dev, int bus, int slot, 165 struct pci_dev **pci_dev_p) 166{ 167 struct pci_dev *pci_dev = NULL; 168 169 *pci_dev_p = NULL; 170 171 /* Look for matching PCI device. */ 172 for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL); 173 pci_dev != NULL; 174 pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, 175 PCI_ANY_ID, pci_dev)) { 176 /* If bus/slot specified, check them. */ 177 if (bus || slot) { 178 if (bus != pci_dev->bus->number 179 || slot != PCI_SLOT(pci_dev->devfn)) 180 continue; 181 } 182 if (thisboard->model == anypci_model) { 183 /* Match any supported model. */ 184 int i; 185 186 for (i = 0; i < ARRAY_SIZE(pc263_boards); i++) { 187 if (pc263_boards[i].bustype != pci_bustype) 188 continue; 189 if (pci_dev->device == pc263_boards[i].devid) { 190 /* Change board_ptr to matched board. */ 191 dev->board_ptr = &pc263_boards[i]; 192 break; 193 } 194 } 195 if (i == ARRAY_SIZE(pc263_boards)) 196 continue; 197 } else { 198 /* Match specific model name. */ 199 if (pci_dev->device != thisboard->devid) 200 continue; 201 } 202 203 /* Found a match. */ 204 *pci_dev_p = pci_dev; 205 return 0; 206 } 207 /* No match found. */ 208 if (bus || slot) { 209 printk(KERN_ERR 210 "comedi%d: error! no %s found at pci %02x:%02x!\n", 211 dev->minor, thisboard->name, bus, slot); 212 } else { 213 printk(KERN_ERR "comedi%d: error! no %s found!\n", 214 dev->minor, thisboard->name); 215 } 216 return -EIO; 217} 218#endif 219 220/* 221 * Attach is called by the Comedi core to configure the driver 222 * for a particular board. If you specified a board_name array 223 * in the driver structure, dev->board_ptr contains that 224 * address. 225 */ 226static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it) 227{ 228 struct comedi_subdevice *s; 229 unsigned long iobase = 0; 230#ifdef CONFIG_COMEDI_PCI 231 struct pci_dev *pci_dev = NULL; 232 int bus = 0, slot = 0; 233#endif 234 int ret; 235 236 printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor, 237 PC263_DRIVER_NAME); 238/* 239 * Allocate the private structure area. alloc_private() is a 240 * convenient macro defined in comedidev.h. 241 */ 242#ifdef CONFIG_COMEDI_PCI 243 ret = alloc_private(dev, sizeof(struct pc263_private)); 244 if (ret < 0) { 245 printk(KERN_ERR "comedi%d: error! out of memory!\n", 246 dev->minor); 247 return ret; 248 } 249#endif 250 /* Process options. */ 251 switch (thisboard->bustype) { 252 case isa_bustype: 253 iobase = it->options[0]; 254 break; 255#ifdef CONFIG_COMEDI_PCI 256 case pci_bustype: 257 bus = it->options[0]; 258 slot = it->options[1]; 259 260 ret = pc263_find_pci(dev, bus, slot, &pci_dev); 261 if (ret < 0) 262 return ret; 263 devpriv->pci_dev = pci_dev; 264 break; 265#endif /* CONFIG_COMEDI_PCI */ 266 default: 267 printk(KERN_ERR 268 "comedi%d: %s: BUG! cannot determine board type!\n", 269 dev->minor, PC263_DRIVER_NAME); 270 return -EINVAL; 271 break; 272 } 273 274/* 275 * Initialize dev->board_name. 276 */ 277 dev->board_name = thisboard->name; 278 279 /* Enable device and reserve I/O spaces. */ 280#ifdef CONFIG_COMEDI_PCI 281 if (pci_dev) { 282 ret = comedi_pci_enable(pci_dev, PC263_DRIVER_NAME); 283 if (ret < 0) { 284 printk(KERN_ERR 285 "comedi%d: error! cannot enable PCI device and " 286 "request regions!\n", 287 dev->minor); 288 return ret; 289 } 290 iobase = pci_resource_start(pci_dev, 2); 291 } else 292#endif 293 { 294 ret = pc263_request_region(dev->minor, iobase, PC263_IO_SIZE); 295 if (ret < 0) { 296 return ret; 297 } 298 } 299 dev->iobase = iobase; 300 301/* 302 * Allocate the subdevice structures. alloc_subdevice() is a 303 * convenient macro defined in comedidev.h. 304 */ 305 ret = alloc_subdevices(dev, 1); 306 if (ret < 0) { 307 printk(KERN_ERR "comedi%d: error! out of memory!\n", 308 dev->minor); 309 return ret; 310 } 311 312 s = dev->subdevices + 0; 313 /* digital i/o subdevice */ 314 s->type = COMEDI_SUBD_DIO; 315 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 316 s->n_chan = 16; 317 s->maxdata = 1; 318 s->range_table = &range_digital; 319 s->insn_bits = pc263_dio_insn_bits; 320 s->insn_config = pc263_dio_insn_config; 321 /* all outputs */ 322 s->io_bits = 0xffff; 323 /* read initial relay state */ 324 s->state = inb(dev->iobase); 325 s->state = s->state | (inb(dev->iobase) << 8); 326 327 printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name); 328 if (thisboard->bustype == isa_bustype) { 329 printk("(base %#lx) ", iobase); 330 } else { 331#ifdef CONFIG_COMEDI_PCI 332 printk("(pci %s) ", pci_name(pci_dev)); 333#endif 334 } 335 336 printk("attached\n"); 337 338 return 1; 339} 340 341/* 342 * _detach is called to deconfigure a device. It should deallocate 343 * resources. 344 * This function is also called when _attach() fails, so it should be 345 * careful not to release resources that were not necessarily 346 * allocated by _attach(). dev->private and dev->subdevices are 347 * deallocated automatically by the core. 348 */ 349static int pc263_detach(struct comedi_device *dev) 350{ 351 printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor, 352 PC263_DRIVER_NAME); 353 354#ifdef CONFIG_COMEDI_PCI 355 if (devpriv) 356#endif 357 { 358#ifdef CONFIG_COMEDI_PCI 359 if (devpriv->pci_dev) { 360 if (dev->iobase) { 361 comedi_pci_disable(devpriv->pci_dev); 362 } 363 pci_dev_put(devpriv->pci_dev); 364 } else 365#endif 366 { 367 if (dev->iobase) { 368 release_region(dev->iobase, PC263_IO_SIZE); 369 } 370 } 371 } 372 if (dev->board_name) { 373 printk(KERN_INFO "comedi%d: %s removed\n", 374 dev->minor, dev->board_name); 375 } 376 return 0; 377} 378 379/* 380 * This function checks and requests an I/O region, reporting an error 381 * if there is a conflict. 382 */ 383static int pc263_request_region(unsigned minor, unsigned long from, 384 unsigned long extent) 385{ 386 if (!from || !request_region(from, extent, PC263_DRIVER_NAME)) { 387 printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n", 388 minor, from, extent); 389 return -EIO; 390 } 391 return 0; 392} 393 394/* DIO devices are slightly special. Although it is possible to 395 * implement the insn_read/insn_write interface, it is much more 396 * useful to applications if you implement the insn_bits interface. 397 * This allows packed reading/writing of the DIO channels. The 398 * comedi core can convert between insn_bits and insn_read/write */ 399static int pc263_dio_insn_bits(struct comedi_device *dev, 400 struct comedi_subdevice *s, 401 struct comedi_insn *insn, unsigned int *data) 402{ 403 if (insn->n != 2) 404 return -EINVAL; 405 406 /* The insn data is a mask in data[0] and the new data 407 * in data[1], each channel cooresponding to a bit. */ 408 if (data[0]) { 409 s->state &= ~data[0]; 410 s->state |= data[0] & data[1]; 411 /* Write out the new digital output lines */ 412 outb(s->state & 0xFF, dev->iobase); 413 outb(s->state >> 8, dev->iobase + 1); 414 } 415 416 /* on return, data[1] contains the value of the digital 417 * input and output lines. */ 418 /* or we could just return the software copy of the output values if 419 * it was a purely digital output subdevice */ 420 data[1] = s->state; 421 422 return 2; 423} 424 425static int pc263_dio_insn_config(struct comedi_device *dev, 426 struct comedi_subdevice *s, 427 struct comedi_insn *insn, unsigned int *data) 428{ 429 if (insn->n != 1) 430 return -EINVAL; 431 return 1; 432} 433 434/* 435 * A convenient macro that defines init_module() and cleanup_module(), 436 * as necessary. 437 */ 438#ifdef CONFIG_COMEDI_PCI 439COMEDI_PCI_INITCLEANUP(driver_amplc_pc263, pc263_pci_table); 440#else 441COMEDI_INITCLEANUP(driver_amplc_pc263); 442#endif 443