amplc_pc263.c revision 7b0be12b26d86ea2bd5079f0d723289e2f5a43a9
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 { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI263) }, 105 {0} 106}; 107 108MODULE_DEVICE_TABLE(pci, pc263_pci_table); 109#endif /* CONFIG_COMEDI_PCI */ 110 111/* 112 * Useful for shorthand access to the particular board structure 113 */ 114#define thisboard ((const struct pc263_board *)dev->board_ptr) 115 116/* this structure is for data unique to this hardware driver. If 117 several hardware drivers keep similar information in this structure, 118 feel free to suggest moving the variable to the struct comedi_device struct. 119*/ 120#ifdef CONFIG_COMEDI_PCI 121struct pc263_private { 122 /* PCI device. */ 123 struct pci_dev *pci_dev; 124}; 125 126#define devpriv ((struct pc263_private *)dev->private) 127#endif /* CONFIG_COMEDI_PCI */ 128 129/* 130 * The struct comedi_driver structure tells the Comedi core module 131 * which functions to call to configure/deconfigure (attach/detach) 132 * the board, and also about the kernel module that contains 133 * the device code. 134 */ 135static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it); 136static int pc263_detach(struct comedi_device *dev); 137static struct comedi_driver driver_amplc_pc263 = { 138 .driver_name = PC263_DRIVER_NAME, 139 .module = THIS_MODULE, 140 .attach = pc263_attach, 141 .detach = pc263_detach, 142 .board_name = &pc263_boards[0].name, 143 .offset = sizeof(struct pc263_board), 144 .num_names = ARRAY_SIZE(pc263_boards), 145}; 146 147static int pc263_request_region(unsigned minor, unsigned long from, 148 unsigned long extent); 149static int pc263_dio_insn_bits(struct comedi_device *dev, 150 struct comedi_subdevice *s, 151 struct comedi_insn *insn, unsigned int *data); 152static int pc263_dio_insn_config(struct comedi_device *dev, 153 struct comedi_subdevice *s, 154 struct comedi_insn *insn, unsigned int *data); 155 156/* 157 * This function looks for a PCI device matching the requested board name, 158 * bus and slot. 159 */ 160#ifdef CONFIG_COMEDI_PCI 161static int 162pc263_find_pci(struct comedi_device *dev, int bus, int slot, 163 struct pci_dev **pci_dev_p) 164{ 165 struct pci_dev *pci_dev = NULL; 166 167 *pci_dev_p = NULL; 168 169 /* Look for matching PCI device. */ 170 for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL); 171 pci_dev != NULL; 172 pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, 173 PCI_ANY_ID, pci_dev)) { 174 /* If bus/slot specified, check them. */ 175 if (bus || slot) { 176 if (bus != pci_dev->bus->number 177 || slot != PCI_SLOT(pci_dev->devfn)) 178 continue; 179 } 180 if (thisboard->model == anypci_model) { 181 /* Match any supported model. */ 182 int i; 183 184 for (i = 0; i < ARRAY_SIZE(pc263_boards); i++) { 185 if (pc263_boards[i].bustype != pci_bustype) 186 continue; 187 if (pci_dev->device == pc263_boards[i].devid) { 188 /* Change board_ptr to matched board. */ 189 dev->board_ptr = &pc263_boards[i]; 190 break; 191 } 192 } 193 if (i == ARRAY_SIZE(pc263_boards)) 194 continue; 195 } else { 196 /* Match specific model name. */ 197 if (pci_dev->device != thisboard->devid) 198 continue; 199 } 200 201 /* Found a match. */ 202 *pci_dev_p = pci_dev; 203 return 0; 204 } 205 /* No match found. */ 206 if (bus || slot) { 207 printk(KERN_ERR 208 "comedi%d: error! no %s found at pci %02x:%02x!\n", 209 dev->minor, thisboard->name, bus, slot); 210 } else { 211 printk(KERN_ERR "comedi%d: error! no %s found!\n", 212 dev->minor, thisboard->name); 213 } 214 return -EIO; 215} 216#endif 217 218/* 219 * Attach is called by the Comedi core to configure the driver 220 * for a particular board. If you specified a board_name array 221 * in the driver structure, dev->board_ptr contains that 222 * address. 223 */ 224static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it) 225{ 226 struct comedi_subdevice *s; 227 unsigned long iobase = 0; 228#ifdef CONFIG_COMEDI_PCI 229 struct pci_dev *pci_dev = NULL; 230 int bus = 0, slot = 0; 231#endif 232 int ret; 233 234 printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor, 235 PC263_DRIVER_NAME); 236/* 237 * Allocate the private structure area. alloc_private() is a 238 * convenient macro defined in comedidev.h. 239 */ 240#ifdef CONFIG_COMEDI_PCI 241 ret = alloc_private(dev, sizeof(struct pc263_private)); 242 if (ret < 0) { 243 printk(KERN_ERR "comedi%d: error! out of memory!\n", 244 dev->minor); 245 return ret; 246 } 247#endif 248 /* Process options. */ 249 switch (thisboard->bustype) { 250 case isa_bustype: 251 iobase = it->options[0]; 252 break; 253#ifdef CONFIG_COMEDI_PCI 254 case pci_bustype: 255 bus = it->options[0]; 256 slot = it->options[1]; 257 258 ret = pc263_find_pci(dev, bus, slot, &pci_dev); 259 if (ret < 0) 260 return ret; 261 devpriv->pci_dev = pci_dev; 262 break; 263#endif /* CONFIG_COMEDI_PCI */ 264 default: 265 printk(KERN_ERR 266 "comedi%d: %s: BUG! cannot determine board type!\n", 267 dev->minor, PC263_DRIVER_NAME); 268 return -EINVAL; 269 break; 270 } 271 272/* 273 * Initialize dev->board_name. 274 */ 275 dev->board_name = thisboard->name; 276 277 /* Enable device and reserve I/O spaces. */ 278#ifdef CONFIG_COMEDI_PCI 279 if (pci_dev) { 280 ret = comedi_pci_enable(pci_dev, PC263_DRIVER_NAME); 281 if (ret < 0) { 282 printk(KERN_ERR 283 "comedi%d: error! cannot enable PCI device and " 284 "request regions!\n", 285 dev->minor); 286 return ret; 287 } 288 iobase = pci_resource_start(pci_dev, 2); 289 } else 290#endif 291 { 292 ret = pc263_request_region(dev->minor, iobase, PC263_IO_SIZE); 293 if (ret < 0) 294 return ret; 295 } 296 dev->iobase = iobase; 297 298/* 299 * Allocate the subdevice structures. alloc_subdevice() is a 300 * convenient macro defined in comedidev.h. 301 */ 302 ret = alloc_subdevices(dev, 1); 303 if (ret < 0) { 304 printk(KERN_ERR "comedi%d: error! out of memory!\n", 305 dev->minor); 306 return ret; 307 } 308 309 s = dev->subdevices + 0; 310 /* digital i/o subdevice */ 311 s->type = COMEDI_SUBD_DIO; 312 s->subdev_flags = SDF_READABLE | SDF_WRITABLE; 313 s->n_chan = 16; 314 s->maxdata = 1; 315 s->range_table = &range_digital; 316 s->insn_bits = pc263_dio_insn_bits; 317 s->insn_config = pc263_dio_insn_config; 318 /* all outputs */ 319 s->io_bits = 0xffff; 320 /* read initial relay state */ 321 s->state = inb(dev->iobase); 322 s->state = s->state | (inb(dev->iobase) << 8); 323 324 printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name); 325 if (thisboard->bustype == isa_bustype) { 326 printk("(base %#lx) ", iobase); 327 } else { 328#ifdef CONFIG_COMEDI_PCI 329 printk("(pci %s) ", pci_name(pci_dev)); 330#endif 331 } 332 333 printk("attached\n"); 334 335 return 1; 336} 337 338/* 339 * _detach is called to deconfigure a device. It should deallocate 340 * resources. 341 * This function is also called when _attach() fails, so it should be 342 * careful not to release resources that were not necessarily 343 * allocated by _attach(). dev->private and dev->subdevices are 344 * deallocated automatically by the core. 345 */ 346static int pc263_detach(struct comedi_device *dev) 347{ 348 printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor, 349 PC263_DRIVER_NAME); 350 351#ifdef CONFIG_COMEDI_PCI 352 if (devpriv) { 353#endif 354#ifdef CONFIG_COMEDI_PCI 355 if (devpriv->pci_dev) { 356 if (dev->iobase) 357 comedi_pci_disable(devpriv->pci_dev); 358 pci_dev_put(devpriv->pci_dev); 359 } else 360#endif 361 { 362 if (dev->iobase) 363 release_region(dev->iobase, PC263_IO_SIZE); 364 } 365 } 366 if (dev->board_name) { 367 printk(KERN_INFO "comedi%d: %s removed\n", 368 dev->minor, dev->board_name); 369 } 370 return 0; 371} 372 373/* 374 * This function checks and requests an I/O region, reporting an error 375 * if there is a conflict. 376 */ 377static int pc263_request_region(unsigned minor, unsigned long from, 378 unsigned long extent) 379{ 380 if (!from || !request_region(from, extent, PC263_DRIVER_NAME)) { 381 printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n", 382 minor, from, extent); 383 return -EIO; 384 } 385 return 0; 386} 387 388/* DIO devices are slightly special. Although it is possible to 389 * implement the insn_read/insn_write interface, it is much more 390 * useful to applications if you implement the insn_bits interface. 391 * This allows packed reading/writing of the DIO channels. The 392 * comedi core can convert between insn_bits and insn_read/write */ 393static int pc263_dio_insn_bits(struct comedi_device *dev, 394 struct comedi_subdevice *s, 395 struct comedi_insn *insn, unsigned int *data) 396{ 397 if (insn->n != 2) 398 return -EINVAL; 399 400 /* The insn data is a mask in data[0] and the new data 401 * in data[1], each channel cooresponding to a bit. */ 402 if (data[0]) { 403 s->state &= ~data[0]; 404 s->state |= data[0] & data[1]; 405 /* Write out the new digital output lines */ 406 outb(s->state & 0xFF, dev->iobase); 407 outb(s->state >> 8, dev->iobase + 1); 408 } 409 410 /* on return, data[1] contains the value of the digital 411 * input and output lines. */ 412 /* or we could just return the software copy of the output values if 413 * it was a purely digital output subdevice */ 414 data[1] = s->state; 415 416 return 2; 417} 418 419static int pc263_dio_insn_config(struct comedi_device *dev, 420 struct comedi_subdevice *s, 421 struct comedi_insn *insn, unsigned int *data) 422{ 423 if (insn->n != 1) 424 return -EINVAL; 425 return 1; 426} 427 428/* 429 * A convenient macro that defines init_module() and cleanup_module(), 430 * as necessary. 431 */ 432#ifdef CONFIG_COMEDI_PCI 433static int __devinit driver_amplc_pc263_pci_probe(struct pci_dev *dev, 434 const struct pci_device_id 435 *ent) 436{ 437 return comedi_pci_auto_config(dev, driver_amplc_pc263.driver_name); 438} 439 440static void __devexit driver_amplc_pc263_pci_remove(struct pci_dev *dev) 441{ 442 comedi_pci_auto_unconfig(dev); 443} 444 445static struct pci_driver driver_amplc_pc263_pci_driver = { 446 .id_table = pc263_pci_table, 447 .probe = &driver_amplc_pc263_pci_probe, 448 .remove = __devexit_p(&driver_amplc_pc263_pci_remove) 449}; 450 451static int __init driver_amplc_pc263_init_module(void) 452{ 453 int retval; 454 455 retval = comedi_driver_register(&driver_amplc_pc263); 456 if (retval < 0) 457 return retval; 458 459 driver_amplc_pc263_pci_driver.name = 460 (char *)driver_amplc_pc263.driver_name; 461 return pci_register_driver(&driver_amplc_pc263_pci_driver); 462} 463 464static void __exit driver_amplc_pc263_cleanup_module(void) 465{ 466 pci_unregister_driver(&driver_amplc_pc263_pci_driver); 467 comedi_driver_unregister(&driver_amplc_pc263); 468} 469 470module_init(driver_amplc_pc263_init_module); 471module_exit(driver_amplc_pc263_cleanup_module); 472#else 473static int __init driver_amplc_pc263_init_module(void) 474{ 475 return comedi_driver_register(&driver_amplc_pc263); 476} 477 478static void __exit driver_amplc_pc263_cleanup_module(void) 479{ 480 comedi_driver_unregister(&driver_amplc_pc263); 481} 482 483module_init(driver_amplc_pc263_init_module); 484module_exit(driver_amplc_pc263_cleanup_module); 485#endif 486 487MODULE_AUTHOR("Comedi http://www.comedi.org"); 488MODULE_DESCRIPTION("Comedi low-level driver"); 489MODULE_LICENSE("GPL"); 490