amplc_pc263.c revision 8629efa4cbf6f89a54a85af4b8bc31762af01800
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_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI263, PCI_ANY_ID, 105 PCI_ANY_ID, 0, 0, 0}, 106 {0} 107}; 108 109MODULE_DEVICE_TABLE(pci, pc263_pci_table); 110#endif /* CONFIG_COMEDI_PCI */ 111 112/* 113 * Useful for shorthand access to the particular board structure 114 */ 115#define thisboard ((const struct pc263_board *)dev->board_ptr) 116 117/* this structure is for data unique to this hardware driver. If 118 several hardware drivers keep similar information in this structure, 119 feel free to suggest moving the variable to the struct comedi_device struct. */ 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, struct comedi_subdevice *s, 150 struct comedi_insn *insn, unsigned int *data); 151static int pc263_dio_insn_config(struct comedi_device *dev, struct comedi_subdevice *s, 152 struct comedi_insn *insn, unsigned int *data); 153 154/* 155 * This function looks for a PCI device matching the requested board name, 156 * bus and slot. 157 */ 158#ifdef CONFIG_COMEDI_PCI 159static int 160pc263_find_pci(struct comedi_device *dev, int bus, int slot, 161 struct pci_dev **pci_dev_p) 162{ 163 struct pci_dev *pci_dev = NULL; 164 165 *pci_dev_p = NULL; 166 167 /* Look for matching PCI device. */ 168 for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL); 169 pci_dev != NULL; 170 pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, 171 PCI_ANY_ID, pci_dev)) { 172 /* If bus/slot specified, check them. */ 173 if (bus || slot) { 174 if (bus != pci_dev->bus->number 175 || slot != PCI_SLOT(pci_dev->devfn)) 176 continue; 177 } 178 if (thisboard->model == anypci_model) { 179 /* Match any supported model. */ 180 int i; 181 182 for (i = 0; i < ARRAY_SIZE(pc263_boards); i++) { 183 if (pc263_boards[i].bustype != pci_bustype) 184 continue; 185 if (pci_dev->device == pc263_boards[i].devid) { 186 /* Change board_ptr to matched board. */ 187 dev->board_ptr = &pc263_boards[i]; 188 break; 189 } 190 } 191 if (i == ARRAY_SIZE(pc263_boards)) 192 continue; 193 } else { 194 /* Match specific model name. */ 195 if (pci_dev->device != thisboard->devid) 196 continue; 197 } 198 199 /* Found a match. */ 200 *pci_dev_p = pci_dev; 201 return 0; 202 } 203 /* No match found. */ 204 if (bus || slot) { 205 printk(KERN_ERR 206 "comedi%d: error! no %s found at pci %02x:%02x!\n", 207 dev->minor, thisboard->name, bus, slot); 208 } else { 209 printk(KERN_ERR "comedi%d: error! no %s found!\n", 210 dev->minor, thisboard->name); 211 } 212 return -EIO; 213} 214#endif 215 216/* 217 * Attach is called by the Comedi core to configure the driver 218 * for a particular board. If you specified a board_name array 219 * in the driver structure, dev->board_ptr contains that 220 * address. 221 */ 222static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it) 223{ 224 struct comedi_subdevice *s; 225 unsigned long iobase = 0; 226#ifdef CONFIG_COMEDI_PCI 227 struct pci_dev *pci_dev = NULL; 228 int bus = 0, slot = 0; 229#endif 230 int ret; 231 232 printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor, 233 PC263_DRIVER_NAME); 234/* 235 * Allocate the private structure area. alloc_private() is a 236 * convenient macro defined in comedidev.h. 237 */ 238#ifdef CONFIG_COMEDI_PCI 239 ret = alloc_private(dev, sizeof(struct pc263_private)); 240 if (ret < 0) { 241 printk(KERN_ERR "comedi%d: error! out of memory!\n", 242 dev->minor); 243 return ret; 244 } 245#endif 246 /* Process options. */ 247 switch (thisboard->bustype) { 248 case isa_bustype: 249 iobase = it->options[0]; 250 break; 251#ifdef CONFIG_COMEDI_PCI 252 case pci_bustype: 253 bus = it->options[0]; 254 slot = it->options[1]; 255 256 ret = pc263_find_pci(dev, bus, slot, &pci_dev); 257 if (ret < 0) 258 return ret; 259 devpriv->pci_dev = pci_dev; 260 break; 261#endif /* CONFIG_COMEDI_PCI */ 262 default: 263 printk(KERN_ERR 264 "comedi%d: %s: BUG! cannot determine board type!\n", 265 dev->minor, PC263_DRIVER_NAME); 266 return -EINVAL; 267 break; 268 } 269 270/* 271 * Initialize dev->board_name. 272 */ 273 dev->board_name = thisboard->name; 274 275 /* Enable device and reserve I/O spaces. */ 276#ifdef CONFIG_COMEDI_PCI 277 if (pci_dev) { 278 ret = comedi_pci_enable(pci_dev, PC263_DRIVER_NAME); 279 if (ret < 0) { 280 printk(KERN_ERR 281 "comedi%d: error! cannot enable PCI device and request regions!\n", 282 dev->minor); 283 return ret; 284 } 285 iobase = pci_resource_start(pci_dev, 2); 286 } else 287#endif 288 { 289 ret = pc263_request_region(dev->minor, iobase, PC263_IO_SIZE); 290 if (ret < 0) { 291 return ret; 292 } 293 } 294 dev->iobase = iobase; 295 296/* 297 * Allocate the subdevice structures. alloc_subdevice() is a 298 * convenient macro defined in comedidev.h. 299 */ 300 ret = alloc_subdevices(dev, 1); 301 if (ret < 0) { 302 printk(KERN_ERR "comedi%d: error! out of memory!\n", 303 dev->minor); 304 return ret; 305 } 306 307 s = dev->subdevices + 0; 308 /* digital i/o subdevice */ 309 s->type = COMEDI_SUBD_DIO; 310 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_RT; 311 s->n_chan = 16; 312 s->maxdata = 1; 313 s->range_table = &range_digital; 314 s->insn_bits = pc263_dio_insn_bits; 315 s->insn_config = pc263_dio_insn_config; 316 /* all outputs */ 317 s->io_bits = 0xffff; 318 /* read initial relay state */ 319 s->state = inb(dev->iobase); 320 s->state = s->state | (inb(dev->iobase) << 8); 321 322 printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name); 323 if (thisboard->bustype == isa_bustype) { 324 printk("(base %#lx) ", iobase); 325 } else { 326#ifdef CONFIG_COMEDI_PCI 327 printk("(pci %s) ", pci_name(pci_dev)); 328#endif 329 } 330 331 printk("attached\n"); 332 333 return 1; 334} 335 336/* 337 * _detach is called to deconfigure a device. It should deallocate 338 * resources. 339 * This function is also called when _attach() fails, so it should be 340 * careful not to release resources that were not necessarily 341 * allocated by _attach(). dev->private and dev->subdevices are 342 * deallocated automatically by the core. 343 */ 344static int pc263_detach(struct comedi_device *dev) 345{ 346 printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor, 347 PC263_DRIVER_NAME); 348 349#ifdef CONFIG_COMEDI_PCI 350 if (devpriv) 351#endif 352 { 353#ifdef CONFIG_COMEDI_PCI 354 if (devpriv->pci_dev) { 355 if (dev->iobase) { 356 comedi_pci_disable(devpriv->pci_dev); 357 } 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 } 367 if (dev->board_name) { 368 printk(KERN_INFO "comedi%d: %s removed\n", 369 dev->minor, dev->board_name); 370 } 371 return 0; 372} 373 374/* 375 * This function checks and requests an I/O region, reporting an error 376 * if there is a conflict. 377 */ 378static int pc263_request_region(unsigned minor, unsigned long from, 379 unsigned long extent) 380{ 381 if (!from || !request_region(from, extent, PC263_DRIVER_NAME)) { 382 printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n", 383 minor, from, extent); 384 return -EIO; 385 } 386 return 0; 387} 388 389/* DIO devices are slightly special. Although it is possible to 390 * implement the insn_read/insn_write interface, it is much more 391 * useful to applications if you implement the insn_bits interface. 392 * This allows packed reading/writing of the DIO channels. The 393 * comedi core can convert between insn_bits and insn_read/write */ 394static int pc263_dio_insn_bits(struct comedi_device *dev, 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, struct comedi_subdevice *s, 420 struct comedi_insn *insn, unsigned int *data) 421{ 422 if (insn->n != 1) 423 return -EINVAL; 424 return 1; 425} 426 427/* 428 * A convenient macro that defines init_module() and cleanup_module(), 429 * as necessary. 430 */ 431#ifdef CONFIG_COMEDI_PCI 432COMEDI_PCI_INITCLEANUP(driver_amplc_pc263, pc263_pci_table); 433#else 434COMEDI_INITCLEANUP(driver_amplc_pc263); 435#endif 436