Staging: comedi: Remove comedi_subdevice typedef
[safe/jmp/linux-2.6] / drivers / staging / comedi / drivers / adl_pci6208.c
1 /*
2     comedi/drivers/adl_pci6208.c
3
4     Hardware driver for ADLink 6208 series cards:
5         card         | voltage output    | current output
6         -------------+-------------------+---------------
7         PCI-6208V    |  8 channels       | -
8         PCI-6216V    | 16 channels       | -
9         PCI-6208A    |  8 channels       | 8 channels
10
11     COMEDI - Linux Control and Measurement Device Interface
12     Copyright (C) 2000 David A. Schleef <ds@schleef.org>
13
14     This program is free software; you can redistribute it and/or modify
15     it under the terms of the GNU General Public License as published by
16     the Free Software Foundation; either version 2 of the License, or
17     (at your option) any later version.
18
19     This program is distributed in the hope that it will be useful,
20     but WITHOUT ANY WARRANTY; without even the implied warranty of
21     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22     GNU General Public License for more details.
23
24     You should have received a copy of the GNU General Public License
25     along with this program; if not, write to the Free Software
26     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 */
28 /*
29 Driver: adl_pci6208
30 Description: ADLink PCI-6208A
31 Devices: [ADLink] PCI-6208A (adl_pci6208)
32 Author: nsyeow <nsyeow@pd.jaring.my>
33 Updated: Fri, 30 Jan 2004 14:44:27 +0800
34 Status: untested
35
36 Configuration Options:
37   none
38
39 References:
40         - ni_660x.c
41         - adl_pci9111.c         copied the entire pci setup section
42         - adl_pci9118.c
43 */
44 /*
45  * These headers should be followed by a blank line, and any comments
46  * you wish to say about the driver.  The comment area is the place
47  * to put any known bugs, limitations, unsupported features, supported
48  * command triggers, whether or not commands are supported on particular
49  * subdevices, etc.
50  *
51  * Somewhere in the comment should be information about configuration
52  * options that are used with comedi_config.
53  */
54 #include "../comedidev.h"
55 #include "comedi_pci.h"
56
57 #define PCI6208_DRIVER_NAME     "adl_pci6208"
58
59 /* Board descriptions */
60 typedef struct {
61         const char *name;
62         unsigned short dev_id;  /* `lspci` will show you this */
63         int ao_chans;
64         //int ao_bits;
65 } pci6208_board;
66 static const pci6208_board pci6208_boards[] = {
67         /*{
68            name :  "pci6208v",
69            dev_id       :  0x6208,      //not sure
70            ao_chans:  8
71            //,  ao_bits :  16
72            },
73            {
74            name :  "pci6216v",
75            dev_id       :  0x6208,      //not sure
76            ao_chans:  16
77            //,  ao_bits :  16
78            }, */
79         {
80               name:     "pci6208a",
81               dev_id:   0x6208,
82               ao_chans:8
83                         //,     ao_bits :  16
84                 }
85 };
86
87 /* This is used by modprobe to translate PCI IDs to drivers.  Should
88  * only be used for PCI and ISA-PnP devices */
89 static DEFINE_PCI_DEVICE_TABLE(pci6208_pci_table) = {
90         //{ PCI_VENDOR_ID_ADLINK, 0x6208, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
91         //{ PCI_VENDOR_ID_ADLINK, 0x6208, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
92         {PCI_VENDOR_ID_ADLINK, 0x6208, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
93         {0}
94 };
95
96 MODULE_DEVICE_TABLE(pci, pci6208_pci_table);
97
98 /* Will be initialized in pci6208_find device(). */
99 #define thisboard ((const pci6208_board *)dev->board_ptr)
100
101 typedef struct {
102         int data;
103         struct pci_dev *pci_dev;        /* for a PCI device */
104         unsigned int ao_readback[2];    /* Used for AO readback */
105 } pci6208_private;
106
107 #define devpriv ((pci6208_private *)dev->private)
108
109 static int pci6208_attach(struct comedi_device * dev, comedi_devconfig * it);
110 static int pci6208_detach(struct comedi_device * dev);
111
112 #define pci6208_board_nbr \
113         (sizeof(pci6208_boards) / sizeof(pci6208_board))
114
115 static comedi_driver driver_pci6208 = {
116       driver_name:PCI6208_DRIVER_NAME,
117       module:THIS_MODULE,
118       attach:pci6208_attach,
119       detach:pci6208_detach,
120 };
121
122 COMEDI_PCI_INITCLEANUP(driver_pci6208, pci6208_pci_table);
123
124 static int pci6208_find_device(struct comedi_device * dev, int bus, int slot);
125 static int
126 pci6208_pci_setup(struct pci_dev *pci_dev, unsigned long *io_base_ptr,
127         int dev_minor);
128
129 /*read/write functions*/
130 static int pci6208_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
131         comedi_insn * insn, unsigned int * data);
132 static int pci6208_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
133         comedi_insn * insn, unsigned int * data);
134 //static int pci6208_dio_insn_bits(struct comedi_device *dev,struct comedi_subdevice *s,
135 //      comedi_insn *insn,unsigned int *data);
136 //static int pci6208_dio_insn_config(struct comedi_device *dev,struct comedi_subdevice *s,
137 //      comedi_insn *insn,unsigned int *data);
138
139 /*
140  * Attach is called by the Comedi core to configure the driver
141  * for a particular board.  If you specified a board_name array
142  * in the driver structure, dev->board_ptr contains that
143  * address.
144  */
145 static int pci6208_attach(struct comedi_device * dev, comedi_devconfig * it)
146 {
147         struct comedi_subdevice *s;
148         int retval;
149         unsigned long io_base;
150
151         printk("comedi%d: pci6208: ", dev->minor);
152
153         retval = alloc_private(dev, sizeof(pci6208_private));
154         if (retval < 0)
155                 return retval;
156
157         retval = pci6208_find_device(dev, it->options[0], it->options[1]);
158         if (retval < 0)
159                 return retval;
160
161         retval = pci6208_pci_setup(devpriv->pci_dev, &io_base, dev->minor);
162         if (retval < 0)
163                 return retval;
164
165         dev->iobase = io_base;
166         dev->board_name = thisboard->name;
167
168 /*
169  * Allocate the subdevice structures.  alloc_subdevice() is a
170  * convenient macro defined in comedidev.h.
171  */
172         if (alloc_subdevices(dev, 2) < 0)
173                 return -ENOMEM;
174
175         s = dev->subdevices + 0;
176         /* analog output subdevice */
177         s->type = COMEDI_SUBD_AO;
178         s->subdev_flags = SDF_WRITABLE; //anything else to add here??
179         s->n_chan = thisboard->ao_chans;
180         s->maxdata = 0xffff;    //16-bit DAC
181         s->range_table = &range_bipolar10;      //this needs to be checked.
182         s->insn_write = pci6208_ao_winsn;
183         s->insn_read = pci6208_ao_rinsn;
184
185         //s=dev->subdevices+1;
186         /* digital i/o subdevice */
187         //s->type=COMEDI_SUBD_DIO;
188         //s->subdev_flags=SDF_READABLE|SDF_WRITABLE;
189         //s->n_chan=16;
190         //s->maxdata=1;
191         //s->range_table=&range_digital;
192         //s->insn_bits = pci6208_dio_insn_bits;
193         //s->insn_config = pci6208_dio_insn_config;
194
195         printk("attached\n");
196
197         return 1;
198 }
199
200 /*
201  * _detach is called to deconfigure a device.  It should deallocate
202  * resources.
203  * This function is also called when _attach() fails, so it should be
204  * careful not to release resources that were not necessarily
205  * allocated by _attach().  dev->private and dev->subdevices are
206  * deallocated automatically by the core.
207  */
208 static int pci6208_detach(struct comedi_device * dev)
209 {
210         printk("comedi%d: pci6208: remove\n", dev->minor);
211
212         if (devpriv && devpriv->pci_dev) {
213                 if (dev->iobase) {
214                         comedi_pci_disable(devpriv->pci_dev);
215                 }
216                 pci_dev_put(devpriv->pci_dev);
217         }
218
219         return 0;
220 }
221
222 static int pci6208_ao_winsn(struct comedi_device * dev, struct comedi_subdevice * s,
223         comedi_insn * insn, unsigned int * data)
224 {
225         int i = 0, Data_Read;
226         unsigned short chan = CR_CHAN(insn->chanspec);
227         unsigned long invert = 1 << (16 - 1);
228         unsigned long out_value;
229         /* Writing a list of values to an AO channel is probably not
230          * very useful, but that's how the interface is defined. */
231         for (i = 0; i < insn->n; i++) {
232                 out_value = data[i] ^ invert;
233                 /* a typical programming sequence */
234                 do {
235                         Data_Read = (inw(dev->iobase) & 1);
236                 } while (Data_Read);
237                 outw(out_value, dev->iobase + (0x02 * chan));
238                 devpriv->ao_readback[chan] = out_value;
239         }
240
241         /* return the number of samples read/written */
242         return i;
243 }
244
245 /* AO subdevices should have a read insn as well as a write insn.
246  * Usually this means copying a value stored in devpriv. */
247 static int pci6208_ao_rinsn(struct comedi_device * dev, struct comedi_subdevice * s,
248         comedi_insn * insn, unsigned int * data)
249 {
250         int i;
251         int chan = CR_CHAN(insn->chanspec);
252
253         for (i = 0; i < insn->n; i++)
254                 data[i] = devpriv->ao_readback[chan];
255
256         return i;
257 }
258
259 /* DIO devices are slightly special.  Although it is possible to
260  * implement the insn_read/insn_write interface, it is much more
261  * useful to applications if you implement the insn_bits interface.
262  * This allows packed reading/writing of the DIO channels.  The
263  * comedi core can convert between insn_bits and insn_read/write */
264 //static int pci6208_dio_insn_bits(struct comedi_device *dev,struct comedi_subdevice *s,
265 //      comedi_insn *insn,unsigned int *data)
266 //{
267 //      if(insn->n!=2)return -EINVAL;
268
269         /* The insn data is a mask in data[0] and the new data
270          * in data[1], each channel cooresponding to a bit. */
271 //      if(data[0]){
272 //              s->state &= ~data[0];
273 //              s->state |= data[0]&data[1];
274                 /* Write out the new digital output lines */
275                 //outw(s->state,dev->iobase + SKEL_DIO);
276 //      }
277
278         /* on return, data[1] contains the value of the digital
279          * input and output lines. */
280         //data[1]=inw(dev->iobase + SKEL_DIO);
281         /* or we could just return the software copy of the output values if
282          * it was a purely digital output subdevice */
283         //data[1]=s->state;
284
285 //      return 2;
286 //}
287
288 //static int pci6208_dio_insn_config(struct comedi_device *dev,struct comedi_subdevice *s,
289 //      comedi_insn *insn,unsigned int *data)
290 //{
291 //      int chan=CR_CHAN(insn->chanspec);
292
293         /* The input or output configuration of each digital line is
294          * configured by a special insn_config instruction.  chanspec
295          * contains the channel to be changed, and data[0] contains the
296          * value COMEDI_INPUT or COMEDI_OUTPUT. */
297
298 //      if(data[0]==COMEDI_OUTPUT){
299 //              s->io_bits |= 1<<chan;
300 //      }else{
301 //              s->io_bits &= ~(1<<chan);
302 //      }
303         //outw(s->io_bits,dev->iobase + SKEL_DIO_CONFIG);
304
305 //      return 1;
306 //}
307
308 static int pci6208_find_device(struct comedi_device * dev, int bus, int slot)
309 {
310         struct pci_dev *pci_dev;
311         int i;
312
313         for (pci_dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
314                 pci_dev != NULL;
315                 pci_dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev)) {
316                 if (pci_dev->vendor == PCI_VENDOR_ID_ADLINK) {
317                         for (i = 0; i < pci6208_board_nbr; i++) {
318                                 if (pci6208_boards[i].dev_id == pci_dev->device) {
319                                         // was a particular bus/slot requested?
320                                         if ((bus != 0) || (slot != 0)) {
321                                                 // are we on the wrong bus/slot?
322                                                 if (pci_dev->bus->number
323                                                         != bus ||
324                                                         PCI_SLOT(pci_dev->devfn)
325                                                         != slot) {
326                                                         continue;
327                                                 }
328                                         }
329                                         dev->board_ptr = pci6208_boards + i;
330                                         goto found;
331                                 }
332                         }
333                 }
334         }
335
336         printk("comedi%d: no supported board found! (req. bus/slot : %d/%d)\n",
337                 dev->minor, bus, slot);
338         return -EIO;
339
340       found:
341         printk("comedi%d: found %s (b:s:f=%d:%d:%d) , irq=%d\n",
342                 dev->minor,
343                 pci6208_boards[i].name,
344                 pci_dev->bus->number,
345                 PCI_SLOT(pci_dev->devfn),
346                 PCI_FUNC(pci_dev->devfn), pci_dev->irq);
347
348         // TODO: Warn about non-tested boards.
349         //switch(board->device_id)
350         //{
351         //};
352
353         devpriv->pci_dev = pci_dev;
354
355         return 0;
356 }
357
358 static int
359 pci6208_pci_setup(struct pci_dev *pci_dev, unsigned long *io_base_ptr,
360         int dev_minor)
361 {
362         unsigned long io_base, io_range, lcr_io_base, lcr_io_range;
363
364         // Enable PCI device and request regions
365         if (comedi_pci_enable(pci_dev, PCI6208_DRIVER_NAME) < 0) {
366                 printk("comedi%d: Failed to enable PCI device and request regions\n", dev_minor);
367                 return -EIO;
368         }
369         // Read local configuration register base address [PCI_BASE_ADDRESS #1].
370         lcr_io_base = pci_resource_start(pci_dev, 1);
371         lcr_io_range = pci_resource_len(pci_dev, 1);
372
373         printk("comedi%d: local config registers at address 0x%4lx [0x%4lx]\n",
374                 dev_minor, lcr_io_base, lcr_io_range);
375
376         // Read PCI6208 register base address [PCI_BASE_ADDRESS #2].
377         io_base = pci_resource_start(pci_dev, 2);
378         io_range = pci_resource_end(pci_dev, 2) - io_base + 1;
379
380         printk("comedi%d: 6208 registers at address 0x%4lx [0x%4lx]\n",
381                 dev_minor, io_base, io_range);
382
383         *io_base_ptr = io_base;
384         //devpriv->io_range = io_range;
385         //devpriv->is_valid=0;
386         //devpriv->lcr_io_base=lcr_io_base;
387         //devpriv->lcr_io_range=lcr_io_range;
388
389         return 0;
390 }