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