2 comedi/drivers/amplc_pc263.c
3 Driver for Amplicon PC263 and PCI263 relay boards.
5 Copyright (C) 2002 MEV Ltd. <http://www.mev.co.uk/>
7 COMEDI - Linux Control and Measurement Device Interface
8 Copyright (C) 2000 David A. Schleef <ds@schleef.org>
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.
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.
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.
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
33 Configuration options - PC263:
34 [0] - I/O port base address
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
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.
47 #include "../comedidev.h"
49 #define PC263_DRIVER_NAME "amplc_pc263"
51 /* PCI263 PCI configuration register information */
52 #define PCI_VENDOR_ID_AMPLICON 0x14dc
53 #define PCI_DEVICE_ID_AMPLICON_PCI263 0x000c
54 #define PCI_DEVICE_ID_INVALID 0xffff
56 /* PC263 / PCI263 registers */
57 #define PC263_IO_SIZE 2
60 * Board descriptions for Amplicon PC263 / PCI263.
63 enum pc263_bustype
{ isa_bustype
, pci_bustype
};
64 enum pc263_model
{ pc263_model
, pci263_model
, anypci_model
};
69 enum pc263_bustype bustype
;
70 enum pc263_model model
;
72 static const struct pc263_board pc263_boards
[] = {
73 #if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_ISA)
76 .bustype
= isa_bustype
,
80 #if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI)
83 .devid
= PCI_DEVICE_ID_AMPLICON_PCI263
,
84 .bustype
= pci_bustype
,
85 .model
= pci263_model
,
88 .name
= PC263_DRIVER_NAME
,
89 .devid
= PCI_DEVICE_ID_INVALID
,
90 .bustype
= pci_bustype
,
91 .model
= anypci_model
, /* wildcard */
96 /* this structure is for data unique to this hardware driver. If
97 several hardware drivers keep similar information in this structure,
98 feel free to suggest moving the variable to the struct comedi_device struct.
100 struct pc263_private
{
102 struct pci_dev
*pci_dev
;
106 * This function looks for a board matching the supplied PCI device.
108 static const struct pc263_board
*pc263_find_pci_board(struct pci_dev
*pci_dev
)
112 for (i
= 0; i
< ARRAY_SIZE(pc263_boards
); i
++)
113 if (pc263_boards
[i
].bustype
== pci_bustype
&&
114 pci_dev
->device
== pc263_boards
[i
].devid
)
115 return &pc263_boards
[i
];
121 * This function looks for a PCI device matching the requested board name,
124 static struct pci_dev
*
125 pc263_find_pci(struct comedi_device
*dev
, int bus
, int slot
)
127 const struct pc263_board
*thisboard
= comedi_board(dev
);
128 struct pci_dev
*pci_dev
= NULL
;
130 /* Look for matching PCI device. */
131 for (pci_dev
= pci_get_device(PCI_VENDOR_ID_AMPLICON
, PCI_ANY_ID
, NULL
);
133 pci_dev
= pci_get_device(PCI_VENDOR_ID_AMPLICON
,
134 PCI_ANY_ID
, pci_dev
)) {
135 /* If bus/slot specified, check them. */
137 if (bus
!= pci_dev
->bus
->number
138 || slot
!= PCI_SLOT(pci_dev
->devfn
))
141 if (thisboard
->model
== anypci_model
) {
142 /* Wildcard board matches any supported PCI board. */
143 const struct pc263_board
*foundboard
;
144 foundboard
= pc263_find_pci_board(pci_dev
);
145 if (foundboard
== NULL
)
147 /* Replace wildcard board_ptr. */
148 dev
->board_ptr
= thisboard
= foundboard
;
150 /* Match specific model name. */
151 if (pci_dev
->device
!= thisboard
->devid
)
158 /* No match found. */
160 dev_err(dev
->class_dev
,
161 "error! no %s found at pci %02x:%02x!\n",
162 thisboard
->name
, bus
, slot
);
164 dev_err(dev
->class_dev
, "error! no %s found!\n",
170 * This function checks and requests an I/O region, reporting an error
171 * if there is a conflict.
173 static int pc263_request_region(struct comedi_device
*dev
, unsigned long from
,
174 unsigned long extent
)
176 if (!from
|| !request_region(from
, extent
, PC263_DRIVER_NAME
)) {
177 dev_err(dev
->class_dev
, "I/O port conflict (%#lx,%lu)!\n",
184 static int pc263_do_insn_bits(struct comedi_device
*dev
,
185 struct comedi_subdevice
*s
,
186 struct comedi_insn
*insn
, unsigned int *data
)
191 /* The insn data is a mask in data[0] and the new data
192 * in data[1], each channel cooresponding to a bit. */
194 s
->state
&= ~data
[0];
195 s
->state
|= data
[0] & data
[1];
196 /* Write out the new digital output lines */
197 outb(s
->state
& 0xFF, dev
->iobase
);
198 outb(s
->state
>> 8, dev
->iobase
+ 1);
203 static void pc263_report_attach(struct comedi_device
*dev
)
205 const struct pc263_board
*thisboard
= comedi_board(dev
);
206 struct pc263_private
*devpriv
= dev
->private;
209 if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_ISA
) &&
210 thisboard
->bustype
== isa_bustype
)
211 snprintf(tmpbuf
, sizeof(tmpbuf
), "(base %#lx) ", dev
->iobase
);
212 else if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI
) &&
213 thisboard
->bustype
== pci_bustype
)
214 snprintf(tmpbuf
, sizeof(tmpbuf
), "(pci %s) ",
215 pci_name(devpriv
->pci_dev
));
218 dev_info(dev
->class_dev
, "%s %sattached\n", dev
->board_name
, tmpbuf
);
221 static int pc263_common_attach(struct comedi_device
*dev
, unsigned long iobase
)
223 const struct pc263_board
*thisboard
= comedi_board(dev
);
224 struct comedi_subdevice
*s
;
227 dev
->board_name
= thisboard
->name
;
228 dev
->iobase
= iobase
;
230 ret
= comedi_alloc_subdevices(dev
, 1);
232 dev_err(dev
->class_dev
, "error! out of memory!\n");
236 s
= dev
->subdevices
+ 0;
237 /* digital output subdevice */
238 s
->type
= COMEDI_SUBD_DO
;
239 s
->subdev_flags
= SDF_READABLE
| SDF_WRITABLE
;
242 s
->range_table
= &range_digital
;
243 s
->insn_bits
= pc263_do_insn_bits
;
244 /* read initial relay state */
245 s
->state
= inb(dev
->iobase
) | (inb(dev
->iobase
+ 1) << 8);
247 pc263_report_attach(dev
);
251 static int pc263_pci_common_attach(struct comedi_device
*dev
,
252 struct pci_dev
*pci_dev
)
254 struct pc263_private
*devpriv
= dev
->private;
255 unsigned long iobase
;
258 devpriv
->pci_dev
= pci_dev
;
259 ret
= comedi_pci_enable(pci_dev
, PC263_DRIVER_NAME
);
261 dev_err(dev
->class_dev
,
262 "error! cannot enable PCI device and request regions!\n");
265 iobase
= pci_resource_start(pci_dev
, 2);
266 return pc263_common_attach(dev
, iobase
);
270 * Attach is called by the Comedi core to configure the driver
271 * for a particular board. If you specified a board_name array
272 * in the driver structure, dev->board_ptr contains that
275 static int pc263_attach(struct comedi_device
*dev
, struct comedi_devconfig
*it
)
277 const struct pc263_board
*thisboard
= comedi_board(dev
);
280 dev_info(dev
->class_dev
, PC263_DRIVER_NAME
": attach\n");
282 /* Process options and reserve resources according to bus type. */
283 if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_ISA
) &&
284 thisboard
->bustype
== isa_bustype
) {
285 unsigned long iobase
= it
->options
[0];
286 ret
= pc263_request_region(dev
, iobase
, PC263_IO_SIZE
);
289 return pc263_common_attach(dev
, iobase
);
290 } else if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI
) &&
291 thisboard
->bustype
== pci_bustype
) {
292 struct pci_dev
*pci_dev
;
295 ret
= alloc_private(dev
, sizeof(struct pc263_private
));
297 dev_err(dev
->class_dev
, "error! out of memory!\n");
300 bus
= it
->options
[0];
301 slot
= it
->options
[1];
302 pci_dev
= pc263_find_pci(dev
, bus
, slot
);
305 return pc263_pci_common_attach(dev
, pci_dev
);
307 dev_err(dev
->class_dev
, PC263_DRIVER_NAME
308 ": BUG! cannot determine board type!\n");
313 * The attach_pci hook (if non-NULL) is called at PCI probe time in preference
314 * to the "manual" attach hook. dev->board_ptr is NULL on entry. There should
315 * be a board entry matching the supplied PCI device.
317 static int __devinit
pc263_attach_pci(struct comedi_device
*dev
,
318 struct pci_dev
*pci_dev
)
322 if (!IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI
))
325 dev_info(dev
->class_dev
, PC263_DRIVER_NAME
": attach pci %s\n",
327 ret
= alloc_private(dev
, sizeof(struct pc263_private
));
329 dev_err(dev
->class_dev
, "error! out of memory!\n");
332 dev
->board_ptr
= pc263_find_pci_board(pci_dev
);
333 if (dev
->board_ptr
== NULL
) {
334 dev_err(dev
->class_dev
, "BUG! cannot determine board type!\n");
337 return pc263_pci_common_attach(dev
, pci_dev
);
340 static void pc263_detach(struct comedi_device
*dev
)
342 struct pc263_private
*devpriv
= dev
->private;
344 if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI
) && devpriv
&&
347 comedi_pci_disable(devpriv
->pci_dev
);
348 pci_dev_put(devpriv
->pci_dev
);
349 } else if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_ISA
)) {
351 release_region(dev
->iobase
, PC263_IO_SIZE
);
356 * The struct comedi_driver structure tells the Comedi core module
357 * which functions to call to configure/deconfigure (attach/detach)
358 * the board, and also about the kernel module that contains
361 static struct comedi_driver amplc_pc263_driver
= {
362 .driver_name
= PC263_DRIVER_NAME
,
363 .module
= THIS_MODULE
,
364 .attach
= pc263_attach
,
365 .attach_pci
= pc263_attach_pci
,
366 .detach
= pc263_detach
,
367 .board_name
= &pc263_boards
[0].name
,
368 .offset
= sizeof(struct pc263_board
),
369 .num_names
= ARRAY_SIZE(pc263_boards
),
372 #if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI)
373 static DEFINE_PCI_DEVICE_TABLE(pc263_pci_table
) = {
374 { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON
, PCI_DEVICE_ID_AMPLICON_PCI263
) },
377 MODULE_DEVICE_TABLE(pci
, pc263_pci_table
);
379 static int __devinit
amplc_pc263_pci_probe(struct pci_dev
*dev
,
380 const struct pci_device_id
383 return comedi_pci_auto_config(dev
, &lc_pc263_driver
);
386 static void __devexit
amplc_pc263_pci_remove(struct pci_dev
*dev
)
388 comedi_pci_auto_unconfig(dev
);
391 static struct pci_driver amplc_pc263_pci_driver
= {
392 .name
= PC263_DRIVER_NAME
,
393 .id_table
= pc263_pci_table
,
394 .probe
= &lc_pc263_pci_probe
,
395 .remove
= __devexit_p(&lc_pc263_pci_remove
)
397 module_comedi_pci_driver(amplc_pc263_driver
, amplc_pc263_pci_driver
);
399 module_comedi_driver(amplc_pc263_driver
);
402 MODULE_AUTHOR("Comedi http://www.comedi.org");
403 MODULE_DESCRIPTION("Comedi low-level driver");
404 MODULE_LICENSE("GPL");