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