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