staging: comedi: propogate error code from comedi_alloc_subdevices
[deliverable/linux.git] / drivers / staging / comedi / drivers / adl_pci8164.c
1 /*
2 comedi/drivers/adl_pci8164.c
3
4 Hardware comedi driver fot PCI-8164 Adlink card
5 Copyright (C) 2004 Michel Lachine <mike@mikelachaine.ca>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 */
22 /*
23 Driver: adl_pci8164
24 Description: Driver for the Adlink PCI-8164 4 Axes Motion Control board
25 Devices: [ADLink] PCI-8164 (adl_pci8164)
26 Author: Michel Lachaine <mike@mikelachaine.ca>
27 Status: experimental
28 Updated: Mon, 14 Apr 2008 15:10:32 +0100
29
30 Configuration Options:
31 [0] - PCI bus of device (optional)
32 [1] - PCI slot of device (optional)
33 If bus/slot is not specified, the first supported
34 PCI device found will be used.
35 */
36
37 #include "../comedidev.h"
38 #include <linux/kernel.h>
39 #include <linux/delay.h>
40 #include "comedi_fc.h"
41 #include "8253.h"
42
43 #define PCI8164_AXIS_X 0x00
44 #define PCI8164_AXIS_Y 0x08
45 #define PCI8164_AXIS_Z 0x10
46 #define PCI8164_AXIS_U 0x18
47
48 #define PCI8164_MSTS 0x00
49 #define PCI8164_SSTS 0x02
50 #define PCI8164_BUF0 0x04
51 #define PCI8164_BUF1 0x06
52
53 #define PCI8164_CMD 0x00
54 #define PCI8164_OTP 0x02
55
56 #define PCI_DEVICE_ID_PCI8164 0x8164
57
58 struct adl_pci8164_private {
59 int data;
60 struct pci_dev *pci_dev;
61 };
62
63 #define devpriv ((struct adl_pci8164_private *)dev->private)
64
65 /*
66 all the read commands are the same except for the addition a constant
67 * const to the data for inw()
68 */
69 static void adl_pci8164_insn_read(struct comedi_device *dev,
70 struct comedi_subdevice *s,
71 struct comedi_insn *insn,
72 unsigned int *data,
73 char *action, unsigned short offset)
74 {
75 int axis, axis_reg;
76 char *axisname;
77
78 axis = CR_CHAN(insn->chanspec);
79
80 switch (axis) {
81 case 0:
82 axis_reg = PCI8164_AXIS_X;
83 axisname = "X";
84 break;
85 case 1:
86 axis_reg = PCI8164_AXIS_Y;
87 axisname = "Y";
88 break;
89 case 2:
90 axis_reg = PCI8164_AXIS_Z;
91 axisname = "Z";
92 break;
93 case 3:
94 axis_reg = PCI8164_AXIS_U;
95 axisname = "U";
96 break;
97 default:
98 axis_reg = PCI8164_AXIS_X;
99 axisname = "X";
100 }
101
102 data[0] = inw(dev->iobase + axis_reg + offset);
103 printk(KERN_DEBUG "comedi: pci8164 %s read -> "
104 "%04X:%04X on axis %s\n",
105 action, data[0], data[1], axisname);
106 }
107
108 static int adl_pci8164_insn_read_msts(struct comedi_device *dev,
109 struct comedi_subdevice *s,
110 struct comedi_insn *insn,
111 unsigned int *data)
112 {
113 adl_pci8164_insn_read(dev, s, insn, data, "MSTS", PCI8164_MSTS);
114 return 2;
115 }
116
117 static int adl_pci8164_insn_read_ssts(struct comedi_device *dev,
118 struct comedi_subdevice *s,
119 struct comedi_insn *insn,
120 unsigned int *data)
121 {
122 adl_pci8164_insn_read(dev, s, insn, data, "SSTS", PCI8164_SSTS);
123 return 2;
124 }
125
126 static int adl_pci8164_insn_read_buf0(struct comedi_device *dev,
127 struct comedi_subdevice *s,
128 struct comedi_insn *insn,
129 unsigned int *data)
130 {
131 adl_pci8164_insn_read(dev, s, insn, data, "BUF0", PCI8164_BUF0);
132 return 2;
133 }
134
135 static int adl_pci8164_insn_read_buf1(struct comedi_device *dev,
136 struct comedi_subdevice *s,
137 struct comedi_insn *insn,
138 unsigned int *data)
139 {
140 adl_pci8164_insn_read(dev, s, insn, data, "BUF1", PCI8164_BUF1);
141 return 2;
142 }
143
144 /*
145 all the write commands are the same except for the addition a constant
146 * const to the data for outw()
147 */
148 static void adl_pci8164_insn_out(struct comedi_device *dev,
149 struct comedi_subdevice *s,
150 struct comedi_insn *insn,
151 unsigned int *data,
152 char *action, unsigned short offset)
153 {
154 unsigned int axis, axis_reg;
155
156 char *axisname;
157
158 axis = CR_CHAN(insn->chanspec);
159
160 switch (axis) {
161 case 0:
162 axis_reg = PCI8164_AXIS_X;
163 axisname = "X";
164 break;
165 case 1:
166 axis_reg = PCI8164_AXIS_Y;
167 axisname = "Y";
168 break;
169 case 2:
170 axis_reg = PCI8164_AXIS_Z;
171 axisname = "Z";
172 break;
173 case 3:
174 axis_reg = PCI8164_AXIS_U;
175 axisname = "U";
176 break;
177 default:
178 axis_reg = PCI8164_AXIS_X;
179 axisname = "X";
180 }
181
182 outw(data[0], dev->iobase + axis_reg + offset);
183
184 printk(KERN_DEBUG "comedi: pci8164 %s write -> "
185 "%04X:%04X on axis %s\n",
186 action, data[0], data[1], axisname);
187
188 }
189
190 static int adl_pci8164_insn_write_cmd(struct comedi_device *dev,
191 struct comedi_subdevice *s,
192 struct comedi_insn *insn,
193 unsigned int *data)
194 {
195 adl_pci8164_insn_out(dev, s, insn, data, "CMD", PCI8164_CMD);
196 return 2;
197 }
198
199 static int adl_pci8164_insn_write_otp(struct comedi_device *dev,
200 struct comedi_subdevice *s,
201 struct comedi_insn *insn,
202 unsigned int *data)
203 {
204 adl_pci8164_insn_out(dev, s, insn, data, "OTP", PCI8164_OTP);
205 return 2;
206 }
207
208 static int adl_pci8164_insn_write_buf0(struct comedi_device *dev,
209 struct comedi_subdevice *s,
210 struct comedi_insn *insn,
211 unsigned int *data)
212 {
213 adl_pci8164_insn_out(dev, s, insn, data, "BUF0", PCI8164_BUF0);
214 return 2;
215 }
216
217 static int adl_pci8164_insn_write_buf1(struct comedi_device *dev,
218 struct comedi_subdevice *s,
219 struct comedi_insn *insn,
220 unsigned int *data)
221 {
222 adl_pci8164_insn_out(dev, s, insn, data, "BUF1", PCI8164_BUF1);
223 return 2;
224 }
225
226 static struct pci_dev *adl_pci8164_find_pci(struct comedi_device *dev,
227 struct comedi_devconfig *it)
228 {
229 struct pci_dev *pcidev = NULL;
230 int bus = it->options[0];
231 int slot = it->options[1];
232
233 for_each_pci_dev(pcidev) {
234 if (pcidev->vendor != PCI_VENDOR_ID_ADLINK ||
235 pcidev->device != PCI_DEVICE_ID_PCI8164)
236 continue;
237 if (bus || slot) {
238 /* requested particular bus/slot */
239 if (pcidev->bus->number != bus ||
240 PCI_SLOT(pcidev->devfn) != slot)
241 continue;
242 }
243 return pcidev;
244 }
245 printk(KERN_ERR
246 "comedi%d: no supported board found! (req. bus/slot : %d/%d)\n",
247 dev->minor, bus, slot);
248 return NULL;
249 }
250
251 static int adl_pci8164_attach(struct comedi_device *dev,
252 struct comedi_devconfig *it)
253 {
254 struct comedi_subdevice *s;
255 int ret;
256
257 printk(KERN_INFO "comedi: attempt to attach...\n");
258 printk(KERN_INFO "comedi%d: adl_pci8164\n", dev->minor);
259
260 dev->board_name = "pci8164";
261
262 if (alloc_private(dev, sizeof(struct adl_pci8164_private)) < 0)
263 return -ENOMEM;
264
265 ret = comedi_alloc_subdevices(dev, 4);
266 if (ret)
267 return ret;
268
269 devpriv->pci_dev = adl_pci8164_find_pci(dev, it);
270 if (!devpriv->pci_dev)
271 return -EIO;
272
273 if (comedi_pci_enable(devpriv->pci_dev, "adl_pci8164") < 0) {
274 printk(KERN_ERR "comedi%d: Failed to enable "
275 "PCI device and request regions\n", dev->minor);
276 return -EIO;
277 }
278 dev->iobase = pci_resource_start(devpriv->pci_dev, 2);
279 printk(KERN_DEBUG "comedi: base addr %4lx\n", dev->iobase);
280
281 s = dev->subdevices + 0;
282 s->type = COMEDI_SUBD_PROC;
283 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
284 s->n_chan = 4;
285 s->maxdata = 0xffff;
286 s->len_chanlist = 4;
287 /* s->range_table = &range_axis; */
288 s->insn_read = adl_pci8164_insn_read_msts;
289 s->insn_write = adl_pci8164_insn_write_cmd;
290
291 s = dev->subdevices + 1;
292 s->type = COMEDI_SUBD_PROC;
293 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
294 s->n_chan = 4;
295 s->maxdata = 0xffff;
296 s->len_chanlist = 4;
297 /* s->range_table = &range_axis; */
298 s->insn_read = adl_pci8164_insn_read_ssts;
299 s->insn_write = adl_pci8164_insn_write_otp;
300
301 s = dev->subdevices + 2;
302 s->type = COMEDI_SUBD_PROC;
303 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
304 s->n_chan = 4;
305 s->maxdata = 0xffff;
306 s->len_chanlist = 4;
307 /* s->range_table = &range_axis; */
308 s->insn_read = adl_pci8164_insn_read_buf0;
309 s->insn_write = adl_pci8164_insn_write_buf0;
310
311 s = dev->subdevices + 3;
312 s->type = COMEDI_SUBD_PROC;
313 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
314 s->n_chan = 4;
315 s->maxdata = 0xffff;
316 s->len_chanlist = 4;
317 /* s->range_table = &range_axis; */
318 s->insn_read = adl_pci8164_insn_read_buf1;
319 s->insn_write = adl_pci8164_insn_write_buf1;
320
321 printk(KERN_INFO "comedi: attached\n");
322 return 0;
323 }
324
325 static void adl_pci8164_detach(struct comedi_device *dev)
326 {
327 if (devpriv && devpriv->pci_dev) {
328 if (dev->iobase)
329 comedi_pci_disable(devpriv->pci_dev);
330 pci_dev_put(devpriv->pci_dev);
331 }
332 }
333
334 static struct comedi_driver adl_pci8164_driver = {
335 .driver_name = "adl_pci8164",
336 .module = THIS_MODULE,
337 .attach = adl_pci8164_attach,
338 .detach = adl_pci8164_detach,
339 };
340
341 static int __devinit adl_pci8164_pci_probe(struct pci_dev *dev,
342 const struct pci_device_id *ent)
343 {
344 return comedi_pci_auto_config(dev, &adl_pci8164_driver);
345 }
346
347 static void __devexit adl_pci8164_pci_remove(struct pci_dev *dev)
348 {
349 comedi_pci_auto_unconfig(dev);
350 }
351
352 static DEFINE_PCI_DEVICE_TABLE(adl_pci8164_pci_table) = {
353 { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI8164) },
354 {0}
355 };
356 MODULE_DEVICE_TABLE(pci, adl_pci8164_pci_table);
357
358 static struct pci_driver adl_pci8164_pci_driver = {
359 .name = "adl_pci8164",
360 .id_table = adl_pci8164_pci_table,
361 .probe = adl_pci8164_pci_probe,
362 .remove = __devexit_p(adl_pci8164_pci_remove),
363 };
364 module_comedi_pci_driver(adl_pci8164_driver, adl_pci8164_pci_driver);
365
366 MODULE_AUTHOR("Comedi http://www.comedi.org");
367 MODULE_DESCRIPTION("Comedi low-level driver");
368 MODULE_LICENSE("GPL");
This page took 0.043759 seconds and 5 git commands to generate.