Commit | Line | Data |
---|---|---|
2f82613d MH |
1 | /* |
2 | comedi/drivers/ke_counter.c | |
3 | Comedi driver for Kolter-Electronic PCI Counter 1 Card | |
4 | ||
5 | COMEDI - Linux Control and Measurement Device Interface | |
6 | Copyright (C) 2000 David A. Schleef <ds@schleef.org> | |
7 | ||
8 | This program is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 2 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | This program is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with this program; if not, write to the Free Software | |
20 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
21 | ||
22 | */ | |
23 | /* | |
24 | Driver: ke_counter | |
25 | Description: Driver for Kolter Electronic Counter Card | |
26 | Devices: [Kolter Electronic] PCI Counter Card (ke_counter) | |
27 | Author: Michael Hillmann | |
28 | Updated: Mon, 14 Apr 2008 15:42:42 +0100 | |
29 | Status: tested | |
30 | ||
31 | Configuration Options: | |
32 | [0] - PCI bus of device (optional) | |
33 | [1] - PCI slot of device (optional) | |
34 | If bus/slot is not specified, the first supported | |
35 | PCI device found will be used. | |
36 | ||
37 | This driver is a simple driver to read the counter values from | |
38 | Kolter Electronic PCI Counter Card. | |
39 | */ | |
40 | ||
41 | #include "../comedidev.h" | |
42 | ||
43 | #include "comedi_pci.h" | |
44 | ||
45 | #define CNT_DRIVER_NAME "ke_counter" | |
46 | #define PCI_VENDOR_ID_KOLTER 0x1001 | |
47 | #define CNT_CARD_DEVICE_ID 0x0014 | |
48 | ||
49 | /*-- function prototypes ----------------------------------------------------*/ | |
50 | ||
da91b269 BP |
51 | static int cnt_attach(struct comedi_device *dev, struct comedi_devconfig *it); |
52 | static int cnt_detach(struct comedi_device *dev); | |
2f82613d MH |
53 | |
54 | static DEFINE_PCI_DEVICE_TABLE(cnt_pci_table) = { | |
0a85b6f0 MT |
55 | { |
56 | PCI_VENDOR_ID_KOLTER, CNT_CARD_DEVICE_ID, PCI_ANY_ID, | |
57 | PCI_ANY_ID, 0, 0, 0}, { | |
58 | 0} | |
2f82613d MH |
59 | }; |
60 | ||
61 | MODULE_DEVICE_TABLE(pci, cnt_pci_table); | |
62 | ||
63 | /*-- board specification structure ------------------------------------------*/ | |
64 | ||
9beff277 BP |
65 | struct cnt_board_struct { |
66 | ||
2f82613d MH |
67 | const char *name; |
68 | int device_id; | |
69 | int cnt_channel_nbr; | |
70 | int cnt_bits; | |
9beff277 BP |
71 | }; |
72 | ||
9beff277 | 73 | static const struct cnt_board_struct cnt_boards[] = { |
2f82613d | 74 | { |
0a85b6f0 MT |
75 | .name = CNT_DRIVER_NAME, |
76 | .device_id = CNT_CARD_DEVICE_ID, | |
77 | .cnt_channel_nbr = 3, | |
78 | .cnt_bits = 24} | |
2f82613d MH |
79 | }; |
80 | ||
9beff277 | 81 | #define cnt_board_nbr (sizeof(cnt_boards)/sizeof(struct cnt_board_struct)) |
2f82613d MH |
82 | |
83 | /*-- device private structure -----------------------------------------------*/ | |
84 | ||
2e2269f9 BP |
85 | struct cnt_device_private { |
86 | ||
2f82613d | 87 | struct pci_dev *pcidev; |
2e2269f9 BP |
88 | }; |
89 | ||
2e2269f9 | 90 | #define devpriv ((struct cnt_device_private *)dev->private) |
2f82613d | 91 | |
139dfbdf | 92 | static struct comedi_driver cnt_driver = { |
68c3dbff BP |
93 | .driver_name = CNT_DRIVER_NAME, |
94 | .module = THIS_MODULE, | |
95 | .attach = cnt_attach, | |
96 | .detach = cnt_detach, | |
2f82613d MH |
97 | }; |
98 | ||
727b286b AT |
99 | static int __devinit cnt_driver_pci_probe(struct pci_dev *dev, |
100 | const struct pci_device_id *ent) | |
101 | { | |
102 | return comedi_pci_auto_config(dev, cnt_driver.driver_name); | |
103 | } | |
104 | ||
105 | static void __devexit cnt_driver_pci_remove(struct pci_dev *dev) | |
106 | { | |
107 | comedi_pci_auto_unconfig(dev); | |
108 | } | |
109 | ||
110 | static struct pci_driver cnt_driver_pci_driver = { | |
111 | .id_table = cnt_pci_table, | |
112 | .probe = &cnt_driver_pci_probe, | |
113 | .remove = __devexit_p(&cnt_driver_pci_remove) | |
114 | }; | |
115 | ||
116 | static int __init cnt_driver_init_module(void) | |
117 | { | |
118 | int retval; | |
119 | ||
120 | retval = comedi_driver_register(&cnt_driver); | |
121 | if (retval < 0) | |
122 | return retval; | |
123 | ||
124 | cnt_driver_pci_driver.name = (char *)cnt_driver.driver_name; | |
125 | return pci_register_driver(&cnt_driver_pci_driver); | |
126 | } | |
127 | ||
128 | static void __exit cnt_driver_cleanup_module(void) | |
129 | { | |
130 | pci_unregister_driver(&cnt_driver_pci_driver); | |
131 | comedi_driver_unregister(&cnt_driver); | |
132 | } | |
133 | ||
134 | module_init(cnt_driver_init_module); | |
135 | module_exit(cnt_driver_cleanup_module); | |
2f82613d MH |
136 | |
137 | /*-- counter write ----------------------------------------------------------*/ | |
138 | ||
139 | /* This should be used only for resetting the counters; maybe it is better | |
140 | to make a special command 'reset'. */ | |
da91b269 | 141 | static int cnt_winsn(struct comedi_device *dev, |
0a85b6f0 MT |
142 | struct comedi_subdevice *s, struct comedi_insn *insn, |
143 | unsigned int *data) | |
2f82613d MH |
144 | { |
145 | int chan = CR_CHAN(insn->chanspec); | |
146 | ||
147 | outb((unsigned char)((data[0] >> 24) & 0xff), | |
0a85b6f0 | 148 | dev->iobase + chan * 0x20 + 0x10); |
2f82613d | 149 | outb((unsigned char)((data[0] >> 16) & 0xff), |
0a85b6f0 | 150 | dev->iobase + chan * 0x20 + 0x0c); |
2f82613d | 151 | outb((unsigned char)((data[0] >> 8) & 0xff), |
0a85b6f0 | 152 | dev->iobase + chan * 0x20 + 0x08); |
2f82613d | 153 | outb((unsigned char)((data[0] >> 0) & 0xff), |
0a85b6f0 | 154 | dev->iobase + chan * 0x20 + 0x04); |
2f82613d MH |
155 | |
156 | /* return the number of samples written */ | |
157 | return 1; | |
158 | } | |
159 | ||
160 | /*-- counter read -----------------------------------------------------------*/ | |
161 | ||
da91b269 | 162 | static int cnt_rinsn(struct comedi_device *dev, |
0a85b6f0 MT |
163 | struct comedi_subdevice *s, struct comedi_insn *insn, |
164 | unsigned int *data) | |
2f82613d MH |
165 | { |
166 | unsigned char a0, a1, a2, a3, a4; | |
167 | int chan = CR_CHAN(insn->chanspec); | |
168 | int result; | |
169 | ||
170 | a0 = inb(dev->iobase + chan * 0x20); | |
171 | a1 = inb(dev->iobase + chan * 0x20 + 0x04); | |
172 | a2 = inb(dev->iobase + chan * 0x20 + 0x08); | |
173 | a3 = inb(dev->iobase + chan * 0x20 + 0x0c); | |
174 | a4 = inb(dev->iobase + chan * 0x20 + 0x10); | |
175 | ||
176 | result = (a1 + (a2 * 256) + (a3 * 65536)); | |
177 | if (a4 > 0) | |
178 | result = result - s->maxdata; | |
179 | ||
0a85b6f0 | 180 | *data = (unsigned int)result; |
2f82613d MH |
181 | |
182 | /* return the number of samples read */ | |
183 | return 1; | |
184 | } | |
185 | ||
186 | /*-- attach -----------------------------------------------------------------*/ | |
187 | ||
da91b269 | 188 | static int cnt_attach(struct comedi_device *dev, struct comedi_devconfig *it) |
2f82613d | 189 | { |
34c43922 | 190 | struct comedi_subdevice *subdevice; |
20fb2280 | 191 | struct pci_dev *pci_device = NULL; |
9beff277 | 192 | struct cnt_board_struct *board; |
2f82613d MH |
193 | unsigned long io_base; |
194 | int error, i; | |
195 | ||
196 | /* allocate device private structure */ | |
c3744138 BP |
197 | error = alloc_private(dev, sizeof(struct cnt_device_private)); |
198 | if (error < 0) | |
2f82613d | 199 | return error; |
2f82613d MH |
200 | |
201 | /* Probe the device to determine what device in the series it is. */ | |
20fb2280 | 202 | for_each_pci_dev(pci_device) { |
2f82613d MH |
203 | if (pci_device->vendor == PCI_VENDOR_ID_KOLTER) { |
204 | for (i = 0; i < cnt_board_nbr; i++) { | |
205 | if (cnt_boards[i].device_id == | |
0a85b6f0 | 206 | pci_device->device) { |
2f82613d MH |
207 | /* was a particular bus/slot requested? */ |
208 | if ((it->options[0] != 0) | |
0a85b6f0 | 209 | || (it->options[1] != 0)) { |
2f82613d MH |
210 | /* are we on the wrong bus/slot? */ |
211 | if (pci_device->bus->number != | |
0a85b6f0 MT |
212 | it->options[0] |
213 | || | |
214 | PCI_SLOT(pci_device->devfn) | |
215 | != it->options[1]) { | |
2f82613d MH |
216 | continue; |
217 | } | |
218 | } | |
219 | ||
220 | dev->board_ptr = cnt_boards + i; | |
0a85b6f0 MT |
221 | board = |
222 | (struct cnt_board_struct *) | |
223 | dev->board_ptr; | |
2f82613d MH |
224 | goto found; |
225 | } | |
226 | } | |
227 | } | |
228 | } | |
26ac8785 DH |
229 | printk(KERN_WARNING |
230 | "comedi%d: no supported board found! (req. bus/slot: %d/%d)\n", | |
0a85b6f0 | 231 | dev->minor, it->options[0], it->options[1]); |
2f82613d MH |
232 | return -EIO; |
233 | ||
0a85b6f0 | 234 | found: |
26ac8785 DH |
235 | printk(KERN_INFO |
236 | "comedi%d: found %s at PCI bus %d, slot %d\n", dev->minor, | |
0a85b6f0 MT |
237 | board->name, pci_device->bus->number, |
238 | PCI_SLOT(pci_device->devfn)); | |
2f82613d MH |
239 | devpriv->pcidev = pci_device; |
240 | dev->board_name = board->name; | |
241 | ||
242 | /* enable PCI device and request regions */ | |
c3744138 BP |
243 | error = comedi_pci_enable(pci_device, CNT_DRIVER_NAME); |
244 | if (error < 0) { | |
26ac8785 DH |
245 | printk(KERN_WARNING "comedi%d: " |
246 | "failed to enable PCI device and request regions!\n", | |
247 | dev->minor); | |
2f82613d MH |
248 | return error; |
249 | } | |
250 | ||
251 | /* read register base address [PCI_BASE_ADDRESS #0] */ | |
252 | io_base = pci_resource_start(pci_device, 0); | |
253 | dev->iobase = io_base; | |
254 | ||
255 | /* allocate the subdevice structures */ | |
c3744138 BP |
256 | error = alloc_subdevices(dev, 1); |
257 | if (error < 0) | |
2f82613d | 258 | return error; |
2f82613d MH |
259 | |
260 | subdevice = dev->subdevices + 0; | |
261 | dev->read_subdev = subdevice; | |
262 | ||
263 | subdevice->type = COMEDI_SUBD_COUNTER; | |
264 | subdevice->subdev_flags = SDF_READABLE /* | SDF_COMMON */ ; | |
265 | subdevice->n_chan = board->cnt_channel_nbr; | |
266 | subdevice->maxdata = (1 << board->cnt_bits) - 1; | |
267 | subdevice->insn_read = cnt_rinsn; | |
268 | subdevice->insn_write = cnt_winsn; | |
269 | ||
2696fb57 | 270 | /* select 20MHz clock */ |
2f82613d MH |
271 | outb(3, dev->iobase + 248); |
272 | ||
2696fb57 | 273 | /* reset all counters */ |
2f82613d MH |
274 | outb(0, dev->iobase); |
275 | outb(0, dev->iobase + 0x20); | |
276 | outb(0, dev->iobase + 0x40); | |
277 | ||
26ac8785 DH |
278 | printk(KERN_INFO "comedi%d: " CNT_DRIVER_NAME " attached.\n", |
279 | dev->minor); | |
2f82613d MH |
280 | return 0; |
281 | } | |
282 | ||
283 | /*-- detach -----------------------------------------------------------------*/ | |
284 | ||
da91b269 | 285 | static int cnt_detach(struct comedi_device *dev) |
2f82613d MH |
286 | { |
287 | if (devpriv && devpriv->pcidev) { | |
26ac8785 | 288 | if (dev->iobase) |
2f82613d | 289 | comedi_pci_disable(devpriv->pcidev); |
2f82613d MH |
290 | pci_dev_put(devpriv->pcidev); |
291 | } | |
26ac8785 DH |
292 | printk(KERN_INFO "comedi%d: " CNT_DRIVER_NAME " remove\n", |
293 | dev->minor); | |
2f82613d MH |
294 | return 0; |
295 | } | |
90f703d3 AT |
296 | |
297 | MODULE_AUTHOR("Comedi http://www.comedi.org"); | |
298 | MODULE_DESCRIPTION("Comedi low-level driver"); | |
299 | MODULE_LICENSE("GPL"); |