PageRenderTime 36ms CodeModel.GetById 21ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 0ms

/drivers/staging/comedi/drivers/amplc_pc263.c

https://bitbucket.org/cyanogenmod/android_kernel_asus_tf300t
C | 491 lines | 317 code | 49 blank | 125 comment | 46 complexity | e6b84b6207e3fc731f9f968a76e909ed MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
  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*/
122#ifdef CONFIG_COMEDI_PCI
123struct pc263_private {
124	/* PCI device. */
125	struct pci_dev *pci_dev;
126};
127
128#define devpriv ((struct pc263_private *)dev->private)
129#endif /* CONFIG_COMEDI_PCI */
130
131/*
132 * The struct comedi_driver structure tells the Comedi core module
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 */
137static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it);
138static int pc263_detach(struct comedi_device *dev);
139static struct comedi_driver driver_amplc_pc263 = {
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),
146	.num_names = ARRAY_SIZE(pc263_boards),
147};
148
149static int pc263_request_region(unsigned minor, unsigned long from,
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);
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
164pc263_find_pci(struct comedi_device *dev, int bus, int slot,
165	       struct pci_dev **pci_dev_p)
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);
173	     pci_dev != NULL;
174	     pci_dev = pci_get_device(PCI_VENDOR_ID_AMPLICON,
175				      PCI_ANY_ID, pci_dev)) {
176		/* If bus/slot specified, check them. */
177		if (bus || slot) {
178			if (bus != pci_dev->bus->number
179			    || slot != PCI_SLOT(pci_dev->devfn))
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
210		       "comedi%d: error! no %s found at pci %02x:%02x!\n",
211		       dev->minor, thisboard->name, bus, slot);
212	} else {
213		printk(KERN_ERR "comedi%d: error! no %s found!\n",
214		       dev->minor, thisboard->name);
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 */
226static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it)
227{
228	struct comedi_subdevice *s;
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,
237	       PC263_DRIVER_NAME);
238/*
239 * Allocate the private structure area.  alloc_private() is a
240 * convenient macro defined in comedidev.h.
241 */
242#ifdef CONFIG_COMEDI_PCI
243	ret = alloc_private(dev, sizeof(struct pc263_private));
244	if (ret < 0) {
245		printk(KERN_ERR "comedi%d: error! out of memory!\n",
246		       dev->minor);
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
260		ret = pc263_find_pci(dev, bus, slot, &pci_dev);
261		if (ret < 0)
262			return ret;
263		devpriv->pci_dev = pci_dev;
264		break;
265#endif /* CONFIG_COMEDI_PCI */
266	default:
267		printk(KERN_ERR
268		       "comedi%d: %s: BUG! cannot determine board type!\n",
269		       dev->minor, PC263_DRIVER_NAME);
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) {
282		ret = comedi_pci_enable(pci_dev, PC263_DRIVER_NAME);
283		if (ret < 0) {
284			printk(KERN_ERR
285			       "comedi%d: error! cannot enable PCI device and "
286				"request regions!\n",
287			       dev->minor);
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);
295		if (ret < 0)
296			return ret;
297	}
298	dev->iobase = iobase;
299
300/*
301 * Allocate the subdevice structures.  alloc_subdevice() is a
302 * convenient macro defined in comedidev.h.
303 */
304	ret = alloc_subdevices(dev, 1);
305	if (ret < 0) {
306		printk(KERN_ERR "comedi%d: error! out of memory!\n",
307		       dev->minor);
308		return ret;
309	}
310
311	s = dev->subdevices + 0;
312	/* digital i/o subdevice */
313	s->type = COMEDI_SUBD_DIO;
314	s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
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 */
348static int pc263_detach(struct comedi_device *dev)
349{
350	printk(KERN_DEBUG "comedi%d: %s: detach\n", dev->minor,
351	       PC263_DRIVER_NAME);
352
353#ifdef CONFIG_COMEDI_PCI
354	if (devpriv) {
355#endif
356#ifdef CONFIG_COMEDI_PCI
357		if (devpriv->pci_dev) {
358			if (dev->iobase)
359				comedi_pci_disable(devpriv->pci_dev);
360			pci_dev_put(devpriv->pci_dev);
361		} else
362#endif
363		{
364			if (dev->iobase)
365				release_region(dev->iobase, PC263_IO_SIZE);
366		}
367	}
368	if (dev->board_name) {
369		printk(KERN_INFO "comedi%d: %s removed\n",
370		       dev->minor, dev->board_name);
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,
380				unsigned long extent)
381{
382	if (!from || !request_region(from, extent, PC263_DRIVER_NAME)) {
383		printk(KERN_ERR "comedi%d: I/O port conflict (%#lx,%lu)!\n",
384		       minor, from, extent);
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 */
395static int pc263_dio_insn_bits(struct comedi_device *dev,
396			       struct comedi_subdevice *s,
397			       struct comedi_insn *insn, unsigned int *data)
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
421static int pc263_dio_insn_config(struct comedi_device *dev,
422				 struct comedi_subdevice *s,
423				 struct comedi_insn *insn, unsigned int *data)
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
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);
474#else
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);
487#endif
488
489MODULE_AUTHOR("Comedi http://www.comedi.org");
490MODULE_DESCRIPTION("Comedi low-level driver");
491MODULE_LICENSE("GPL");