amplc_pc263.c revision 139dfbdfacb02e3ef3df936d2fabd1ad5f14ea88
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
68typedef struct pc263_board_struct {
69	const char *name;
70	const char *fancy_name;
71	unsigned short devid;
72	enum pc263_bustype bustype;
73	enum pc263_model model;
74} pc263_board;
75static const 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 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
121typedef struct {
122	/* PCI device. */
123	struct pci_dev *pci_dev;
124} pc263_private;
125
126#define devpriv ((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, 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(pc263_board),
144      num_names:sizeof(pc263_boards) / sizeof(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	comedi_insn * insn, unsigned int * data);
151static int pc263_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
152	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, 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	if ((ret = alloc_private(dev, sizeof(pc263_private))) < 0) {
240		printk(KERN_ERR "comedi%d: error! out of memory!\n",
241			dev->minor);
242		return ret;
243	}
244#endif
245	/* Process options. */
246	switch (thisboard->bustype) {
247	case isa_bustype:
248		iobase = it->options[0];
249		break;
250#ifdef CONFIG_COMEDI_PCI
251	case pci_bustype:
252		bus = it->options[0];
253		slot = it->options[1];
254
255		if ((ret = pc263_find_pci(dev, bus, slot, &pci_dev)) < 0)
256			return ret;
257		devpriv->pci_dev = pci_dev;
258		break;
259#endif /* CONFIG_COMEDI_PCI */
260	default:
261		printk(KERN_ERR
262			"comedi%d: %s: BUG! cannot determine board type!\n",
263			dev->minor, PC263_DRIVER_NAME);
264		return -EINVAL;
265		break;
266	}
267
268/*
269 * Initialize dev->board_name.
270 */
271	dev->board_name = thisboard->name;
272
273	/* Enable device and reserve I/O spaces. */
274#ifdef CONFIG_COMEDI_PCI
275	if (pci_dev) {
276		if ((ret = comedi_pci_enable(pci_dev, PC263_DRIVER_NAME)) < 0) {
277			printk(KERN_ERR
278				"comedi%d: error! cannot enable PCI device and request regions!\n",
279				dev->minor);
280			return ret;
281		}
282		iobase = pci_resource_start(pci_dev, 2);
283	} else
284#endif
285	{
286		ret = pc263_request_region(dev->minor, iobase, PC263_IO_SIZE);
287		if (ret < 0) {
288			return ret;
289		}
290	}
291	dev->iobase = iobase;
292
293/*
294 * Allocate the subdevice structures.  alloc_subdevice() is a
295 * convenient macro defined in comedidev.h.
296 */
297	if ((ret = alloc_subdevices(dev, 1)) < 0) {
298		printk(KERN_ERR "comedi%d: error! out of memory!\n",
299			dev->minor);
300		return ret;
301	}
302
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;
307	s->n_chan = 16;
308	s->maxdata = 1;
309	s->range_table = &range_digital;
310	s->insn_bits = pc263_dio_insn_bits;
311	s->insn_config = pc263_dio_insn_config;
312	/* all outputs */
313	s->io_bits = 0xffff;
314	/* read initial relay state */
315	s->state = inb(dev->iobase);
316	s->state = s->state | (inb(dev->iobase) << 8);
317
318	printk(KERN_INFO "comedi%d: %s ", dev->minor, dev->board_name);
319	if (thisboard->bustype == isa_bustype) {
320		printk("(base %#lx) ", iobase);
321	} else {
322#ifdef CONFIG_COMEDI_PCI
323		printk("(pci %s) ", pci_name(pci_dev));
324#endif
325	}
326
327	printk("attached\n");
328
329	return 1;
330}
331
332/*
333 * _detach is called to deconfigure a device.  It should deallocate
334 * resources.
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.
339 */
340static int pc263_detach(struct comedi_device * dev)
341{
342	printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor,
343		PC263_DRIVER_NAME);
344
345#ifdef CONFIG_COMEDI_PCI
346	if (devpriv)
347#endif
348	{
349#ifdef CONFIG_COMEDI_PCI
350		if (devpriv->pci_dev) {
351			if (dev->iobase) {
352				comedi_pci_disable(devpriv->pci_dev);
353			}
354			pci_dev_put(devpriv->pci_dev);
355		} else
356#endif
357		{
358			if (dev->iobase) {
359				release_region(dev->iobase, PC263_IO_SIZE);
360			}
361		}
362	}
363	if (dev->board_name) {
364		printk(KERN_INFO "comedi%d: %s removed\n",
365			dev->minor, dev->board_name);
366	}
367	return 0;
368}
369
370/*
371 * This function checks and requests an I/O region, reporting an error
372 * if there is a conflict.
373 */
374static int pc263_request_region(unsigned minor, unsigned long from,
375	unsigned long extent)
376{
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);
380		return -EIO;
381	}
382	return 0;
383}
384
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 */
390static int pc263_dio_insn_bits(struct comedi_device * dev, struct comedi_subdevice * s,
391	comedi_insn * insn, unsigned int * data)
392{
393	if (insn->n != 2)
394		return -EINVAL;
395
396	/* The insn data is a mask in data[0] and the new data
397	 * in data[1], each channel cooresponding to a bit. */
398	if (data[0]) {
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);
404	}
405
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 */
410	data[1] = s->state;
411
412	return 2;
413}
414
415static int pc263_dio_insn_config(struct comedi_device * dev, struct comedi_subdevice * s,
416	comedi_insn * insn, unsigned int * data)
417{
418	if (insn->n != 1)
419		return -EINVAL;
420	return 1;
421}
422
423/*
424 * A convenient macro that defines init_module() and cleanup_module(),
425 * as necessary.
426 */
427#ifdef CONFIG_COMEDI_PCI
428COMEDI_PCI_INITCLEANUP(driver_amplc_pc263, pc263_pci_table);
429#else
430COMEDI_INITCLEANUP(driver_amplc_pc263);
431#endif
432