Staging: comedi: Remove COMEDI_PCI_INITCLEANUP macro
[deliverable/linux.git] / drivers / staging / comedi / drivers / amplc_pc263.c
CommitLineData
cfd02b71
IA
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
4beb86c2 68struct pc263_board {
cfd02b71
IA
69 const char *name;
70 const char *fancy_name;
71 unsigned short devid;
72 enum pc263_bustype bustype;
73 enum pc263_model model;
4beb86c2
BP
74};
75static const struct pc263_board pc263_boards[] = {
cfd02b71 76 {
0a85b6f0
MT
77 .name = "pc263",
78 .fancy_name = "PC263",
79 .bustype = isa_bustype,
80 .model = pc263_model,
81 },
cfd02b71
IA
82#ifdef CONFIG_COMEDI_PCI
83 {
0a85b6f0
MT
84 .name = "pci263",
85 .fancy_name = "PCI263",
86 .devid = PCI_DEVICE_ID_AMPLICON_PCI263,
87 .bustype = pci_bustype,
88 .model = pci263_model,
89 },
cfd02b71
IA
90#endif
91#ifdef CONFIG_COMEDI_PCI
92 {
0a85b6f0
MT
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 },
cfd02b71
IA
99#endif
100};
101
102#ifdef CONFIG_COMEDI_PCI
103static DEFINE_PCI_DEVICE_TABLE(pc263_pci_table) = {
0a85b6f0
MT
104 {
105 PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI263,
106 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
107 0}
cfd02b71
IA
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 */
4beb86c2 116#define thisboard ((const struct pc263_board *)dev->board_ptr)
cfd02b71
IA
117
118/* this structure is for data unique to this hardware driver. If
119 several hardware drivers keep similar information in this structure,
abdedefe
KAP
120 feel free to suggest moving the variable to the struct comedi_device struct.
121*/
cfd02b71 122#ifdef CONFIG_COMEDI_PCI
cd60d1ec 123struct pc263_private {
cfd02b71
IA
124 /* PCI device. */
125 struct pci_dev *pci_dev;
cd60d1ec 126};
cfd02b71 127
cd60d1ec 128#define devpriv ((struct pc263_private *)dev->private)
cfd02b71
IA
129#endif /* CONFIG_COMEDI_PCI */
130
131/*
139dfbdf 132 * The struct comedi_driver structure tells the Comedi core module
cfd02b71
IA
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 */
da91b269
BP
137static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it);
138static int pc263_detach(struct comedi_device *dev);
139dfbdf 139static struct comedi_driver driver_amplc_pc263 = {
68c3dbff
BP
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),
8629efa4 146 .num_names = ARRAY_SIZE(pc263_boards),
cfd02b71
IA
147};
148
149static int pc263_request_region(unsigned minor, unsigned long from,
0a85b6f0
MT
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);
cfd02b71
IA
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
da91b269 164pc263_find_pci(struct comedi_device *dev, int bus, int slot,
0a85b6f0 165 struct pci_dev **pci_dev_p)
cfd02b71
IA
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);
0a85b6f0
MT
173 pci_dev != NULL;
174 pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
175 PCI_ANY_ID, pci_dev)) {
cfd02b71
IA
176 /* If bus/slot specified, check them. */
177 if (bus || slot) {
178 if (bus != pci_dev->bus->number
0a85b6f0 179 || slot != PCI_SLOT(pci_dev->devfn))
cfd02b71
IA
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
0a85b6f0
MT
210 "comedi%d: error! no %s found at pci %02x:%02x!\n",
211 dev->minor, thisboard->name, bus, slot);
cfd02b71
IA
212 } else {
213 printk(KERN_ERR "comedi%d: error! no %s found!\n",
0a85b6f0 214 dev->minor, thisboard->name);
cfd02b71
IA
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 */
da91b269 226static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it)
cfd02b71 227{
34c43922 228 struct comedi_subdevice *s;
cfd02b71
IA
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,
0a85b6f0 237 PC263_DRIVER_NAME);
cfd02b71
IA
238/*
239 * Allocate the private structure area. alloc_private() is a
240 * convenient macro defined in comedidev.h.
241 */
242#ifdef CONFIG_COMEDI_PCI
c3744138
BP
243 ret = alloc_private(dev, sizeof(struct pc263_private));
244 if (ret < 0) {
cfd02b71 245 printk(KERN_ERR "comedi%d: error! out of memory!\n",
0a85b6f0 246 dev->minor);
cfd02b71
IA
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
c3744138
BP
260 ret = pc263_find_pci(dev, bus, slot, &pci_dev);
261 if (ret < 0)
cfd02b71
IA
262 return ret;
263 devpriv->pci_dev = pci_dev;
264 break;
265#endif /* CONFIG_COMEDI_PCI */
266 default:
267 printk(KERN_ERR
0a85b6f0
MT
268 "comedi%d: %s: BUG! cannot determine board type!\n",
269 dev->minor, PC263_DRIVER_NAME);
cfd02b71
IA
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) {
c3744138
BP
282 ret = comedi_pci_enable(pci_dev, PC263_DRIVER_NAME);
283 if (ret < 0) {
cfd02b71 284 printk(KERN_ERR
abdedefe
KAP
285 "comedi%d: error! cannot enable PCI device and "
286 "request regions!\n",
0a85b6f0 287 dev->minor);
cfd02b71
IA
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);
56f5f24d 295 if (ret < 0)
cfd02b71 296 return ret;
cfd02b71
IA
297 }
298 dev->iobase = iobase;
299
300/*
301 * Allocate the subdevice structures. alloc_subdevice() is a
302 * convenient macro defined in comedidev.h.
303 */
c3744138
BP
304 ret = alloc_subdevices(dev, 1);
305 if (ret < 0) {
cfd02b71 306 printk(KERN_ERR "comedi%d: error! out of memory!\n",
0a85b6f0 307 dev->minor);
cfd02b71
IA
308 return ret;
309 }
310
311 s = dev->subdevices + 0;
312 /* digital i/o subdevice */
313 s->type = COMEDI_SUBD_DIO;
fcea1154 314 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
cfd02b71
IA
315 s->n_chan = 16;
316 s->maxdata = 1;
317 s->range_table = &range_digital;
318 s->insn_bits = pc263_dio_insn_bits;
319 s->insn_config = pc263_dio_insn_config;
320 /* all outputs */
321 s->io_bits = 0xffff;
322 /* read initial relay state */
323 s->state = inb(dev->iobase);
324 s->state = s->state | (inb(dev->iobase) << 8);
325
326 printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
327 if (thisboard->bustype == isa_bustype) {
328 printk("(base %#lx) ", iobase);
329 } else {
330#ifdef CONFIG_COMEDI_PCI
331 printk("(pci %s) ", pci_name(pci_dev));
332#endif
333 }
334
335 printk("attached\n");
336
337 return 1;
338}
339
340/*
341 * _detach is called to deconfigure a device. It should deallocate
342 * resources.
343 * This function is also called when _attach() fails, so it should be
344 * careful not to release resources that were not necessarily
345 * allocated by _attach(). dev->private and dev->subdevices are
346 * deallocated automatically by the core.
347 */
da91b269 348static int pc263_detach(struct comedi_device *dev)
cfd02b71
IA
349{
350 printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor,
0a85b6f0 351 PC263_DRIVER_NAME);
cfd02b71
IA
352
353#ifdef CONFIG_COMEDI_PCI
56f5f24d 354 if (devpriv) {
cfd02b71 355#endif
cfd02b71
IA
356#ifdef CONFIG_COMEDI_PCI
357 if (devpriv->pci_dev) {
56f5f24d 358 if (dev->iobase)
cfd02b71 359 comedi_pci_disable(devpriv->pci_dev);
cfd02b71
IA
360 pci_dev_put(devpriv->pci_dev);
361 } else
362#endif
363 {
56f5f24d 364 if (dev->iobase)
cfd02b71 365 release_region(dev->iobase, PC263_IO_SIZE);
cfd02b71
IA
366 }
367 }
368 if (dev->board_name) {
369 printk(KERN_INFO "comedi%d: %s removed\n",
0a85b6f0 370 dev->minor, dev->board_name);
cfd02b71
IA
371 }
372 return 0;
373}
374
375/*
376 * This function checks and requests an I/O region, reporting an error
377 * if there is a conflict.
378 */
379static int pc263_request_region(unsigned minor, unsigned long from,
0a85b6f0 380 unsigned long extent)
cfd02b71
IA
381{
382 if (!from || !request_region(from, extent, PC263_DRIVER_NAME)) {
383 printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n",
0a85b6f0 384 minor, from, extent);
cfd02b71
IA
385 return -EIO;
386 }
387 return 0;
388}
389
390/* DIO devices are slightly special. Although it is possible to
391 * implement the insn_read/insn_write interface, it is much more
392 * useful to applications if you implement the insn_bits interface.
393 * This allows packed reading/writing of the DIO channels. The
394 * comedi core can convert between insn_bits and insn_read/write */
0a85b6f0
MT
395static int pc263_dio_insn_bits(struct comedi_device *dev,
396 struct comedi_subdevice *s,
397 struct comedi_insn *insn, unsigned int *data)
cfd02b71
IA
398{
399 if (insn->n != 2)
400 return -EINVAL;
401
402 /* The insn data is a mask in data[0] and the new data
403 * in data[1], each channel cooresponding to a bit. */
404 if (data[0]) {
405 s->state &= ~data[0];
406 s->state |= data[0] & data[1];
407 /* Write out the new digital output lines */
408 outb(s->state & 0xFF, dev->iobase);
409 outb(s->state >> 8, dev->iobase + 1);
410 }
411
412 /* on return, data[1] contains the value of the digital
413 * input and output lines. */
414 /* or we could just return the software copy of the output values if
415 * it was a purely digital output subdevice */
416 data[1] = s->state;
417
418 return 2;
419}
420
0a85b6f0
MT
421static int pc263_dio_insn_config(struct comedi_device *dev,
422 struct comedi_subdevice *s,
423 struct comedi_insn *insn, unsigned int *data)
cfd02b71
IA
424{
425 if (insn->n != 1)
426 return -EINVAL;
427 return 1;
428}
429
430/*
431 * A convenient macro that defines init_module() and cleanup_module(),
432 * as necessary.
433 */
434#ifdef CONFIG_COMEDI_PCI
727b286b
AT
435static int __devinit driver_amplc_pc263_pci_probe(struct pci_dev *dev,
436 const struct pci_device_id
437 *ent)
438{
439 return comedi_pci_auto_config(dev, driver_amplc_pc263.driver_name);
440}
441
442static void __devexit driver_amplc_pc263_pci_remove(struct pci_dev *dev)
443{
444 comedi_pci_auto_unconfig(dev);
445}
446
447static struct pci_driver driver_amplc_pc263_pci_driver = {
448 .id_table = pc263_pci_table,
449 .probe = &driver_amplc_pc263_pci_probe,
450 .remove = __devexit_p(&driver_amplc_pc263_pci_remove)
451};
452
453static int __init driver_amplc_pc263_init_module(void)
454{
455 int retval;
456
457 retval = comedi_driver_register(&driver_amplc_pc263);
458 if (retval < 0)
459 return retval;
460
461 driver_amplc_pc263_pci_driver.name =
462 (char *)driver_amplc_pc263.driver_name;
463 return pci_register_driver(&driver_amplc_pc263_pci_driver);
464}
465
466static void __exit driver_amplc_pc263_cleanup_module(void)
467{
468 pci_unregister_driver(&driver_amplc_pc263_pci_driver);
469 comedi_driver_unregister(&driver_amplc_pc263);
470}
471
472module_init(driver_amplc_pc263_init_module);
473module_exit(driver_amplc_pc263_cleanup_module);
cfd02b71 474#else
7114a280
AT
475static int __init driver_amplc_pc263_init_module(void)
476{
477 return comedi_driver_register(&driver_amplc_pc263);
478}
479
480static void __exit driver_amplc_pc263_cleanup_module(void)
481{
482 comedi_driver_unregister(&driver_amplc_pc263);
483}
484
485module_init(driver_amplc_pc263_init_module);
486module_exit(driver_amplc_pc263_cleanup_module);
cfd02b71 487#endif
90f703d3
AT
488
489MODULE_AUTHOR("Comedi http://www.comedi.org");
490MODULE_DESCRIPTION("Comedi low-level driver");
491MODULE_LICENSE("GPL");
This page took 0.2549 seconds and 5 git commands to generate.