2 comedi/drivers/amplc_pc263.c
3 Driver for Amplicon PC263 and PCI263 relay boards.
5 Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>
7 COMEDI - Linux Control and Measurement Device Interface
8 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
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.
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.
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.
27 Description: Amplicon PC263, PCI263
28 Author: Ian Abbott <abbotti@mev.co.uk>
29 Devices: [Amplicon] PC263 (pc263), PCI263 (pci263 or amplc_pc263)
30 Updated: Wed, 22 Oct 2008 14:10:53 +0100
33 Configuration options - PC263:
34 [0] - I/O port base address
36 Configuration 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
42 Each board appears as one subdevice, with 16 digital outputs, each
43 connected to a reed-relay. Relay contacts are closed when output is 1.
44 The state of the outputs can be read.
47 #include "../comedidev.h"
49 #include "comedi_pci.h"
51 #define PC263_DRIVER_NAME "amplc_pc263"
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
58 /* PC263 / PCI263 registers */
59 #define PC263_IO_SIZE 2
62 * Board descriptions for Amplicon PC263 / PCI263.
65 enum pc263_bustype { isa_bustype, pci_bustype };
66 enum pc263_model { pc263_model, pci263_model, anypci_model };
68 typedef struct pc263_board_struct {
70 const char *fancy_name;
72 enum pc263_bustype bustype;
73 enum pc263_model model;
75 static const pc263_board pc263_boards[] = {
82 #ifdef CONFIG_COMEDI_PCI
86 devid: PCI_DEVICE_ID_AMPLICON_PCI263,
91 #ifdef CONFIG_COMEDI_PCI
93 name: PC263_DRIVER_NAME,
94 fancy_name:PC263_DRIVER_NAME,
95 devid: PCI_DEVICE_ID_INVALID,
97 model: anypci_model, /* wildcard */
102 #ifdef CONFIG_COMEDI_PCI
103 static 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},
109 MODULE_DEVICE_TABLE(pci, pc263_pci_table);
110 #endif /* CONFIG_COMEDI_PCI */
113 * Useful for shorthand access to the particular board structure
115 #define thisboard ((const pc263_board *)dev->board_ptr)
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 comedi_device struct. */
120 #ifdef CONFIG_COMEDI_PCI
123 struct pci_dev *pci_dev;
126 #define devpriv ((pc263_private *)dev->private)
127 #endif /* CONFIG_COMEDI_PCI */
130 * The 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
135 static int pc263_attach(comedi_device * dev, comedi_devconfig * it);
136 static int pc263_detach(comedi_device * dev);
137 static comedi_driver driver_amplc_pc263 = {
138 driver_name:PC263_DRIVER_NAME,
142 board_name:&pc263_boards[0].name,
143 offset:sizeof(pc263_board),
144 num_names:sizeof(pc263_boards) / sizeof(pc263_board),
147 static int pc263_request_region(unsigned minor, unsigned long from,
148 unsigned long extent);
149 static int pc263_dio_insn_bits(comedi_device * dev, comedi_subdevice * s,
150 comedi_insn * insn, unsigned int * data);
151 static int pc263_dio_insn_config(comedi_device * dev, comedi_subdevice * s,
152 comedi_insn * insn, unsigned int * data);
155 * This function looks for a PCI device matching the requested board name,
158 #ifdef CONFIG_COMEDI_PCI
160 pc263_find_pci(comedi_device * dev, int bus, int slot,
161 struct pci_dev **pci_dev_p)
163 struct pci_dev *pci_dev = NULL;
167 /* Look for matching PCI device. */
168 for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
170 pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
171 PCI_ANY_ID, pci_dev)) {
172 /* If bus/slot specified, check them. */
174 if (bus != pci_dev->bus->number
175 || slot != PCI_SLOT(pci_dev->devfn))
178 if (thisboard->model == anypci_model) {
179 /* Match any supported model. */
182 for (i = 0; i < ARRAY_SIZE(pc263_boards); i++) {
183 if (pc263_boards[i].bustype != pci_bustype)
185 if (pci_dev->device == pc263_boards[i].devid) {
186 /* Change board_ptr to matched board. */
187 dev->board_ptr = &pc263_boards[i];
191 if (i == ARRAY_SIZE(pc263_boards))
194 /* Match specific model name. */
195 if (pci_dev->device != thisboard->devid)
200 *pci_dev_p = pci_dev;
203 /* No match found. */
206 "comedi%d: error! no %s found at pci %02x:%02x!\n",
207 dev->minor, thisboard->name, bus, slot);
209 printk(KERN_ERR "comedi%d: error! no %s found!\n",
210 dev->minor, thisboard->name);
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
222 static int pc263_attach(comedi_device * dev, comedi_devconfig * it)
225 unsigned long iobase = 0;
226 #ifdef CONFIG_COMEDI_PCI
227 struct pci_dev *pci_dev = NULL;
228 int bus = 0, slot = 0;
232 printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor,
235 * Allocate the private structure area. alloc_private() is a
236 * convenient macro defined in comedidev.h.
238 #ifdef CONFIG_COMEDI_PCI
239 if ((ret = alloc_private(dev, sizeof(pc263_private))) < 0) {
240 printk(KERN_ERR "comedi%d: error! out of memory!\n",
245 /* Process options. */
246 switch (thisboard->bustype) {
248 iobase = it->options[0];
250 #ifdef CONFIG_COMEDI_PCI
252 bus = it->options[0];
253 slot = it->options[1];
255 if ((ret = pc263_find_pci(dev, bus, slot, &pci_dev)) < 0)
257 devpriv->pci_dev = pci_dev;
259 #endif /* CONFIG_COMEDI_PCI */
262 "comedi%d: %s: BUG! cannot determine board type!\n",
263 dev->minor, PC263_DRIVER_NAME);
269 * Initialize dev->board_name.
271 dev->board_name = thisboard->name;
273 /* Enable device and reserve I/O spaces. */
274 #ifdef CONFIG_COMEDI_PCI
276 if ((ret = comedi_pci_enable(pci_dev, PC263_DRIVER_NAME)) < 0) {
278 "comedi%d: error! cannot enable PCI device and request regions!\n",
282 iobase = pci_resource_start(pci_dev, 2);
286 ret = pc263_request_region(dev->minor, iobase, PC263_IO_SIZE);
291 dev->iobase = iobase;
294 * Allocate the subdevice structures. alloc_subdevice() is a
295 * convenient macro defined in comedidev.h.
297 if ((ret = alloc_subdevices(dev, 1)) < 0) {
298 printk(KERN_ERR "comedi%d: error! out of memory!\n",
303 s = dev->subdevices + 0;
304 /* digital i/o subdevice */
305 s->type = COMEDI_SUBD_DIO;
306 s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_RT;
309 s->range_table = &range_digital;
310 s->insn_bits = pc263_dio_insn_bits;
311 s->insn_config = pc263_dio_insn_config;
314 /* read initial relay state */
315 s->state = inb(dev->iobase);
316 s->state = s->state | (inb(dev->iobase) << 8);
318 printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
319 if (thisboard->bustype == isa_bustype) {
320 printk("(base %#lx) ", iobase);
322 #ifdef CONFIG_COMEDI_PCI
323 printk("(pci %s) ", pci_name(pci_dev));
327 printk("attached\n");
333 * _detach is called to deconfigure a device. It should deallocate
335 * This function is also called when _attach() fails, so it should be
336 * careful not to release resources that were not necessarily
337 * allocated by _attach(). dev->private and dev->subdevices are
338 * deallocated automatically by the core.
340 static int pc263_detach(comedi_device * dev)
342 printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor,
345 #ifdef CONFIG_COMEDI_PCI
349 #ifdef CONFIG_COMEDI_PCI
350 if (devpriv->pci_dev) {
352 comedi_pci_disable(devpriv->pci_dev);
354 pci_dev_put(devpriv->pci_dev);
359 release_region(dev->iobase, PC263_IO_SIZE);
363 if (dev->board_name) {
364 printk(KERN_INFO "comedi%d: %s removed\n",
365 dev->minor, dev->board_name);
371 * This function checks and requests an I/O region, reporting an error
372 * if there is a conflict.
374 static int pc263_request_region(unsigned minor, unsigned long from,
375 unsigned long extent)
377 if (!from || !request_region(from, extent, PC263_DRIVER_NAME)) {
378 printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n",
379 minor, from, extent);
385 /* DIO devices are slightly special. Although it is possible to
386 * implement the insn_read/insn_write interface, it is much more
387 * useful to applications if you implement the insn_bits interface.
388 * This allows packed reading/writing of the DIO channels. The
389 * comedi core can convert between insn_bits and insn_read/write */
390 static int pc263_dio_insn_bits(comedi_device * dev, comedi_subdevice * s,
391 comedi_insn * insn, unsigned int * data)
396 /* The insn data is a mask in data[0] and the new data
397 * in data[1], each channel cooresponding to a bit. */
399 s->state &= ~data[0];
400 s->state |= data[0] & data[1];
401 /* Write out the new digital output lines */
402 outb(s->state & 0xFF, dev->iobase);
403 outb(s->state >> 8, dev->iobase + 1);
406 /* on return, data[1] contains the value of the digital
407 * input and output lines. */
408 /* or we could just return the software copy of the output values if
409 * it was a purely digital output subdevice */
415 static int pc263_dio_insn_config(comedi_device * dev, comedi_subdevice * s,
416 comedi_insn * insn, unsigned int * data)
424 * A convenient macro that defines init_module() and cleanup_module(),
427 #ifdef CONFIG_COMEDI_PCI
428 COMEDI_PCI_INITCLEANUP(driver_amplc_pc263, pc263_pci_table);
430 COMEDI_INITCLEANUP(driver_amplc_pc263);