amplc_pc263.c revision 0a85b6f0ab0d2edb0d41b32697111ce0e4f43496
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	{
105	PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI263,
106		    PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {
107	0}
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 */
116#define thisboard ((const struct pc263_board *)dev->board_ptr)
117
118/* this structure is for data unique to this hardware driver.  If
119   several hardware drivers keep similar information in this structure,
120   feel free to suggest moving the variable to the struct comedi_device struct.  */
121#ifdef CONFIG_COMEDI_PCI
122struct pc263_private {
123	/* PCI device. */
124	struct pci_dev *pci_dev;
125};
126
127#define devpriv ((struct pc263_private *)dev->private)
128#endif /* CONFIG_COMEDI_PCI */
129
130/*
131 * The struct comedi_driver structure tells the Comedi core module
132 * which functions to call to configure/deconfigure (attach/detach)
133 * the board, and also about the kernel module that contains
134 * the device code.
135 */
136static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it);
137static int pc263_detach(struct comedi_device *dev);
138static struct comedi_driver driver_amplc_pc263 = {
139	.driver_name = PC263_DRIVER_NAME,
140	.module = THIS_MODULE,
141	.attach = pc263_attach,
142	.detach = pc263_detach,
143	.board_name = &pc263_boards[0].name,
144	.offset = sizeof(struct pc263_board),
145	.num_names = ARRAY_SIZE(pc263_boards),
146};
147
148static int pc263_request_region(unsigned minor, unsigned long from,
149				unsigned long extent);
150static int pc263_dio_insn_bits(struct comedi_device *dev,
151			       struct comedi_subdevice *s,
152			       struct comedi_insn *insn, unsigned int *data);
153static int pc263_dio_insn_config(struct comedi_device *dev,
154				 struct comedi_subdevice *s,
155				 struct comedi_insn *insn, unsigned int *data);
156
157/*
158 * This function looks for a PCI device matching the requested board name,
159 * bus and slot.
160 */
161#ifdef CONFIG_COMEDI_PCI
162static int
163pc263_find_pci(struct comedi_device *dev, int bus, int slot,
164	       struct pci_dev **pci_dev_p)
165{
166	struct pci_dev *pci_dev = NULL;
167
168	*pci_dev_p = NULL;
169
170	/* Look for matching PCI device. */
171	for (pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON, PCI_ANY_ID, NULL);
172	     pci_dev != NULL;
173	     pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
174				      PCI_ANY_ID, pci_dev)) {
175		/* If bus/slot specified, check them. */
176		if (bus || slot) {
177			if (bus != pci_dev->bus->number
178			    || slot != PCI_SLOT(pci_dev->devfn))
179				continue;
180		}
181		if (thisboard->model == anypci_model) {
182			/* Match any supported model. */
183			int i;
184
185			for (i = 0; i < ARRAY_SIZE(pc263_boards); i++) {
186				if (pc263_boards[i].bustype != pci_bustype)
187					continue;
188				if (pci_dev->device == pc263_boards[i].devid) {
189					/* Change board_ptr to matched board. */
190					dev->board_ptr = &pc263_boards[i];
191					break;
192				}
193			}
194			if (i == ARRAY_SIZE(pc263_boards))
195				continue;
196		} else {
197			/* Match specific model name. */
198			if (pci_dev->device != thisboard->devid)
199				continue;
200		}
201
202		/* Found a match. */
203		*pci_dev_p = pci_dev;
204		return 0;
205	}
206	/* No match found. */
207	if (bus || slot) {
208		printk(KERN_ERR
209		       "comedi%d: error! no %s found at pci %02x:%02x!\n",
210		       dev->minor, thisboard->name, bus, slot);
211	} else {
212		printk(KERN_ERR "comedi%d: error! no %s found!\n",
213		       dev->minor, thisboard->name);
214	}
215	return -EIO;
216}
217#endif
218
219/*
220 * Attach is called by the Comedi core to configure the driver
221 * for a particular board.  If you specified a board_name array
222 * in the driver structure, dev->board_ptr contains that
223 * address.
224 */
225static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it)
226{
227	struct comedi_subdevice *s;
228	unsigned long iobase = 0;
229#ifdef CONFIG_COMEDI_PCI
230	struct pci_dev *pci_dev = NULL;
231	int bus = 0, slot = 0;
232#endif
233	int ret;
234
235	printk(KERN_DEBUG "comedi%d: %s: attach\n", dev->minor,
236	       PC263_DRIVER_NAME);
237/*
238 * Allocate the private structure area.  alloc_private() is a
239 * convenient macro defined in comedidev.h.
240 */
241#ifdef CONFIG_COMEDI_PCI
242	ret = alloc_private(dev, sizeof(struct pc263_private));
243	if (ret < 0) {
244		printk(KERN_ERR "comedi%d: error! out of memory!\n",
245		       dev->minor);
246		return ret;
247	}
248#endif
249	/* Process options. */
250	switch (thisboard->bustype) {
251	case isa_bustype:
252		iobase = it->options[0];
253		break;
254#ifdef CONFIG_COMEDI_PCI
255	case pci_bustype:
256		bus = it->options[0];
257		slot = it->options[1];
258
259		ret = pc263_find_pci(dev, bus, slot, &pci_dev);
260		if (ret < 0)
261			return ret;
262		devpriv->pci_dev = pci_dev;
263		break;
264#endif /* CONFIG_COMEDI_PCI */
265	default:
266		printk(KERN_ERR
267		       "comedi%d: %s: BUG! cannot determine board type!\n",
268		       dev->minor, PC263_DRIVER_NAME);
269		return -EINVAL;
270		break;
271	}
272
273/*
274 * Initialize dev->board_name.
275 */
276	dev->board_name = thisboard->name;
277
278	/* Enable device and reserve I/O spaces. */
279#ifdef CONFIG_COMEDI_PCI
280	if (pci_dev) {
281		ret = comedi_pci_enable(pci_dev, PC263_DRIVER_NAME);
282		if (ret < 0) {
283			printk(KERN_ERR
284			       "comedi%d: error! cannot enable PCI device and 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	}
297	dev->iobase = iobase;
298
299/*
300 * Allocate the subdevice structures.  alloc_subdevice() is a
301 * convenient macro defined in comedidev.h.
302 */
303	ret = alloc_subdevices(dev, 1);
304	if (ret < 0) {
305		printk(KERN_ERR "comedi%d: error! out of memory!\n",
306		       dev->minor);
307		return ret;
308	}
309
310	s = dev->subdevices + 0;
311	/* digital i/o subdevice */
312	s->type = COMEDI_SUBD_DIO;
313	s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
314	s->n_chan = 16;
315	s->maxdata = 1;
316	s->range_table = &range_digital;
317	s->insn_bits = pc263_dio_insn_bits;
318	s->insn_config = pc263_dio_insn_config;
319	/* all outputs */
320	s->io_bits = 0xffff;
321	/* read initial relay state */
322	s->state = inb(dev->iobase);
323	s->state = s->state | (inb(dev->iobase) << 8);
324
325	printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
326	if (thisboard->bustype == isa_bustype) {
327		printk("(base %#lx) ", iobase);
328	} else {
329#ifdef CONFIG_COMEDI_PCI
330		printk("(pci %s) ", pci_name(pci_dev));
331#endif
332	}
333
334	printk("attached\n");
335
336	return 1;
337}
338
339/*
340 * _detach is called to deconfigure a device.  It should deallocate
341 * resources.
342 * This function is also called when _attach() fails, so it should be
343 * careful not to release resources that were not necessarily
344 * allocated by _attach().  dev->private and dev->subdevices are
345 * deallocated automatically by the core.
346 */
347static int pc263_detach(struct comedi_device *dev)
348{
349	printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor,
350	       PC263_DRIVER_NAME);
351
352#ifdef CONFIG_COMEDI_PCI
353	if (devpriv)
354#endif
355	{
356#ifdef CONFIG_COMEDI_PCI
357		if (devpriv->pci_dev) {
358			if (dev->iobase) {
359				comedi_pci_disable(devpriv->pci_dev);
360			}
361			pci_dev_put(devpriv->pci_dev);
362		} else
363#endif
364		{
365			if (dev->iobase) {
366				release_region(dev->iobase, PC263_IO_SIZE);
367			}
368		}
369	}
370	if (dev->board_name) {
371		printk(KERN_INFO "comedi%d: %s removed\n",
372		       dev->minor, dev->board_name);
373	}
374	return 0;
375}
376
377/*
378 * This function checks and requests an I/O region, reporting an error
379 * if there is a conflict.
380 */
381static int pc263_request_region(unsigned minor, unsigned long from,
382				unsigned long extent)
383{
384	if (!from || !request_region(from, extent, PC263_DRIVER_NAME)) {
385		printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n",
386		       minor, from, extent);
387		return -EIO;
388	}
389	return 0;
390}
391
392/* DIO devices are slightly special.  Although it is possible to
393 * implement the insn_read/insn_write interface, it is much more
394 * useful to applications if you implement the insn_bits interface.
395 * This allows packed reading/writing of the DIO channels.  The
396 * comedi core can convert between insn_bits and insn_read/write */
397static int pc263_dio_insn_bits(struct comedi_device *dev,
398			       struct comedi_subdevice *s,
399			       struct comedi_insn *insn, unsigned int *data)
400{
401	if (insn->n != 2)
402		return -EINVAL;
403
404	/* The insn data is a mask in data[0] and the new data
405	 * in data[1], each channel cooresponding to a bit. */
406	if (data[0]) {
407		s->state &= ~data[0];
408		s->state |= data[0] & data[1];
409		/* Write out the new digital output lines */
410		outb(s->state & 0xFF, dev->iobase);
411		outb(s->state >> 8, dev->iobase + 1);
412	}
413
414	/* on return, data[1] contains the value of the digital
415	 * input and output lines. */
416	/* or we could just return the software copy of the output values if
417	 * it was a purely digital output subdevice */
418	data[1] = s->state;
419
420	return 2;
421}
422
423static int pc263_dio_insn_config(struct comedi_device *dev,
424				 struct comedi_subdevice *s,
425				 struct comedi_insn *insn, unsigned int *data)
426{
427	if (insn->n != 1)
428		return -EINVAL;
429	return 1;
430}
431
432/*
433 * A convenient macro that defines init_module() and cleanup_module(),
434 * as necessary.
435 */
436#ifdef CONFIG_COMEDI_PCI
437COMEDI_PCI_INITCLEANUP(driver_amplc_pc263, pc263_pci_table);
438#else
439COMEDI_INITCLEANUP(driver_amplc_pc263);
440#endif
441