Changed prototype of alloc_subdevices() so it doesn't require dev->n_subdevices
[comedi.git] / comedi / drivers / ni_670x.c
1 /*
2     comedi/drivers/ni_670x.c
3     Hardware driver for NI 670x devices
4
5     COMEDI - Linux Control and Measurement Device Interface
6     Copyright (C) 1997-2001 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: ni_670x.o
25 Description: National Instruments 670x
26 Author: Bart Joris <bjoris@advalvas.be>
27 Updated: Wed, 11 Dec 2002 18:25:35 -0800
28 Devices: [National Instruments] PCI-6703 (ni_670x), PCI-6704
29 Status: unknown
30
31 Commands are not supported.
32 */
33
34 /*
35         Bart Joris <bjoris@advalvas.be> Last updated on 20/08/2001
36
37         Manuals:
38
39         322110a.pdf     PCI/PXI-6704 User Manual
40         322110b.pdf     PCI/PXI-6703/6704 User Manual
41
42 */
43
44 #include <linux/comedidev.h>
45
46 #include "mite.h"
47
48 #define PCI_VENDOR_ID_NATINST   0x1093
49
50 #define AO_VALUE_OFFSET                 0x00
51 #define AO_CHAN_OFFSET                  0x0c
52 #define AO_STATUS_OFFSET                0x10
53 #define AO_CONTROL_OFFSET               0x10
54 #define DIO_PORT0_DIR_OFFSET    0x20    
55 #define DIO_PORT0_DATA_OFFSET   0x24
56 #define DIO_PORT1_DIR_OFFSET    0x28
57 #define DIO_PORT1_DATA_OFFSET   0x2c
58 #define MISC_STATUS_OFFSET              0x14
59 #define MISC_CONTROL_OFFSET             0x14
60
61 /* Board description*/
62
63 typedef struct ni_670x_board_struct
64 {
65         unsigned short dev_id;
66         char *name;
67         unsigned short ao_chans;
68         unsigned short ao_bits;
69 }ni_670x_board;
70 static ni_670x_board ni_670x_boards[] = 
71 {
72         {
73         dev_id          : 0x2c90,
74         name            : "PCI-6703",
75         ao_chans        : 16,
76         ao_bits         : 16,
77         },
78         {
79         dev_id          : 0x1920,
80         name            : "PXI-6704",
81         ao_chans        : 32,
82         ao_bits         : 16,
83         },
84         {
85         dev_id          : 0x0000,
86         name            : "PCI-6704",
87         ao_chans        : 32,
88         ao_bits         : 16,
89         },
90 };
91
92 static struct pci_device_id ni_670x_pci_table[] __devinitdata = {
93         { PCI_VENDOR_ID_NATINST, 0x2c90, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
94         { PCI_VENDOR_ID_NATINST, 0x1920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
95         //{ PCI_VENDOR_ID_NATINST, 0x0000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
96         { 0 }
97 };
98 MODULE_DEVICE_TABLE(pci, ni_670x_pci_table);
99
100 #define thisboard ((ni_670x_board *)dev->board_ptr)
101
102 typedef struct
103 {
104         struct mite_struct *mite;
105         int boardtype;
106         int dio;
107         lsampl_t ao_readback[32];
108 }ni_670x_private;
109
110 #define devpriv ((ni_670x_private *)dev->private)
111 #define n_ni_670x_boards (sizeof(ni_670x_boards)/sizeof(ni_670x_boards[0]))
112
113 static int ni_670x_attach(comedi_device *dev,comedi_devconfig *it);
114 static int ni_670x_detach(comedi_device *dev);
115
116 static comedi_driver driver_ni_670x=
117 {
118         driver_name:    "ni_670x",
119         module:         THIS_MODULE,
120         attach:         ni_670x_attach,
121         detach:         ni_670x_detach,
122 };
123 COMEDI_INITCLEANUP(driver_ni_670x);
124
125 static comedi_lrange range_0_20mA = { 1, { RANGE_mA(0,20)}};
126
127 static int ni_670x_find_device(comedi_device *dev,int bus,int slot);
128
129 static int ni_670x_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
130 static int ni_670x_ao_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
131 static int ni_670x_dio_insn_bits(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
132 static int ni_670x_dio_insn_config(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data);
133
134
135 static int ni_670x_attach(comedi_device *dev,comedi_devconfig *it)
136 {
137         comedi_subdevice *s;
138         int ret;
139         int i;
140         
141         printk("comedi%d: ni_670x: ",dev->minor);
142
143         if((ret=alloc_private(dev,sizeof(ni_670x_private)))<0)
144                 return ret;
145                 
146         ret=ni_670x_find_device(dev,it->options[0],it->options[1]);
147         if(ret<0) return ret;
148         
149         ret = mite_setup(devpriv->mite);
150         if(ret < 0)
151         {
152                 printk("error setting up mite\n");
153                 return ret;
154         }
155         dev->iobase=mite_iobase(devpriv->mite);
156         dev->board_name=thisboard->name;
157         dev->irq=mite_irq(devpriv->mite);
158         printk(" %s",dev->board_name);
159
160         if(alloc_subdevices(dev, 2)<0)
161                 return -ENOMEM;
162
163         s=dev->subdevices+0;
164         /* analog output subdevice */
165         s->type                 =       COMEDI_SUBD_AO;
166         s->subdev_flags         =       SDF_WRITABLE;
167         s->n_chan               =       thisboard->ao_chans;
168         s->maxdata              =       0xffff;
169         if(s->n_chan == 32){
170                 s->range_table_list = kmalloc(sizeof(comedi_lrange *)*32,
171                         GFP_KERNEL);
172                 for(i=0;i<16;i++){
173                         s->range_table_list[i] = &range_bipolar10; 
174                         s->range_table_list[16+i] = &range_0_20mA;
175                 }
176         }else{
177                 s->range_table  =       &range_bipolar10; 
178         }
179         s->insn_write           =       &ni_670x_ao_winsn;
180         s->insn_read            =       &ni_670x_ao_rinsn;
181
182         s=dev->subdevices+1;
183         /* digital i/o subdevice */
184         s->type                 =       COMEDI_SUBD_DIO;
185         s->subdev_flags =       SDF_READABLE|SDF_WRITABLE;
186         s->n_chan               =       8;
187         s->maxdata              =       1;
188         s->range_table  =       &range_digital;
189         s->insn_bits    =       ni_670x_dio_insn_bits;
190         s->insn_config  =       ni_670x_dio_insn_config;
191         
192         writel(0x10 ,dev->iobase + MISC_CONTROL_OFFSET);        /* Config of misc registers */  
193         writel(0x00 ,dev->iobase + AO_CONTROL_OFFSET);          /* Config of ao registers */
194         
195         printk("attached\n");
196
197         return 1;
198 }
199
200
201 static int ni_670x_detach(comedi_device *dev)
202 {
203         printk("comedi%d: ni_670x: remove\n",dev->minor);
204         
205         if(dev->subdevices[0].range_table_list){
206                 kfree(dev->subdevices[0].range_table_list);
207         }
208         if(dev->private && devpriv->mite)
209                 mite_unsetup(devpriv->mite);
210                 
211         if(dev->irq)
212                 comedi_free_irq(dev->irq,dev);
213         
214         return 0;
215 }
216
217
218
219
220 static int ni_670x_ao_winsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
221 {
222         int i;
223         int chan = CR_CHAN(insn->chanspec);
224
225         /* Channel number mapping :
226         
227         NI 6703/ NI 6704        | NI 6704 Only
228         ----------------------------------------------------
229         vch(0)  :       0       | ich(16)       :       1
230         vch(1)  :       2       | ich(17)       :       3
231           .     :       .       |   .                   .
232           .     :       .       |   .                   .
233           .     :       .       |   .                   .
234         vch(15) :       30      | ich(31)       :       31      */
235         
236         for(i=0;i<insn->n;i++){
237                 writel(((chan&15)<<1) | ((chan&16)>>4),
238                         dev->iobase + AO_CHAN_OFFSET);          /* First write in channel register which channel to use */
239                 writel(data[i],dev->iobase + AO_VALUE_OFFSET);  /* write channel value */
240                 devpriv->ao_readback[chan] = data[i];
241         }
242
243         return i;
244 }
245
246 static int ni_670x_ao_rinsn(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
247 {
248         int i;
249         int chan = CR_CHAN(insn->chanspec);
250
251         for(i=0;i<insn->n;i++)
252                 data[i] = devpriv->ao_readback[chan];
253
254         return i;
255 }
256
257 static int ni_670x_dio_insn_bits(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
258 {
259         if(insn->n!=2)return -EINVAL;
260
261         /* The insn data is a mask in data[0] and the new data
262          * in data[1], each channel cooresponding to a bit. */
263         if(data[0])
264         {
265                 s->state &= ~data[0];
266                 s->state |= data[0]&data[1];
267                 writel(s->state,dev->iobase + DIO_PORT0_DATA_OFFSET);
268         }
269         
270         /* on return, data[1] contains the value of the digital
271          * input lines. */
272         data[1]=readl(dev->iobase + DIO_PORT0_DATA_OFFSET);
273
274         return 2;
275 }
276
277 static int ni_670x_dio_insn_config(comedi_device *dev,comedi_subdevice *s,comedi_insn *insn,lsampl_t *data)
278 {
279         int chan=CR_CHAN(insn->chanspec);
280         
281
282         if(insn->n!=1) return -EINVAL;
283
284         if(data[0]==COMEDI_OUTPUT)
285         {
286                 s->io_bits |= 1<<chan;
287         }
288         else
289         {
290                 s->io_bits &= ~(1<<chan);
291         }
292         writel(s->io_bits,dev->iobase + DIO_PORT0_DIR_OFFSET);
293
294         return 1;
295 }
296
297 static int ni_670x_find_device(comedi_device *dev,int bus,int slot)
298 {
299         struct mite_struct *mite;
300         int i;
301
302         for(mite=mite_devices;mite;mite=mite->next)
303         {
304                 if(mite->used)continue;
305                 if(bus || slot)
306                 {
307                         if(bus!=mite->pcidev->bus->number || slot!=PCI_SLOT(mite->pcidev->devfn))
308                                 continue;
309                 }
310
311                 for(i=0;i<n_ni_670x_boards;i++)
312                 {
313                         if(mite_device_id(mite)==ni_670x_boards[i].dev_id)
314                         {
315                                 dev->board_ptr=ni_670x_boards+i;
316                                 devpriv->mite=mite;
317
318                                 return 0;
319                         }
320                 }
321         }
322         printk("no device found\n");
323         mite_list_devices();
324         return -EIO;
325 }
326