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 */
97 * This function looks for a board matching the supplied PCI device.
99 static const struct pc263_board
*pc263_find_pci_board(struct pci_dev
*pci_dev
)
103 for (i
= 0; i
< ARRAY_SIZE(pc263_boards
); i
++)
104 if (pc263_boards
[i
].bustype
== pci_bustype
&&
105 pci_dev
->device
== pc263_boards
[i
].devid
)
106 return &pc263_boards
[i
];
112 * This function looks for a PCI device matching the requested board name,
115 static struct pci_dev
*pc263_find_pci_dev(struct comedi_device
*dev
,
116 struct comedi_devconfig
*it
)
118 const struct pc263_board
*thisboard
= comedi_board(dev
);
119 struct pci_dev
*pci_dev
= NULL
;
120 int bus
= it
->options
[0];
121 int slot
= it
->options
[1];
123 for_each_pci_dev(pci_dev
) {
125 if (bus
!= pci_dev
->bus
->number
||
126 slot
!= PCI_SLOT(pci_dev
->devfn
))
129 if (pci_dev
->vendor
!= PCI_VENDOR_ID_AMPLICON
)
132 if (thisboard
->model
== anypci_model
) {
133 /* Wildcard board matches any supported PCI board. */
134 const struct pc263_board
*foundboard
;
136 foundboard
= pc263_find_pci_board(pci_dev
);
137 if (foundboard
== NULL
)
139 /* Replace wildcard board_ptr. */
140 dev
->board_ptr
= thisboard
= foundboard
;
142 /* Match specific model name. */
143 if (pci_dev
->device
!= thisboard
->devid
)
148 dev_err(dev
->class_dev
,
149 "No supported board found! (req. bus %d, slot %d)\n",
154 * This function checks and requests an I/O region, reporting an error
155 * if there is a conflict.
157 static int pc263_request_region(struct comedi_device
*dev
, unsigned long from
,
158 unsigned long extent
)
160 if (!from
|| !request_region(from
, extent
, PC263_DRIVER_NAME
)) {
161 dev_err(dev
->class_dev
, "I/O port conflict (%#lx,%lu)!\n",
168 static int pc263_do_insn_bits(struct comedi_device
*dev
,
169 struct comedi_subdevice
*s
,
170 struct comedi_insn
*insn
, unsigned int *data
)
172 /* The insn data is a mask in data[0] and the new data
173 * in data[1], each channel cooresponding to a bit. */
175 s
->state
&= ~data
[0];
176 s
->state
|= data
[0] & data
[1];
177 /* Write out the new digital output lines */
178 outb(s
->state
& 0xFF, dev
->iobase
);
179 outb(s
->state
>> 8, dev
->iobase
+ 1);
184 static void pc263_report_attach(struct comedi_device
*dev
)
186 const struct pc263_board
*thisboard
= comedi_board(dev
);
187 struct pci_dev
*pcidev
= comedi_to_pci_dev(dev
);
190 if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_ISA
) &&
191 thisboard
->bustype
== isa_bustype
)
192 snprintf(tmpbuf
, sizeof(tmpbuf
), "(base %#lx) ", dev
->iobase
);
193 else if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI
) &&
194 thisboard
->bustype
== pci_bustype
)
195 snprintf(tmpbuf
, sizeof(tmpbuf
), "(pci %s) ",
199 dev_info(dev
->class_dev
, "%s %sattached\n", dev
->board_name
, tmpbuf
);
202 static int pc263_common_attach(struct comedi_device
*dev
, unsigned long iobase
)
204 const struct pc263_board
*thisboard
= comedi_board(dev
);
205 struct comedi_subdevice
*s
;
208 dev
->board_name
= thisboard
->name
;
209 dev
->iobase
= iobase
;
211 ret
= comedi_alloc_subdevices(dev
, 1);
215 s
= dev
->subdevices
+ 0;
216 /* digital output subdevice */
217 s
->type
= COMEDI_SUBD_DO
;
218 s
->subdev_flags
= SDF_READABLE
| SDF_WRITABLE
;
221 s
->range_table
= &range_digital
;
222 s
->insn_bits
= pc263_do_insn_bits
;
223 /* read initial relay state */
224 s
->state
= inb(dev
->iobase
) | (inb(dev
->iobase
+ 1) << 8);
226 pc263_report_attach(dev
);
230 static int pc263_pci_common_attach(struct comedi_device
*dev
,
231 struct pci_dev
*pci_dev
)
233 unsigned long iobase
;
236 comedi_set_hw_dev(dev
, &pci_dev
->dev
);
238 ret
= comedi_pci_enable(pci_dev
, PC263_DRIVER_NAME
);
240 dev_err(dev
->class_dev
,
241 "error! cannot enable PCI device and request regions!\n");
244 iobase
= pci_resource_start(pci_dev
, 2);
245 return pc263_common_attach(dev
, iobase
);
249 * Attach is called by the Comedi core to configure the driver
250 * for a particular board. If you specified a board_name array
251 * in the driver structure, dev->board_ptr contains that
254 static int pc263_attach(struct comedi_device
*dev
, struct comedi_devconfig
*it
)
256 const struct pc263_board
*thisboard
= comedi_board(dev
);
259 dev_info(dev
->class_dev
, PC263_DRIVER_NAME
": attach\n");
261 /* Process options and reserve resources according to bus type. */
262 if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_ISA
) &&
263 thisboard
->bustype
== isa_bustype
) {
264 unsigned long iobase
= it
->options
[0];
265 ret
= pc263_request_region(dev
, iobase
, PC263_IO_SIZE
);
268 return pc263_common_attach(dev
, iobase
);
269 } else if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI
) &&
270 thisboard
->bustype
== pci_bustype
) {
271 struct pci_dev
*pci_dev
;
273 pci_dev
= pc263_find_pci_dev(dev
, it
);
276 return pc263_pci_common_attach(dev
, pci_dev
);
278 dev_err(dev
->class_dev
, PC263_DRIVER_NAME
279 ": BUG! cannot determine board type!\n");
284 * The attach_pci hook (if non-NULL) is called at PCI probe time in preference
285 * to the "manual" attach hook. dev->board_ptr is NULL on entry. There should
286 * be a board entry matching the supplied PCI device.
288 static int __devinit
pc263_attach_pci(struct comedi_device
*dev
,
289 struct pci_dev
*pci_dev
)
291 if (!IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI
))
294 dev_info(dev
->class_dev
, PC263_DRIVER_NAME
": attach pci %s\n",
296 dev
->board_ptr
= pc263_find_pci_board(pci_dev
);
297 if (dev
->board_ptr
== NULL
) {
298 dev_err(dev
->class_dev
, "BUG! cannot determine board type!\n");
302 * Need to 'get' the PCI device to match the 'put' in pc263_detach().
303 * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
304 * support for manual attachment of PCI devices via pc263_attach()
307 pci_dev_get(pci_dev
);
308 return pc263_pci_common_attach(dev
, pci_dev
);
311 static void pc263_detach(struct comedi_device
*dev
)
313 struct pci_dev
*pcidev
= comedi_to_pci_dev(dev
);
317 comedi_pci_disable(pcidev
);
321 release_region(dev
->iobase
, PC263_IO_SIZE
);
326 * The struct comedi_driver structure tells the Comedi core module
327 * which functions to call to configure/deconfigure (attach/detach)
328 * the board, and also about the kernel module that contains
331 static struct comedi_driver amplc_pc263_driver
= {
332 .driver_name
= PC263_DRIVER_NAME
,
333 .module
= THIS_MODULE
,
334 .attach
= pc263_attach
,
335 .attach_pci
= pc263_attach_pci
,
336 .detach
= pc263_detach
,
337 .board_name
= &pc263_boards
[0].name
,
338 .offset
= sizeof(struct pc263_board
),
339 .num_names
= ARRAY_SIZE(pc263_boards
),
342 #if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI)
343 static DEFINE_PCI_DEVICE_TABLE(pc263_pci_table
) = {
344 { PCI_DEVICE(PCI_VENDOR_ID_AMPLICON
, PCI_DEVICE_ID_AMPLICON_PCI263
) },
347 MODULE_DEVICE_TABLE(pci
, pc263_pci_table
);
349 static int __devinit
amplc_pc263_pci_probe(struct pci_dev
*dev
,
350 const struct pci_device_id
353 return comedi_pci_auto_config(dev
, &lc_pc263_driver
);
356 static void __devexit
amplc_pc263_pci_remove(struct pci_dev
*dev
)
358 comedi_pci_auto_unconfig(dev
);
361 static struct pci_driver amplc_pc263_pci_driver
= {
362 .name
= PC263_DRIVER_NAME
,
363 .id_table
= pc263_pci_table
,
364 .probe
= &lc_pc263_pci_probe
,
365 .remove
= __devexit_p(&lc_pc263_pci_remove
)
367 module_comedi_pci_driver(amplc_pc263_driver
, amplc_pc263_pci_driver
);
369 module_comedi_driver(amplc_pc263_driver
);
372 MODULE_AUTHOR("Comedi http://www.comedi.org");
373 MODULE_DESCRIPTION("Comedi low-level driver");
374 MODULE_LICENSE("GPL");