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