2 * comedi/drivers/dyna_pci10xx.c
3 * Copyright (C) 2011 Prashant Shah, pshah.mumbai@gmail.com
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 Devices: Dynalog India PCI DAQ Cards, http://www.dynalogindia.com/
23 Author: Prashant Shah <pshah.mumbai@gmail.com>
24 Developed at Automation Labs, Chemical Dept., IIT Bombay, India.
25 Prof. Kannan Moudgalya <kannan@iitb.ac.in>
33 - Dynalog India Pvt. Ltd. does not have a registered PCI Vendor ID and
34 they are using the PLX Technlogies Vendor ID since that is the PCI Chip used
36 - Dynalog India Pvt. Ltd. has provided the internal register specification for
37 their cards in their manuals.
40 #include "../comedidev.h"
41 #include <linux/mutex.h>
43 #define PCI_VENDOR_ID_DYNALOG 0x10b5
44 #define DRV_NAME "dyna_pci10xx"
46 #define READ_TIMEOUT 50
48 static const struct comedi_lrange range_pci1050_ai
= { 3, {
55 static const char range_codes_pci1050_ai
[] = { 0x00, 0x10, 0x30 };
57 static const struct comedi_lrange range_pci1050_ao
= { 1, {
62 static const char range_codes_pci1050_ao
[] = { 0x00 };
75 const struct comedi_lrange
*range_ai
;
76 const char *range_codes_ai
;
77 const struct comedi_lrange
*range_ao
;
78 const char *range_codes_ao
;
81 static const struct boardtype boardtypes
[] = {
83 .name
= "dyna_pci1050",
93 .range_ai
= &range_pci1050_ai
,
94 .range_codes_ai
= range_codes_pci1050_ai
,
95 .range_ao
= &range_pci1050_ao
,
96 .range_codes_ao
= range_codes_pci1050_ao
,
98 /* dummy entry corresponding to driver name */
102 struct dyna_pci10xx_private
{
107 #define thisboard ((const struct boardtype *)dev->board_ptr)
108 #define devpriv ((struct dyna_pci10xx_private *)dev->private)
110 /******************************************************************************/
111 /************************** READ WRITE FUNCTIONS ******************************/
112 /******************************************************************************/
114 /* analog input callback */
115 static int dyna_pci10xx_insn_read_ai(struct comedi_device
*dev
,
116 struct comedi_subdevice
*s
,
117 struct comedi_insn
*insn
, unsigned int *data
)
121 unsigned int chan
, range
;
123 /* get the channel number and range */
124 chan
= CR_CHAN(insn
->chanspec
);
125 range
= thisboard
->range_codes_ai
[CR_RANGE((insn
->chanspec
))];
127 mutex_lock(&devpriv
->mutex
);
128 /* convert n samples */
129 for (n
= 0; n
< insn
->n
; n
++) {
130 /* trigger conversion */
132 outw_p(0x0000 + range
+ chan
, dev
->iobase
+ 2);
135 for (counter
= 0; counter
< READ_TIMEOUT
; counter
++) {
136 d
= inw_p(dev
->iobase
);
138 /* check if read is successful if the EOC bit is set */
143 printk(KERN_DEBUG
"comedi: dyna_pci10xx: "
144 "timeout reading analog input\n");
147 /* mask the first 4 bits - EOC bits */
151 mutex_unlock(&devpriv
->mutex
);
153 /* return the number of samples read/written */
157 /* analog output callback */
158 static int dyna_pci10xx_insn_write_ao(struct comedi_device
*dev
,
159 struct comedi_subdevice
*s
,
160 struct comedi_insn
*insn
, unsigned int *data
)
163 unsigned int chan
, range
;
165 chan
= CR_CHAN(insn
->chanspec
);
166 range
= thisboard
->range_codes_ai
[CR_RANGE((insn
->chanspec
))];
168 mutex_lock(&devpriv
->mutex
);
169 for (n
= 0; n
< insn
->n
; n
++) {
171 /* trigger conversion and write data */
172 outw_p(data
[n
], dev
->iobase
);
175 mutex_unlock(&devpriv
->mutex
);
179 /* digital input bit interface */
180 static int dyna_pci10xx_di_insn_bits(struct comedi_device
*dev
,
181 struct comedi_subdevice
*s
,
182 struct comedi_insn
*insn
, unsigned int *data
)
186 mutex_lock(&devpriv
->mutex
);
188 d
= inw_p(devpriv
->BADR3
);
191 /* on return the data[0] contains output and data[1] contains input */
194 mutex_unlock(&devpriv
->mutex
);
198 /* digital output bit interface */
199 static int dyna_pci10xx_do_insn_bits(struct comedi_device
*dev
,
200 struct comedi_subdevice
*s
,
201 struct comedi_insn
*insn
, unsigned int *data
)
203 /* The insn data is a mask in data[0] and the new data
204 * in data[1], each channel cooresponding to a bit.
205 * s->state contains the previous write data
207 mutex_lock(&devpriv
->mutex
);
209 s
->state
&= ~data
[0];
210 s
->state
|= (data
[0] & data
[1]);
212 outw_p(s
->state
, devpriv
->BADR3
);
217 * On return, data[1] contains the value of the digital
218 * input and output lines. We just return the software copy of the
219 * output values if it was a purely digital output subdevice.
222 mutex_unlock(&devpriv
->mutex
);
226 static struct pci_dev
*dyna_pci10xx_find_pci_dev(struct comedi_device
*dev
,
227 struct comedi_devconfig
*it
)
229 struct pci_dev
*pcidev
= NULL
;
230 int bus
= it
->options
[0];
231 int slot
= it
->options
[1];
234 for_each_pci_dev(pcidev
) {
236 if (bus
!= pcidev
->bus
->number
||
237 slot
!= PCI_SLOT(pcidev
->devfn
))
240 if (pcidev
->vendor
!= PCI_VENDOR_ID_DYNALOG
)
243 for (i
= 0; i
< ARRAY_SIZE(boardtypes
); ++i
) {
244 if (pcidev
->device
!= boardtypes
[i
].device_id
)
247 dev
->board_ptr
= &boardtypes
[i
];
251 dev_err(dev
->class_dev
,
252 "No supported board found! (req. bus %d, slot %d)\n",
257 static int dyna_pci10xx_attach(struct comedi_device
*dev
,
258 struct comedi_devconfig
*it
)
260 struct pci_dev
*pcidev
;
261 struct comedi_subdevice
*s
;
264 if (alloc_private(dev
, sizeof(struct dyna_pci10xx_private
)) < 0) {
265 printk(KERN_ERR
"comedi: dyna_pci10xx: "
266 "failed to allocate memory!\n");
270 pcidev
= dyna_pci10xx_find_pci_dev(dev
, it
);
273 comedi_set_hw_dev(dev
, &pcidev
->dev
);
275 dev
->board_name
= thisboard
->name
;
278 if (comedi_pci_enable(pcidev
, DRV_NAME
)) {
279 printk(KERN_ERR
"comedi: dyna_pci10xx: "
280 "failed to enable PCI device and request regions!");
284 mutex_init(&devpriv
->mutex
);
286 printk(KERN_INFO
"comedi: dyna_pci10xx: device found!\n");
288 dev
->iobase
= pci_resource_start(pcidev
, 2);
289 devpriv
->BADR3
= pci_resource_start(pcidev
, 3);
291 ret
= comedi_alloc_subdevices(dev
, 4);
296 s
= dev
->subdevices
+ 0;
297 s
->type
= COMEDI_SUBD_AI
;
298 s
->subdev_flags
= SDF_READABLE
| SDF_GROUND
| SDF_DIFF
;
299 s
->n_chan
= thisboard
->ai_chans
;
301 s
->range_table
= thisboard
->range_ai
;
302 s
->len_chanlist
= 16;
303 s
->insn_read
= dyna_pci10xx_insn_read_ai
;
306 s
= dev
->subdevices
+ 1;
307 s
->type
= COMEDI_SUBD_AO
;
308 s
->subdev_flags
= SDF_WRITABLE
;
309 s
->n_chan
= thisboard
->ao_chans
;
311 s
->range_table
= thisboard
->range_ao
;
312 s
->len_chanlist
= 16;
313 s
->insn_write
= dyna_pci10xx_insn_write_ao
;
316 s
= dev
->subdevices
+ 2;
317 s
->type
= COMEDI_SUBD_DI
;
318 s
->subdev_flags
= SDF_READABLE
| SDF_GROUND
;
319 s
->n_chan
= thisboard
->di_chans
;
321 s
->range_table
= &range_digital
;
322 s
->len_chanlist
= thisboard
->di_chans
;
323 s
->insn_bits
= dyna_pci10xx_di_insn_bits
;
326 s
= dev
->subdevices
+ 3;
327 s
->type
= COMEDI_SUBD_DO
;
328 s
->subdev_flags
= SDF_WRITABLE
| SDF_GROUND
;
329 s
->n_chan
= thisboard
->do_chans
;
331 s
->range_table
= &range_digital
;
332 s
->len_chanlist
= thisboard
->do_chans
;
334 s
->insn_bits
= dyna_pci10xx_do_insn_bits
;
336 printk(KERN_INFO
"comedi: dyna_pci10xx: %s - device setup completed!\n",
342 static void dyna_pci10xx_detach(struct comedi_device
*dev
)
344 struct pci_dev
*pcidev
= comedi_to_pci_dev(dev
);
347 mutex_destroy(&devpriv
->mutex
);
350 comedi_pci_disable(pcidev
);
355 static struct comedi_driver dyna_pci10xx_driver
= {
356 .driver_name
= "dyna_pci10xx",
357 .module
= THIS_MODULE
,
358 .attach
= dyna_pci10xx_attach
,
359 .detach
= dyna_pci10xx_detach
,
360 .board_name
= &boardtypes
[0].name
,
361 .offset
= sizeof(struct boardtype
),
362 .num_names
= ARRAY_SIZE(boardtypes
),
365 static int __devinit
dyna_pci10xx_pci_probe(struct pci_dev
*dev
,
366 const struct pci_device_id
*ent
)
368 return comedi_pci_auto_config(dev
, &dyna_pci10xx_driver
);
371 static void __devexit
dyna_pci10xx_pci_remove(struct pci_dev
*dev
)
373 comedi_pci_auto_unconfig(dev
);
376 static DEFINE_PCI_DEVICE_TABLE(dyna_pci10xx_pci_table
) = {
377 { PCI_DEVICE(PCI_VENDOR_ID_DYNALOG
, 0x1050) },
380 MODULE_DEVICE_TABLE(pci
, dyna_pci10xx_pci_table
);
382 static struct pci_driver dyna_pci10xx_pci_driver
= {
383 .name
= "dyna_pci10xx",
384 .id_table
= dyna_pci10xx_pci_table
,
385 .probe
= dyna_pci10xx_pci_probe
,
386 .remove
= __devexit_p(dyna_pci10xx_pci_remove
),
388 module_comedi_pci_driver(dyna_pci10xx_driver
, dyna_pci10xx_pci_driver
);
390 MODULE_LICENSE("GPL");
391 MODULE_AUTHOR("Prashant Shah <pshah.mumbai@gmail.com>");
392 MODULE_DESCRIPTION("Comedi based drivers for Dynalog PCI DAQ cards");