amplc_pc263.c revision 68c3dbff9fc9f25872408d0e95980d41733d48d0
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 = sizeof(pc263_boards) / sizeof(struct pc263_board),
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