]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blob - drivers/staging/comedi/drivers/vmk80xx.c
staging: comedi: pcl818: Fix endian problem for AI command data
[mirror_ubuntu-hirsute-kernel.git] / drivers / staging / comedi / drivers / vmk80xx.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * vmk80xx.c
4 * Velleman USB Board Low-Level Driver
5 *
6 * Copyright (C) 2009 Manuel Gebele <forensixs@gmx.de>, Germany
7 *
8 * COMEDI - Linux Control and Measurement Device Interface
9 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
10 */
11
12 /*
13 * Driver: vmk80xx
14 * Description: Velleman USB Board Low-Level Driver
15 * Devices: [Velleman] K8055 (K8055/VM110), K8061 (K8061/VM140),
16 * VM110 (K8055/VM110), VM140 (K8061/VM140)
17 * Author: Manuel Gebele <forensixs@gmx.de>
18 * Updated: Sun, 10 May 2009 11:14:59 +0200
19 * Status: works
20 *
21 * Supports:
22 * - analog input
23 * - analog output
24 * - digital input
25 * - digital output
26 * - counter
27 * - pwm
28 */
29
30 #include <linux/kernel.h>
31 #include <linux/module.h>
32 #include <linux/mutex.h>
33 #include <linux/errno.h>
34 #include <linux/input.h>
35 #include <linux/slab.h>
36 #include <linux/poll.h>
37 #include <linux/uaccess.h>
38
39 #include "../comedi_usb.h"
40
41 enum {
42 DEVICE_VMK8055,
43 DEVICE_VMK8061
44 };
45
46 #define VMK8055_DI_REG 0x00
47 #define VMK8055_DO_REG 0x01
48 #define VMK8055_AO1_REG 0x02
49 #define VMK8055_AO2_REG 0x03
50 #define VMK8055_AI1_REG 0x02
51 #define VMK8055_AI2_REG 0x03
52 #define VMK8055_CNT1_REG 0x04
53 #define VMK8055_CNT2_REG 0x06
54
55 #define VMK8061_CH_REG 0x01
56 #define VMK8061_DI_REG 0x01
57 #define VMK8061_DO_REG 0x01
58 #define VMK8061_PWM_REG1 0x01
59 #define VMK8061_PWM_REG2 0x02
60 #define VMK8061_CNT_REG 0x02
61 #define VMK8061_AO_REG 0x02
62 #define VMK8061_AI_REG1 0x02
63 #define VMK8061_AI_REG2 0x03
64
65 #define VMK8055_CMD_RST 0x00
66 #define VMK8055_CMD_DEB1_TIME 0x01
67 #define VMK8055_CMD_DEB2_TIME 0x02
68 #define VMK8055_CMD_RST_CNT1 0x03
69 #define VMK8055_CMD_RST_CNT2 0x04
70 #define VMK8055_CMD_WRT_AD 0x05
71
72 #define VMK8061_CMD_RD_AI 0x00
73 #define VMK8061_CMR_RD_ALL_AI 0x01 /* !non-active! */
74 #define VMK8061_CMD_SET_AO 0x02
75 #define VMK8061_CMD_SET_ALL_AO 0x03 /* !non-active! */
76 #define VMK8061_CMD_OUT_PWM 0x04
77 #define VMK8061_CMD_RD_DI 0x05
78 #define VMK8061_CMD_DO 0x06 /* !non-active! */
79 #define VMK8061_CMD_CLR_DO 0x07
80 #define VMK8061_CMD_SET_DO 0x08
81 #define VMK8061_CMD_RD_CNT 0x09 /* TODO: completely pointless? */
82 #define VMK8061_CMD_RST_CNT 0x0a /* TODO: completely pointless? */
83 #define VMK8061_CMD_RD_VERSION 0x0b /* internal usage */
84 #define VMK8061_CMD_RD_JMP_STAT 0x0c /* TODO: not implemented yet */
85 #define VMK8061_CMD_RD_PWR_STAT 0x0d /* internal usage */
86 #define VMK8061_CMD_RD_DO 0x0e
87 #define VMK8061_CMD_RD_AO 0x0f
88 #define VMK8061_CMD_RD_PWM 0x10
89
90 #define IC3_VERSION BIT(0)
91 #define IC6_VERSION BIT(1)
92
93 enum vmk80xx_model {
94 VMK8055_MODEL,
95 VMK8061_MODEL
96 };
97
98 static const struct comedi_lrange vmk8061_range = {
99 2, {
100 UNI_RANGE(5),
101 UNI_RANGE(10)
102 }
103 };
104
105 struct vmk80xx_board {
106 const char *name;
107 enum vmk80xx_model model;
108 const struct comedi_lrange *range;
109 int ai_nchans;
110 unsigned int ai_maxdata;
111 int ao_nchans;
112 int di_nchans;
113 unsigned int cnt_maxdata;
114 int pwm_nchans;
115 unsigned int pwm_maxdata;
116 };
117
118 static const struct vmk80xx_board vmk80xx_boardinfo[] = {
119 [DEVICE_VMK8055] = {
120 .name = "K8055 (VM110)",
121 .model = VMK8055_MODEL,
122 .range = &range_unipolar5,
123 .ai_nchans = 2,
124 .ai_maxdata = 0x00ff,
125 .ao_nchans = 2,
126 .di_nchans = 6,
127 .cnt_maxdata = 0xffff,
128 },
129 [DEVICE_VMK8061] = {
130 .name = "K8061 (VM140)",
131 .model = VMK8061_MODEL,
132 .range = &vmk8061_range,
133 .ai_nchans = 8,
134 .ai_maxdata = 0x03ff,
135 .ao_nchans = 8,
136 .di_nchans = 8,
137 .cnt_maxdata = 0, /* unknown, device is not writeable */
138 .pwm_nchans = 1,
139 .pwm_maxdata = 0x03ff,
140 },
141 };
142
143 struct vmk80xx_private {
144 struct usb_endpoint_descriptor *ep_rx;
145 struct usb_endpoint_descriptor *ep_tx;
146 struct semaphore limit_sem;
147 unsigned char *usb_rx_buf;
148 unsigned char *usb_tx_buf;
149 enum vmk80xx_model model;
150 };
151
152 static void vmk80xx_do_bulk_msg(struct comedi_device *dev)
153 {
154 struct vmk80xx_private *devpriv = dev->private;
155 struct usb_device *usb = comedi_to_usb_dev(dev);
156 __u8 tx_addr;
157 __u8 rx_addr;
158 unsigned int tx_pipe;
159 unsigned int rx_pipe;
160 size_t size;
161
162 tx_addr = devpriv->ep_tx->bEndpointAddress;
163 rx_addr = devpriv->ep_rx->bEndpointAddress;
164 tx_pipe = usb_sndbulkpipe(usb, tx_addr);
165 rx_pipe = usb_rcvbulkpipe(usb, rx_addr);
166
167 /*
168 * The max packet size attributes of the K8061
169 * input/output endpoints are identical
170 */
171 size = usb_endpoint_maxp(devpriv->ep_tx);
172
173 usb_bulk_msg(usb, tx_pipe, devpriv->usb_tx_buf,
174 size, NULL, devpriv->ep_tx->bInterval);
175 usb_bulk_msg(usb, rx_pipe, devpriv->usb_rx_buf, size, NULL, HZ * 10);
176 }
177
178 static int vmk80xx_read_packet(struct comedi_device *dev)
179 {
180 struct vmk80xx_private *devpriv = dev->private;
181 struct usb_device *usb = comedi_to_usb_dev(dev);
182 struct usb_endpoint_descriptor *ep;
183 unsigned int pipe;
184
185 if (devpriv->model == VMK8061_MODEL) {
186 vmk80xx_do_bulk_msg(dev);
187 return 0;
188 }
189
190 ep = devpriv->ep_rx;
191 pipe = usb_rcvintpipe(usb, ep->bEndpointAddress);
192 return usb_interrupt_msg(usb, pipe, devpriv->usb_rx_buf,
193 usb_endpoint_maxp(ep), NULL,
194 HZ * 10);
195 }
196
197 static int vmk80xx_write_packet(struct comedi_device *dev, int cmd)
198 {
199 struct vmk80xx_private *devpriv = dev->private;
200 struct usb_device *usb = comedi_to_usb_dev(dev);
201 struct usb_endpoint_descriptor *ep;
202 unsigned int pipe;
203
204 devpriv->usb_tx_buf[0] = cmd;
205
206 if (devpriv->model == VMK8061_MODEL) {
207 vmk80xx_do_bulk_msg(dev);
208 return 0;
209 }
210
211 ep = devpriv->ep_tx;
212 pipe = usb_sndintpipe(usb, ep->bEndpointAddress);
213 return usb_interrupt_msg(usb, pipe, devpriv->usb_tx_buf,
214 usb_endpoint_maxp(ep), NULL,
215 HZ * 10);
216 }
217
218 static int vmk80xx_reset_device(struct comedi_device *dev)
219 {
220 struct vmk80xx_private *devpriv = dev->private;
221 size_t size;
222 int retval;
223
224 size = usb_endpoint_maxp(devpriv->ep_tx);
225 memset(devpriv->usb_tx_buf, 0, size);
226 retval = vmk80xx_write_packet(dev, VMK8055_CMD_RST);
227 if (retval)
228 return retval;
229 /* set outputs to known state as we cannot read them */
230 return vmk80xx_write_packet(dev, VMK8055_CMD_WRT_AD);
231 }
232
233 static int vmk80xx_ai_insn_read(struct comedi_device *dev,
234 struct comedi_subdevice *s,
235 struct comedi_insn *insn,
236 unsigned int *data)
237 {
238 struct vmk80xx_private *devpriv = dev->private;
239 int chan;
240 int reg[2];
241 int n;
242
243 down(&devpriv->limit_sem);
244 chan = CR_CHAN(insn->chanspec);
245
246 switch (devpriv->model) {
247 case VMK8055_MODEL:
248 if (!chan)
249 reg[0] = VMK8055_AI1_REG;
250 else
251 reg[0] = VMK8055_AI2_REG;
252 break;
253 case VMK8061_MODEL:
254 default:
255 reg[0] = VMK8061_AI_REG1;
256 reg[1] = VMK8061_AI_REG2;
257 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AI;
258 devpriv->usb_tx_buf[VMK8061_CH_REG] = chan;
259 break;
260 }
261
262 for (n = 0; n < insn->n; n++) {
263 if (vmk80xx_read_packet(dev))
264 break;
265
266 if (devpriv->model == VMK8055_MODEL) {
267 data[n] = devpriv->usb_rx_buf[reg[0]];
268 continue;
269 }
270
271 /* VMK8061_MODEL */
272 data[n] = devpriv->usb_rx_buf[reg[0]] + 256 *
273 devpriv->usb_rx_buf[reg[1]];
274 }
275
276 up(&devpriv->limit_sem);
277
278 return n;
279 }
280
281 static int vmk80xx_ao_insn_write(struct comedi_device *dev,
282 struct comedi_subdevice *s,
283 struct comedi_insn *insn,
284 unsigned int *data)
285 {
286 struct vmk80xx_private *devpriv = dev->private;
287 int chan;
288 int cmd;
289 int reg;
290 int n;
291
292 down(&devpriv->limit_sem);
293 chan = CR_CHAN(insn->chanspec);
294
295 switch (devpriv->model) {
296 case VMK8055_MODEL:
297 cmd = VMK8055_CMD_WRT_AD;
298 if (!chan)
299 reg = VMK8055_AO1_REG;
300 else
301 reg = VMK8055_AO2_REG;
302 break;
303 default: /* NOTE: avoid compiler warnings */
304 cmd = VMK8061_CMD_SET_AO;
305 reg = VMK8061_AO_REG;
306 devpriv->usb_tx_buf[VMK8061_CH_REG] = chan;
307 break;
308 }
309
310 for (n = 0; n < insn->n; n++) {
311 devpriv->usb_tx_buf[reg] = data[n];
312
313 if (vmk80xx_write_packet(dev, cmd))
314 break;
315 }
316
317 up(&devpriv->limit_sem);
318
319 return n;
320 }
321
322 static int vmk80xx_ao_insn_read(struct comedi_device *dev,
323 struct comedi_subdevice *s,
324 struct comedi_insn *insn,
325 unsigned int *data)
326 {
327 struct vmk80xx_private *devpriv = dev->private;
328 int chan;
329 int reg;
330 int n;
331
332 down(&devpriv->limit_sem);
333 chan = CR_CHAN(insn->chanspec);
334
335 reg = VMK8061_AO_REG - 1;
336
337 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AO;
338
339 for (n = 0; n < insn->n; n++) {
340 if (vmk80xx_read_packet(dev))
341 break;
342
343 data[n] = devpriv->usb_rx_buf[reg + chan];
344 }
345
346 up(&devpriv->limit_sem);
347
348 return n;
349 }
350
351 static int vmk80xx_di_insn_bits(struct comedi_device *dev,
352 struct comedi_subdevice *s,
353 struct comedi_insn *insn,
354 unsigned int *data)
355 {
356 struct vmk80xx_private *devpriv = dev->private;
357 unsigned char *rx_buf;
358 int reg;
359 int retval;
360
361 down(&devpriv->limit_sem);
362
363 rx_buf = devpriv->usb_rx_buf;
364
365 if (devpriv->model == VMK8061_MODEL) {
366 reg = VMK8061_DI_REG;
367 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_DI;
368 } else {
369 reg = VMK8055_DI_REG;
370 }
371
372 retval = vmk80xx_read_packet(dev);
373
374 if (!retval) {
375 if (devpriv->model == VMK8055_MODEL)
376 data[1] = (((rx_buf[reg] >> 4) & 0x03) |
377 ((rx_buf[reg] << 2) & 0x04) |
378 ((rx_buf[reg] >> 3) & 0x18));
379 else
380 data[1] = rx_buf[reg];
381
382 retval = 2;
383 }
384
385 up(&devpriv->limit_sem);
386
387 return retval;
388 }
389
390 static int vmk80xx_do_insn_bits(struct comedi_device *dev,
391 struct comedi_subdevice *s,
392 struct comedi_insn *insn,
393 unsigned int *data)
394 {
395 struct vmk80xx_private *devpriv = dev->private;
396 unsigned char *rx_buf = devpriv->usb_rx_buf;
397 unsigned char *tx_buf = devpriv->usb_tx_buf;
398 int reg, cmd;
399 int ret = 0;
400
401 if (devpriv->model == VMK8061_MODEL) {
402 reg = VMK8061_DO_REG;
403 cmd = VMK8061_CMD_DO;
404 } else { /* VMK8055_MODEL */
405 reg = VMK8055_DO_REG;
406 cmd = VMK8055_CMD_WRT_AD;
407 }
408
409 down(&devpriv->limit_sem);
410
411 if (comedi_dio_update_state(s, data)) {
412 tx_buf[reg] = s->state;
413 ret = vmk80xx_write_packet(dev, cmd);
414 if (ret)
415 goto out;
416 }
417
418 if (devpriv->model == VMK8061_MODEL) {
419 tx_buf[0] = VMK8061_CMD_RD_DO;
420 ret = vmk80xx_read_packet(dev);
421 if (ret)
422 goto out;
423 data[1] = rx_buf[reg];
424 } else {
425 data[1] = s->state;
426 }
427
428 out:
429 up(&devpriv->limit_sem);
430
431 return ret ? ret : insn->n;
432 }
433
434 static int vmk80xx_cnt_insn_read(struct comedi_device *dev,
435 struct comedi_subdevice *s,
436 struct comedi_insn *insn,
437 unsigned int *data)
438 {
439 struct vmk80xx_private *devpriv = dev->private;
440 int chan;
441 int reg[2];
442 int n;
443
444 down(&devpriv->limit_sem);
445 chan = CR_CHAN(insn->chanspec);
446
447 switch (devpriv->model) {
448 case VMK8055_MODEL:
449 if (!chan)
450 reg[0] = VMK8055_CNT1_REG;
451 else
452 reg[0] = VMK8055_CNT2_REG;
453 break;
454 case VMK8061_MODEL:
455 default:
456 reg[0] = VMK8061_CNT_REG;
457 reg[1] = VMK8061_CNT_REG;
458 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_CNT;
459 break;
460 }
461
462 for (n = 0; n < insn->n; n++) {
463 if (vmk80xx_read_packet(dev))
464 break;
465
466 if (devpriv->model == VMK8055_MODEL)
467 data[n] = devpriv->usb_rx_buf[reg[0]];
468 else /* VMK8061_MODEL */
469 data[n] = devpriv->usb_rx_buf[reg[0] * (chan + 1) + 1]
470 + 256 * devpriv->usb_rx_buf[reg[1] * 2 + 2];
471 }
472
473 up(&devpriv->limit_sem);
474
475 return n;
476 }
477
478 static int vmk80xx_cnt_insn_config(struct comedi_device *dev,
479 struct comedi_subdevice *s,
480 struct comedi_insn *insn,
481 unsigned int *data)
482 {
483 struct vmk80xx_private *devpriv = dev->private;
484 unsigned int chan = CR_CHAN(insn->chanspec);
485 int cmd;
486 int reg;
487 int ret;
488
489 down(&devpriv->limit_sem);
490 switch (data[0]) {
491 case INSN_CONFIG_RESET:
492 if (devpriv->model == VMK8055_MODEL) {
493 if (!chan) {
494 cmd = VMK8055_CMD_RST_CNT1;
495 reg = VMK8055_CNT1_REG;
496 } else {
497 cmd = VMK8055_CMD_RST_CNT2;
498 reg = VMK8055_CNT2_REG;
499 }
500 devpriv->usb_tx_buf[reg] = 0x00;
501 } else {
502 cmd = VMK8061_CMD_RST_CNT;
503 }
504 ret = vmk80xx_write_packet(dev, cmd);
505 break;
506 default:
507 ret = -EINVAL;
508 break;
509 }
510 up(&devpriv->limit_sem);
511
512 return ret ? ret : insn->n;
513 }
514
515 static int vmk80xx_cnt_insn_write(struct comedi_device *dev,
516 struct comedi_subdevice *s,
517 struct comedi_insn *insn,
518 unsigned int *data)
519 {
520 struct vmk80xx_private *devpriv = dev->private;
521 unsigned long debtime;
522 unsigned long val;
523 int chan;
524 int cmd;
525 int n;
526
527 down(&devpriv->limit_sem);
528 chan = CR_CHAN(insn->chanspec);
529
530 if (!chan)
531 cmd = VMK8055_CMD_DEB1_TIME;
532 else
533 cmd = VMK8055_CMD_DEB2_TIME;
534
535 for (n = 0; n < insn->n; n++) {
536 debtime = data[n];
537 if (debtime == 0)
538 debtime = 1;
539
540 /* TODO: Prevent overflows */
541 if (debtime > 7450)
542 debtime = 7450;
543
544 val = int_sqrt(debtime * 1000 / 115);
545 if (((val + 1) * val) < debtime * 1000 / 115)
546 val += 1;
547
548 devpriv->usb_tx_buf[6 + chan] = val;
549
550 if (vmk80xx_write_packet(dev, cmd))
551 break;
552 }
553
554 up(&devpriv->limit_sem);
555
556 return n;
557 }
558
559 static int vmk80xx_pwm_insn_read(struct comedi_device *dev,
560 struct comedi_subdevice *s,
561 struct comedi_insn *insn,
562 unsigned int *data)
563 {
564 struct vmk80xx_private *devpriv = dev->private;
565 unsigned char *tx_buf;
566 unsigned char *rx_buf;
567 int reg[2];
568 int n;
569
570 down(&devpriv->limit_sem);
571
572 tx_buf = devpriv->usb_tx_buf;
573 rx_buf = devpriv->usb_rx_buf;
574
575 reg[0] = VMK8061_PWM_REG1;
576 reg[1] = VMK8061_PWM_REG2;
577
578 tx_buf[0] = VMK8061_CMD_RD_PWM;
579
580 for (n = 0; n < insn->n; n++) {
581 if (vmk80xx_read_packet(dev))
582 break;
583
584 data[n] = rx_buf[reg[0]] + 4 * rx_buf[reg[1]];
585 }
586
587 up(&devpriv->limit_sem);
588
589 return n;
590 }
591
592 static int vmk80xx_pwm_insn_write(struct comedi_device *dev,
593 struct comedi_subdevice *s,
594 struct comedi_insn *insn,
595 unsigned int *data)
596 {
597 struct vmk80xx_private *devpriv = dev->private;
598 unsigned char *tx_buf;
599 int reg[2];
600 int cmd;
601 int n;
602
603 down(&devpriv->limit_sem);
604
605 tx_buf = devpriv->usb_tx_buf;
606
607 reg[0] = VMK8061_PWM_REG1;
608 reg[1] = VMK8061_PWM_REG2;
609
610 cmd = VMK8061_CMD_OUT_PWM;
611
612 /*
613 * The followin piece of code was translated from the inline
614 * assembler code in the DLL source code.
615 *
616 * asm
617 * mov eax, k ; k is the value (data[n])
618 * and al, 03h ; al are the lower 8 bits of eax
619 * mov lo, al ; lo is the low part (tx_buf[reg[0]])
620 * mov eax, k
621 * shr eax, 2 ; right shift eax register by 2
622 * mov hi, al ; hi is the high part (tx_buf[reg[1]])
623 * end;
624 */
625 for (n = 0; n < insn->n; n++) {
626 tx_buf[reg[0]] = (unsigned char)(data[n] & 0x03);
627 tx_buf[reg[1]] = (unsigned char)(data[n] >> 2) & 0xff;
628
629 if (vmk80xx_write_packet(dev, cmd))
630 break;
631 }
632
633 up(&devpriv->limit_sem);
634
635 return n;
636 }
637
638 static int vmk80xx_find_usb_endpoints(struct comedi_device *dev)
639 {
640 struct vmk80xx_private *devpriv = dev->private;
641 struct usb_interface *intf = comedi_to_usb_interface(dev);
642 struct usb_host_interface *iface_desc = intf->cur_altsetting;
643 struct usb_endpoint_descriptor *ep_desc;
644 int i;
645
646 if (iface_desc->desc.bNumEndpoints != 2)
647 return -ENODEV;
648
649 for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
650 ep_desc = &iface_desc->endpoint[i].desc;
651
652 if (usb_endpoint_is_int_in(ep_desc) ||
653 usb_endpoint_is_bulk_in(ep_desc)) {
654 if (!devpriv->ep_rx)
655 devpriv->ep_rx = ep_desc;
656 continue;
657 }
658
659 if (usb_endpoint_is_int_out(ep_desc) ||
660 usb_endpoint_is_bulk_out(ep_desc)) {
661 if (!devpriv->ep_tx)
662 devpriv->ep_tx = ep_desc;
663 continue;
664 }
665 }
666
667 if (!devpriv->ep_rx || !devpriv->ep_tx)
668 return -ENODEV;
669
670 if (!usb_endpoint_maxp(devpriv->ep_rx) || !usb_endpoint_maxp(devpriv->ep_tx))
671 return -EINVAL;
672
673 return 0;
674 }
675
676 static int vmk80xx_alloc_usb_buffers(struct comedi_device *dev)
677 {
678 struct vmk80xx_private *devpriv = dev->private;
679 size_t size;
680
681 size = usb_endpoint_maxp(devpriv->ep_rx);
682 devpriv->usb_rx_buf = kzalloc(size, GFP_KERNEL);
683 if (!devpriv->usb_rx_buf)
684 return -ENOMEM;
685
686 size = usb_endpoint_maxp(devpriv->ep_tx);
687 devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL);
688 if (!devpriv->usb_tx_buf)
689 return -ENOMEM;
690
691 return 0;
692 }
693
694 static int vmk80xx_init_subdevices(struct comedi_device *dev)
695 {
696 const struct vmk80xx_board *board = dev->board_ptr;
697 struct vmk80xx_private *devpriv = dev->private;
698 struct comedi_subdevice *s;
699 int n_subd;
700 int ret;
701
702 down(&devpriv->limit_sem);
703
704 if (devpriv->model == VMK8055_MODEL)
705 n_subd = 5;
706 else
707 n_subd = 6;
708 ret = comedi_alloc_subdevices(dev, n_subd);
709 if (ret) {
710 up(&devpriv->limit_sem);
711 return ret;
712 }
713
714 /* Analog input subdevice */
715 s = &dev->subdevices[0];
716 s->type = COMEDI_SUBD_AI;
717 s->subdev_flags = SDF_READABLE | SDF_GROUND;
718 s->n_chan = board->ai_nchans;
719 s->maxdata = board->ai_maxdata;
720 s->range_table = board->range;
721 s->insn_read = vmk80xx_ai_insn_read;
722
723 /* Analog output subdevice */
724 s = &dev->subdevices[1];
725 s->type = COMEDI_SUBD_AO;
726 s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
727 s->n_chan = board->ao_nchans;
728 s->maxdata = 0x00ff;
729 s->range_table = board->range;
730 s->insn_write = vmk80xx_ao_insn_write;
731 if (devpriv->model == VMK8061_MODEL) {
732 s->subdev_flags |= SDF_READABLE;
733 s->insn_read = vmk80xx_ao_insn_read;
734 }
735
736 /* Digital input subdevice */
737 s = &dev->subdevices[2];
738 s->type = COMEDI_SUBD_DI;
739 s->subdev_flags = SDF_READABLE;
740 s->n_chan = board->di_nchans;
741 s->maxdata = 1;
742 s->range_table = &range_digital;
743 s->insn_bits = vmk80xx_di_insn_bits;
744
745 /* Digital output subdevice */
746 s = &dev->subdevices[3];
747 s->type = COMEDI_SUBD_DO;
748 s->subdev_flags = SDF_WRITABLE;
749 s->n_chan = 8;
750 s->maxdata = 1;
751 s->range_table = &range_digital;
752 s->insn_bits = vmk80xx_do_insn_bits;
753
754 /* Counter subdevice */
755 s = &dev->subdevices[4];
756 s->type = COMEDI_SUBD_COUNTER;
757 s->subdev_flags = SDF_READABLE;
758 s->n_chan = 2;
759 s->maxdata = board->cnt_maxdata;
760 s->insn_read = vmk80xx_cnt_insn_read;
761 s->insn_config = vmk80xx_cnt_insn_config;
762 if (devpriv->model == VMK8055_MODEL) {
763 s->subdev_flags |= SDF_WRITABLE;
764 s->insn_write = vmk80xx_cnt_insn_write;
765 }
766
767 /* PWM subdevice */
768 if (devpriv->model == VMK8061_MODEL) {
769 s = &dev->subdevices[5];
770 s->type = COMEDI_SUBD_PWM;
771 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
772 s->n_chan = board->pwm_nchans;
773 s->maxdata = board->pwm_maxdata;
774 s->insn_read = vmk80xx_pwm_insn_read;
775 s->insn_write = vmk80xx_pwm_insn_write;
776 }
777
778 up(&devpriv->limit_sem);
779
780 return 0;
781 }
782
783 static int vmk80xx_auto_attach(struct comedi_device *dev,
784 unsigned long context)
785 {
786 struct usb_interface *intf = comedi_to_usb_interface(dev);
787 const struct vmk80xx_board *board = NULL;
788 struct vmk80xx_private *devpriv;
789 int ret;
790
791 if (context < ARRAY_SIZE(vmk80xx_boardinfo))
792 board = &vmk80xx_boardinfo[context];
793 if (!board)
794 return -ENODEV;
795 dev->board_ptr = board;
796 dev->board_name = board->name;
797
798 devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
799 if (!devpriv)
800 return -ENOMEM;
801
802 devpriv->model = board->model;
803
804 sema_init(&devpriv->limit_sem, 8);
805
806 ret = vmk80xx_find_usb_endpoints(dev);
807 if (ret)
808 return ret;
809
810 ret = vmk80xx_alloc_usb_buffers(dev);
811 if (ret)
812 return ret;
813
814 usb_set_intfdata(intf, devpriv);
815
816 if (devpriv->model == VMK8055_MODEL)
817 vmk80xx_reset_device(dev);
818
819 return vmk80xx_init_subdevices(dev);
820 }
821
822 static void vmk80xx_detach(struct comedi_device *dev)
823 {
824 struct usb_interface *intf = comedi_to_usb_interface(dev);
825 struct vmk80xx_private *devpriv = dev->private;
826
827 if (!devpriv)
828 return;
829
830 down(&devpriv->limit_sem);
831
832 usb_set_intfdata(intf, NULL);
833
834 kfree(devpriv->usb_rx_buf);
835 kfree(devpriv->usb_tx_buf);
836
837 up(&devpriv->limit_sem);
838 }
839
840 static struct comedi_driver vmk80xx_driver = {
841 .module = THIS_MODULE,
842 .driver_name = "vmk80xx",
843 .auto_attach = vmk80xx_auto_attach,
844 .detach = vmk80xx_detach,
845 };
846
847 static int vmk80xx_usb_probe(struct usb_interface *intf,
848 const struct usb_device_id *id)
849 {
850 return comedi_usb_auto_config(intf, &vmk80xx_driver, id->driver_info);
851 }
852
853 static const struct usb_device_id vmk80xx_usb_id_table[] = {
854 { USB_DEVICE(0x10cf, 0x5500), .driver_info = DEVICE_VMK8055 },
855 { USB_DEVICE(0x10cf, 0x5501), .driver_info = DEVICE_VMK8055 },
856 { USB_DEVICE(0x10cf, 0x5502), .driver_info = DEVICE_VMK8055 },
857 { USB_DEVICE(0x10cf, 0x5503), .driver_info = DEVICE_VMK8055 },
858 { USB_DEVICE(0x10cf, 0x8061), .driver_info = DEVICE_VMK8061 },
859 { USB_DEVICE(0x10cf, 0x8062), .driver_info = DEVICE_VMK8061 },
860 { USB_DEVICE(0x10cf, 0x8063), .driver_info = DEVICE_VMK8061 },
861 { USB_DEVICE(0x10cf, 0x8064), .driver_info = DEVICE_VMK8061 },
862 { USB_DEVICE(0x10cf, 0x8065), .driver_info = DEVICE_VMK8061 },
863 { USB_DEVICE(0x10cf, 0x8066), .driver_info = DEVICE_VMK8061 },
864 { USB_DEVICE(0x10cf, 0x8067), .driver_info = DEVICE_VMK8061 },
865 { USB_DEVICE(0x10cf, 0x8068), .driver_info = DEVICE_VMK8061 },
866 { }
867 };
868 MODULE_DEVICE_TABLE(usb, vmk80xx_usb_id_table);
869
870 static struct usb_driver vmk80xx_usb_driver = {
871 .name = "vmk80xx",
872 .id_table = vmk80xx_usb_id_table,
873 .probe = vmk80xx_usb_probe,
874 .disconnect = comedi_usb_auto_unconfig,
875 };
876 module_comedi_usb_driver(vmk80xx_driver, vmk80xx_usb_driver);
877
878 MODULE_AUTHOR("Manuel Gebele <forensixs@gmx.de>");
879 MODULE_DESCRIPTION("Velleman USB Board Low-Level Driver");
880 MODULE_SUPPORTED_DEVICE("K8055/K8061 aka VM110/VM140");
881 MODULE_LICENSE("GPL");