staging: comedi: pcmuio: fix possible NULL deref on detach
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / staging / comedi / drivers / c6xdigio.c
1 /*
2 comedi/drivers/c6xdigio.c
3
4 Hardware driver for Mechatronic Systems Inc. C6x_DIGIO DSP daughter card.
5 (http://robot0.ge.uiuc.edu/~spong/mecha/)
6
7 COMEDI - Linux Control and Measurement Device Interface
8 Copyright (C) 1999 Dan Block
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23
24 */
25 /*
26 Driver: c6xdigio
27 Description: Mechatronic Systems Inc. C6x_DIGIO DSP daughter card
28 Author: Dan Block
29 Status: unknown
30 Devices: [Mechatronic Systems Inc.] C6x_DIGIO DSP daughter card (c6xdigio)
31 Updated: Sun Nov 20 20:18:34 EST 2005
32
33 This driver will not work with a 2.4 kernel.
34 http://robot0.ge.uiuc.edu/~spong/mecha/
35
36 */
37
38 #include <linux/kernel.h>
39 #include <linux/module.h>
40 #include <linux/sched.h>
41 #include <linux/mm.h>
42 #include <linux/errno.h>
43 #include <linux/ioport.h>
44 #include <linux/delay.h>
45 #include <linux/interrupt.h>
46 #include <linux/timex.h>
47 #include <linux/timer.h>
48 #include <linux/io.h>
49 #include <linux/pnp.h>
50
51 #include "../comedidev.h"
52
53 static u8 ReadByteFromHwPort(unsigned long addr)
54 {
55 u8 result = inb(addr);
56 return result;
57 }
58
59 static void WriteByteToHwPort(unsigned long addr, u8 val)
60 {
61 outb_p(val, addr);
62 }
63
64 #define C6XDIGIO_SIZE 3
65
66 /*
67 * port offsets
68 */
69 #define C6XDIGIO_PARALLEL_DATA 0
70 #define C6XDIGIO_PARALLEL_STATUS 1
71 #define C6XDIGIO_PARALLEL_CONTROL 2
72 struct pwmbitstype {
73 unsigned sb0:2;
74 unsigned sb1:2;
75 unsigned sb2:2;
76 unsigned sb3:2;
77 unsigned sb4:2;
78 };
79 union pwmcmdtype {
80 unsigned cmd; /* assuming here that int is 32bit */
81 struct pwmbitstype bits;
82 };
83 struct encbitstype {
84 unsigned sb0:3;
85 unsigned sb1:3;
86 unsigned sb2:3;
87 unsigned sb3:3;
88 unsigned sb4:3;
89 unsigned sb5:3;
90 unsigned sb6:3;
91 unsigned sb7:3;
92 };
93 union encvaluetype {
94 unsigned value;
95 struct encbitstype bits;
96 };
97
98 #define C6XDIGIO_TIME_OUT 20
99
100 static void C6X_pwmInit(unsigned long baseAddr)
101 {
102 int timeout = 0;
103
104 /* printk("Inside C6X_pwmInit\n"); */
105
106 WriteByteToHwPort(baseAddr, 0x70);
107 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
108 && (timeout < C6XDIGIO_TIME_OUT)) {
109 timeout++;
110 }
111
112 WriteByteToHwPort(baseAddr, 0x74);
113 timeout = 0;
114 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
115 && (timeout < C6XDIGIO_TIME_OUT)) {
116 timeout++;
117 }
118
119 WriteByteToHwPort(baseAddr, 0x70);
120 timeout = 0;
121 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
122 && (timeout < C6XDIGIO_TIME_OUT)) {
123 timeout++;
124 }
125
126 WriteByteToHwPort(baseAddr, 0x0);
127 timeout = 0;
128 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
129 && (timeout < C6XDIGIO_TIME_OUT)) {
130 timeout++;
131 }
132
133 }
134
135 static void C6X_pwmOutput(unsigned long baseAddr, unsigned channel, int value)
136 {
137 unsigned ppcmd;
138 union pwmcmdtype pwm;
139 int timeout = 0;
140 unsigned tmp;
141
142 /* printk("Inside C6X_pwmOutput\n"); */
143
144 pwm.cmd = value;
145 if (pwm.cmd > 498)
146 pwm.cmd = 498;
147 if (pwm.cmd < 2)
148 pwm.cmd = 2;
149
150 if (channel == 0) {
151 ppcmd = 0x28;
152 } else { /* if channel == 1 */
153 ppcmd = 0x30;
154 } /* endif */
155
156 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb0);
157 tmp = ReadByteFromHwPort(baseAddr + 1);
158 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
159 tmp = ReadByteFromHwPort(baseAddr + 1);
160 timeout++;
161 }
162
163 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb1 + 0x4);
164 timeout = 0;
165 tmp = ReadByteFromHwPort(baseAddr + 1);
166 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
167 tmp = ReadByteFromHwPort(baseAddr + 1);
168 timeout++;
169 }
170
171 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb2);
172 tmp = ReadByteFromHwPort(baseAddr + 1);
173 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
174 tmp = ReadByteFromHwPort(baseAddr + 1);
175 timeout++;
176 }
177
178 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb3 + 0x4);
179 timeout = 0;
180 tmp = ReadByteFromHwPort(baseAddr + 1);
181 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
182 tmp = ReadByteFromHwPort(baseAddr + 1);
183 timeout++;
184 }
185
186 WriteByteToHwPort(baseAddr, ppcmd + pwm.bits.sb4);
187 tmp = ReadByteFromHwPort(baseAddr + 1);
188 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
189 tmp = ReadByteFromHwPort(baseAddr + 1);
190 timeout++;
191 }
192
193 WriteByteToHwPort(baseAddr, 0x0);
194 timeout = 0;
195 tmp = ReadByteFromHwPort(baseAddr + 1);
196 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
197 tmp = ReadByteFromHwPort(baseAddr + 1);
198 timeout++;
199 }
200
201 }
202
203 static int C6X_encInput(unsigned long baseAddr, unsigned channel)
204 {
205 unsigned ppcmd;
206 union encvaluetype enc;
207 int timeout = 0;
208 int tmp;
209
210 /* printk("Inside C6X_encInput\n"); */
211
212 enc.value = 0;
213 if (channel == 0)
214 ppcmd = 0x48;
215 else
216 ppcmd = 0x50;
217
218 WriteByteToHwPort(baseAddr, ppcmd);
219 tmp = ReadByteFromHwPort(baseAddr + 1);
220 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
221 tmp = ReadByteFromHwPort(baseAddr + 1);
222 timeout++;
223 }
224
225 enc.bits.sb0 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
226 WriteByteToHwPort(baseAddr, ppcmd + 0x4);
227 timeout = 0;
228 tmp = ReadByteFromHwPort(baseAddr + 1);
229 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
230 tmp = ReadByteFromHwPort(baseAddr + 1);
231 timeout++;
232 }
233 enc.bits.sb1 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
234 WriteByteToHwPort(baseAddr, ppcmd);
235 timeout = 0;
236 tmp = ReadByteFromHwPort(baseAddr + 1);
237 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
238 tmp = ReadByteFromHwPort(baseAddr + 1);
239 timeout++;
240 }
241 enc.bits.sb2 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
242 WriteByteToHwPort(baseAddr, ppcmd + 0x4);
243 timeout = 0;
244 tmp = ReadByteFromHwPort(baseAddr + 1);
245 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
246 tmp = ReadByteFromHwPort(baseAddr + 1);
247 timeout++;
248 }
249 enc.bits.sb3 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
250 WriteByteToHwPort(baseAddr, ppcmd);
251 timeout = 0;
252 tmp = ReadByteFromHwPort(baseAddr + 1);
253 while (((tmp & 0x80) == 0) && (timeout < C6XDIGIO_TIME_OUT)) {
254 tmp = ReadByteFromHwPort(baseAddr + 1);
255 timeout++;
256 }
257 enc.bits.sb4 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
258 WriteByteToHwPort(baseAddr, ppcmd + 0x4);
259 timeout = 0;
260 tmp = ReadByteFromHwPort(baseAddr + 1);
261 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
262 tmp = ReadByteFromHwPort(baseAddr + 1);
263 timeout++;
264 }
265 enc.bits.sb5 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
266 WriteByteToHwPort(baseAddr, ppcmd);
267 timeout = 0;
268 tmp = ReadByteFromHwPort(baseAddr + 1);
269 while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
270 tmp = ReadByteFromHwPort(baseAddr + 1);
271 timeout++;
272 }
273 enc.bits.sb6 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
274 WriteByteToHwPort(baseAddr, ppcmd + 0x4);
275 timeout = 0;
276 tmp = ReadByteFromHwPort(baseAddr + 1);
277 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
278 tmp = ReadByteFromHwPort(baseAddr + 1);
279 timeout++;
280 }
281 enc.bits.sb7 = ((ReadByteFromHwPort(baseAddr + 1) >> 3) & 0x7);
282 WriteByteToHwPort(baseAddr, ppcmd);
283 timeout = 0;
284 tmp = ReadByteFromHwPort(baseAddr + 1);
285 while (((tmp & 0x80) == 0x0) && (timeout < C6XDIGIO_TIME_OUT)) {
286 tmp = ReadByteFromHwPort(baseAddr + 1);
287 timeout++;
288 }
289
290 WriteByteToHwPort(baseAddr, 0x0);
291 timeout = 0;
292 tmp = ReadByteFromHwPort(baseAddr + 1);
293 while (((tmp & 0x80) == 0x80) && (timeout < C6XDIGIO_TIME_OUT)) {
294 tmp = ReadByteFromHwPort(baseAddr + 1);
295 timeout++;
296 }
297
298 return enc.value ^ 0x800000;
299 }
300
301 static void C6X_encResetAll(unsigned long baseAddr)
302 {
303 unsigned timeout = 0;
304
305 /* printk("Inside C6X_encResetAll\n"); */
306
307 WriteByteToHwPort(baseAddr, 0x68);
308 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0)
309 && (timeout < C6XDIGIO_TIME_OUT)) {
310 timeout++;
311 }
312 WriteByteToHwPort(baseAddr, 0x6C);
313 timeout = 0;
314 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
315 && (timeout < C6XDIGIO_TIME_OUT)) {
316 timeout++;
317 }
318 WriteByteToHwPort(baseAddr, 0x68);
319 timeout = 0;
320 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x0)
321 && (timeout < C6XDIGIO_TIME_OUT)) {
322 timeout++;
323 }
324 WriteByteToHwPort(baseAddr, 0x0);
325 timeout = 0;
326 while (((ReadByteFromHwPort(baseAddr + 1) & 0x80) == 0x80)
327 && (timeout < C6XDIGIO_TIME_OUT)) {
328 timeout++;
329 }
330 }
331
332 static int c6xdigio_pwmo_insn_read(struct comedi_device *dev,
333 struct comedi_subdevice *s,
334 struct comedi_insn *insn, unsigned int *data)
335 {
336 printk(KERN_DEBUG "c6xdigio_pwmo_insn_read %x\n", insn->n);
337 return insn->n;
338 }
339
340 static int c6xdigio_pwmo_insn_write(struct comedi_device *dev,
341 struct comedi_subdevice *s,
342 struct comedi_insn *insn,
343 unsigned int *data)
344 {
345 int i;
346 int chan = CR_CHAN(insn->chanspec);
347
348 /* printk("c6xdigio_pwmo_insn_write %x\n", insn->n); */
349 for (i = 0; i < insn->n; i++) {
350 C6X_pwmOutput(dev->iobase, chan, data[i]);
351 /* devpriv->ao_readback[chan] = data[i]; */
352 }
353 return i;
354 }
355
356 /* static int c6xdigio_ei_init_insn_read(struct comedi_device *dev, */
357 /* struct comedi_subdevice *s, */
358 /* struct comedi_insn *insn, */
359 /* unsigned int *data) */
360 /* { */
361 /* printk("c6xdigio_ei_init_insn_read %x\n", insn->n); */
362 /* return insn->n; */
363 /* } */
364
365 /* static int c6xdigio_ei_init_insn_write(struct comedi_device *dev, */
366 /* struct comedi_subdevice *s, */
367 /* struct comedi_insn *insn, */
368 /* unsigned int *data) */
369 /* { */
370 /* int i; */
371 /* int chan = CR_CHAN(insn->chanspec); */
372 /* *//* C6X_encResetAll( dev->iobase ); */
373 /* *//* return insn->n; */
374 /* } */
375
376 static int c6xdigio_ei_insn_read(struct comedi_device *dev,
377 struct comedi_subdevice *s,
378 struct comedi_insn *insn, unsigned int *data)
379 {
380 /* printk("c6xdigio_ei__insn_read %x\n", insn->n); */
381 int n;
382 int chan = CR_CHAN(insn->chanspec);
383
384 for (n = 0; n < insn->n; n++)
385 data[n] = (C6X_encInput(dev->iobase, chan) & 0xffffff);
386
387 return n;
388 }
389
390 static void board_init(struct comedi_device *dev)
391 {
392
393 /* printk("Inside board_init\n"); */
394
395 C6X_pwmInit(dev->iobase);
396 C6X_encResetAll(dev->iobase);
397
398 }
399
400 static const struct pnp_device_id c6xdigio_pnp_tbl[] = {
401 /* Standard LPT Printer Port */
402 {.id = "PNP0400", .driver_data = 0},
403 /* ECP Printer Port */
404 {.id = "PNP0401", .driver_data = 0},
405 {}
406 };
407
408 static struct pnp_driver c6xdigio_pnp_driver = {
409 .name = "c6xdigio",
410 .id_table = c6xdigio_pnp_tbl,
411 };
412
413 static int c6xdigio_attach(struct comedi_device *dev,
414 struct comedi_devconfig *it)
415 {
416 struct comedi_subdevice *s;
417 int ret;
418
419 ret = comedi_request_region(dev, it->options[0], C6XDIGIO_SIZE);
420 if (ret)
421 return ret;
422
423 ret = comedi_alloc_subdevices(dev, 2);
424 if (ret)
425 return ret;
426
427 /* Make sure that PnP ports get activated */
428 pnp_register_driver(&c6xdigio_pnp_driver);
429
430 s = &dev->subdevices[0];
431 /* pwm output subdevice */
432 s->type = COMEDI_SUBD_AO; /* Not sure what to put here */
433 s->subdev_flags = SDF_WRITEABLE;
434 s->n_chan = 2;
435 /* s->trig[0] = c6xdigio_pwmo; */
436 s->insn_read = c6xdigio_pwmo_insn_read;
437 s->insn_write = c6xdigio_pwmo_insn_write;
438 s->maxdata = 500;
439 s->range_table = &range_bipolar10; /* A suitable lie */
440
441 s = &dev->subdevices[1];
442 /* encoder (counter) subdevice */
443 s->type = COMEDI_SUBD_COUNTER;
444 s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
445 s->n_chan = 2;
446 /* s->trig[0] = c6xdigio_ei; */
447 s->insn_read = c6xdigio_ei_insn_read;
448 s->maxdata = 0xffffff;
449 s->range_table = &range_unknown;
450
451 /* s = &dev->subdevices[2]; */
452 /* pwm output subdevice */
453 /* s->type = COMEDI_SUBD_COUNTER; // Not sure what to put here */
454 /* s->subdev_flags = SDF_WRITEABLE; */
455 /* s->n_chan = 1; */
456 /* s->trig[0] = c6xdigio_ei_init; */
457 /* s->insn_read = c6xdigio_ei_init_insn_read; */
458 /* s->insn_write = c6xdigio_ei_init_insn_write; */
459 /* s->maxdata = 0xFFFF; // Really just a don't care */
460 /* s->range_table = &range_unknown; // Not sure what to put here */
461
462 /* I will call this init anyway but more than likely the DSP board */
463 /* will not be connected when device driver is loaded. */
464 board_init(dev);
465
466 return 0;
467 }
468
469 static void c6xdigio_detach(struct comedi_device *dev)
470 {
471 comedi_legacy_detach(dev);
472 pnp_unregister_driver(&c6xdigio_pnp_driver);
473 }
474
475 static struct comedi_driver c6xdigio_driver = {
476 .driver_name = "c6xdigio",
477 .module = THIS_MODULE,
478 .attach = c6xdigio_attach,
479 .detach = c6xdigio_detach,
480 };
481 module_comedi_driver(c6xdigio_driver);
482
483 MODULE_AUTHOR("Comedi http://www.comedi.org");
484 MODULE_DESCRIPTION("Comedi low-level driver");
485 MODULE_LICENSE("GPL");