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 /*
26 Driver: amplc_pc263
27 Description: Amplicon PC263, PCI263
28 Author: Ian Abbott <abbotti@mev.co.uk>
29 Devices: [Amplicon] PC263 (pc263), PCI263 (pci263 or amplc_pc263)
30 Updated: Wed, 22 Oct 2008 14:10:53 +0100
31 Status: works
32 
33 Configuration options - PC263:
34   [0] - I/O port base address
35 
36 Configuration 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 
42 Each board appears as one subdevice, with 16 digital outputs, each
43 connected to a reed-relay. Relay contacts are closed when output is 1.
44 The 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 
65 enum pc263_bustype { isa_bustype, pci_bustype };
66 enum pc263_model { pc263_model, pci263_model, anypci_model };
67 
68 struct 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 };
75 static 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
103 static DEFINE_PCI_DEVICE_TABLE(pc263_pci_table) = {
104 	{ PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI263) },
105 	{0}
106 };
107 
108 MODULE_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
121 struct 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  */
135 static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it);
136 static int pc263_detach(struct comedi_device *dev);
137 static 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 
147 static int pc263_request_region(unsigned minor, unsigned long from,
148 				unsigned long extent);
149 static int pc263_dio_insn_bits(struct comedi_device *dev,
150 			       struct comedi_subdevice *s,
151 			       struct comedi_insn *insn, unsigned int *data);
152 static 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
161 static int
pc263_find_pci(struct comedi_device * dev,int bus,int slot,struct pci_dev ** pci_dev_p)162 pc263_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  */
pc263_attach(struct comedi_device * dev,struct comedi_devconfig * it)224 static 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  */
pc263_detach(struct comedi_device * dev)346 static 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  */
pc263_request_region(unsigned minor,unsigned long from,unsigned long extent)377 static 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 */
pc263_dio_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)393 static 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 
pc263_dio_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)419 static 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
driver_amplc_pc263_pci_probe(struct pci_dev * dev,const struct pci_device_id * ent)433 static 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 
driver_amplc_pc263_pci_remove(struct pci_dev * dev)440 static void __devexit driver_amplc_pc263_pci_remove(struct pci_dev *dev)
441 {
442 	comedi_pci_auto_unconfig(dev);
443 }
444 
445 static 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 
driver_amplc_pc263_init_module(void)451 static 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 
driver_amplc_pc263_cleanup_module(void)464 static 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 
470 module_init(driver_amplc_pc263_init_module);
471 module_exit(driver_amplc_pc263_cleanup_module);
472 #else
driver_amplc_pc263_init_module(void)473 static int __init driver_amplc_pc263_init_module(void)
474 {
475 	return comedi_driver_register(&driver_amplc_pc263);
476 }
477 
driver_amplc_pc263_cleanup_module(void)478 static void __exit driver_amplc_pc263_cleanup_module(void)
479 {
480 	comedi_driver_unregister(&driver_amplc_pc263);
481 }
482 
483 module_init(driver_amplc_pc263_init_module);
484 module_exit(driver_amplc_pc263_cleanup_module);
485 #endif
486 
487 MODULE_AUTHOR("Comedi http://www.comedi.org");
488 MODULE_DESCRIPTION("Comedi low-level driver");
489 MODULE_LICENSE("GPL");
490