staging: comedi: vmk80xx: simplify vmk80xx_reset_device()
[linux-block.git] / drivers / staging / comedi / drivers / vmk80xx.c
CommitLineData
3faad673
MG
1/*
2 comedi/drivers/vmk80xx.c
985cafcc 3 Velleman USB Board Low-Level Driver
3faad673
MG
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
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/*
26Driver: vmk80xx
985cafcc
MG
27Description: Velleman USB Board Low-Level Driver
28Devices: K8055/K8061 aka VM110/VM140
3faad673 29Author: Manuel Gebele <forensixs@gmx.de>
985cafcc 30Updated: Sun, 10 May 2009 11:14:59 +0200
3faad673 31Status: works
985cafcc
MG
32
33Supports:
34 - analog input
35 - analog output
36 - digital input
37 - digital output
38 - counter
39 - pwm
40*/
3faad673
MG
41
42#include <linux/kernel.h>
3faad673
MG
43#include <linux/module.h>
44#include <linux/mutex.h>
45#include <linux/errno.h>
46#include <linux/input.h>
47#include <linux/slab.h>
48#include <linux/poll.h>
49#include <linux/usb.h>
985cafcc
MG
50#include <linux/uaccess.h>
51
52#include "../comedidev.h"
53
985cafcc
MG
54enum {
55 DEVICE_VMK8055,
56 DEVICE_VMK8061
57};
58
985cafcc
MG
59#define VMK8055_DI_REG 0x00
60#define VMK8055_DO_REG 0x01
61#define VMK8055_AO1_REG 0x02
62#define VMK8055_AO2_REG 0x03
63#define VMK8055_AI1_REG 0x02
64#define VMK8055_AI2_REG 0x03
65#define VMK8055_CNT1_REG 0x04
66#define VMK8055_CNT2_REG 0x06
67
68#define VMK8061_CH_REG 0x01
69#define VMK8061_DI_REG 0x01
70#define VMK8061_DO_REG 0x01
71#define VMK8061_PWM_REG1 0x01
72#define VMK8061_PWM_REG2 0x02
73#define VMK8061_CNT_REG 0x02
74#define VMK8061_AO_REG 0x02
75#define VMK8061_AI_REG1 0x02
76#define VMK8061_AI_REG2 0x03
77
78#define VMK8055_CMD_RST 0x00
79#define VMK8055_CMD_DEB1_TIME 0x01
80#define VMK8055_CMD_DEB2_TIME 0x02
81#define VMK8055_CMD_RST_CNT1 0x03
82#define VMK8055_CMD_RST_CNT2 0x04
83#define VMK8055_CMD_WRT_AD 0x05
84
85#define VMK8061_CMD_RD_AI 0x00
0a85b6f0 86#define VMK8061_CMR_RD_ALL_AI 0x01 /* !non-active! */
985cafcc 87#define VMK8061_CMD_SET_AO 0x02
0a85b6f0 88#define VMK8061_CMD_SET_ALL_AO 0x03 /* !non-active! */
985cafcc
MG
89#define VMK8061_CMD_OUT_PWM 0x04
90#define VMK8061_CMD_RD_DI 0x05
0a85b6f0 91#define VMK8061_CMD_DO 0x06 /* !non-active! */
985cafcc
MG
92#define VMK8061_CMD_CLR_DO 0x07
93#define VMK8061_CMD_SET_DO 0x08
0a85b6f0
MT
94#define VMK8061_CMD_RD_CNT 0x09 /* TODO: completely pointless? */
95#define VMK8061_CMD_RST_CNT 0x0a /* TODO: completely pointless? */
96#define VMK8061_CMD_RD_VERSION 0x0b /* internal usage */
97#define VMK8061_CMD_RD_JMP_STAT 0x0c /* TODO: not implemented yet */
98#define VMK8061_CMD_RD_PWR_STAT 0x0d /* internal usage */
985cafcc
MG
99#define VMK8061_CMD_RD_DO 0x0e
100#define VMK8061_CMD_RD_AO 0x0f
101#define VMK8061_CMD_RD_PWM 0x10
102
985cafcc
MG
103#define TRANS_OUT_BUSY 1
104#define TRANS_IN_BUSY 2
105#define TRANS_IN_RUNNING 3
106
107#define IC3_VERSION (1 << 0)
108#define IC6_VERSION (1 << 1)
109
110#define URB_RCV_FLAG (1 << 0)
111#define URB_SND_FLAG (1 << 1)
3faad673 112
985cafcc
MG
113enum vmk80xx_model {
114 VMK8055_MODEL,
115 VMK8061_MODEL
116};
3faad673 117
985cafcc 118struct firmware_version {
0a85b6f0
MT
119 unsigned char ic3_vers[32]; /* USB-Controller */
120 unsigned char ic6_vers[32]; /* CPU */
3faad673
MG
121};
122
985cafcc 123static const struct comedi_lrange vmk8061_range = {
f45787c6
HS
124 2, {
125 UNI_RANGE(5),
126 UNI_RANGE(10)
127 }
985cafcc 128};
3faad673 129
985cafcc
MG
130struct vmk80xx_board {
131 const char *name;
132 enum vmk80xx_model model;
133 const struct comedi_lrange *range;
658cd3ac
HS
134 int ai_nchans;
135 unsigned int ai_maxdata;
8b3ec9f1 136 int ao_nchans;
268e5148 137 int di_nchans;
75a45d92 138 unsigned int cnt_maxdata;
9a23a748
HS
139 int pwm_nchans;
140 unsigned int pwm_maxdata;
985cafcc 141};
3faad673 142
20d60077
HS
143static const struct vmk80xx_board vmk80xx_boardinfo[] = {
144 [DEVICE_VMK8055] = {
145 .name = "K8055 (VM110)",
146 .model = VMK8055_MODEL,
f45787c6 147 .range = &range_unipolar5,
658cd3ac
HS
148 .ai_nchans = 2,
149 .ai_maxdata = 0x00ff,
8b3ec9f1 150 .ao_nchans = 2,
268e5148 151 .di_nchans = 6,
75a45d92 152 .cnt_maxdata = 0xffff,
20d60077
HS
153 },
154 [DEVICE_VMK8061] = {
155 .name = "K8061 (VM140)",
156 .model = VMK8061_MODEL,
157 .range = &vmk8061_range,
658cd3ac
HS
158 .ai_nchans = 8,
159 .ai_maxdata = 0x03ff,
8b3ec9f1 160 .ao_nchans = 8,
268e5148 161 .di_nchans = 8,
75a45d92 162 .cnt_maxdata = 0, /* unknown, device is not writeable */
9a23a748
HS
163 .pwm_nchans = 1,
164 .pwm_maxdata = 0x03ff,
20d60077
HS
165 },
166};
167
dc49cbfc 168struct vmk80xx_private {
da7b18ee 169 struct usb_device *usb;
985cafcc
MG
170 struct usb_interface *intf;
171 struct usb_endpoint_descriptor *ep_rx;
172 struct usb_endpoint_descriptor *ep_tx;
173 struct usb_anchor rx_anchor;
174 struct usb_anchor tx_anchor;
985cafcc
MG
175 struct firmware_version fw;
176 struct semaphore limit_sem;
177 wait_queue_head_t read_wait;
178 wait_queue_head_t write_wait;
179 unsigned char *usb_rx_buf;
180 unsigned char *usb_tx_buf;
181 unsigned long flags;
52d895d3 182 enum vmk80xx_model model;
985cafcc
MG
183};
184
985cafcc 185static void vmk80xx_tx_callback(struct urb *urb)
3faad673 186{
da7b18ee
HS
187 struct vmk80xx_private *devpriv = urb->context;
188 unsigned long *flags = &devpriv->flags;
3faad673 189
da7b18ee 190 if (!test_bit(TRANS_OUT_BUSY, flags))
985cafcc 191 return;
3faad673 192
da7b18ee 193 clear_bit(TRANS_OUT_BUSY, flags);
3faad673 194
da7b18ee 195 wake_up_interruptible(&devpriv->write_wait);
3faad673
MG
196}
197
985cafcc 198static void vmk80xx_rx_callback(struct urb *urb)
3faad673 199{
da7b18ee
HS
200 struct vmk80xx_private *devpriv = urb->context;
201 unsigned long *flags = &devpriv->flags;
985cafcc 202 int stat = urb->status;
3faad673 203
985cafcc
MG
204 switch (stat) {
205 case 0:
3faad673
MG
206 break;
207 case -ENOENT:
208 case -ECONNRESET:
209 case -ESHUTDOWN:
210 break;
211 default:
4c56a2b6
HS
212 /* Try to resubmit the urb */
213 if (test_bit(TRANS_IN_RUNNING, flags) && devpriv->intf) {
214 usb_anchor_urb(urb, &devpriv->rx_anchor);
985cafcc 215
4c56a2b6
HS
216 if (usb_submit_urb(urb, GFP_KERNEL))
217 usb_unanchor_urb(urb);
218 }
219 break;
3faad673 220 }
4c56a2b6 221
da7b18ee 222 clear_bit(TRANS_IN_BUSY, flags);
3faad673 223
da7b18ee 224 wake_up_interruptible(&devpriv->read_wait);
3faad673
MG
225}
226
da7b18ee 227static int vmk80xx_check_data_link(struct vmk80xx_private *devpriv)
3faad673 228{
da7b18ee 229 struct usb_device *usb = devpriv->usb;
3a229fd5
AH
230 unsigned int tx_pipe;
231 unsigned int rx_pipe;
232 unsigned char tx[1];
233 unsigned char rx[2];
985cafcc 234
da7b18ee
HS
235 tx_pipe = usb_sndbulkpipe(usb, 0x01);
236 rx_pipe = usb_rcvbulkpipe(usb, 0x81);
3faad673 237
985cafcc 238 tx[0] = VMK8061_CMD_RD_PWR_STAT;
3faad673 239
3a229fd5
AH
240 /*
241 * Check that IC6 (PIC16F871) is powered and
985cafcc 242 * running and the data link between IC3 and
3a229fd5
AH
243 * IC6 is working properly
244 */
da7b18ee
HS
245 usb_bulk_msg(usb, tx_pipe, tx, 1, NULL, devpriv->ep_tx->bInterval);
246 usb_bulk_msg(usb, rx_pipe, rx, 2, NULL, HZ * 10);
3faad673 247
985cafcc 248 return (int)rx[1];
3faad673
MG
249}
250
da7b18ee 251static void vmk80xx_read_eeprom(struct vmk80xx_private *devpriv, int flag)
3faad673 252{
da7b18ee 253 struct usb_device *usb = devpriv->usb;
3a229fd5
AH
254 unsigned int tx_pipe;
255 unsigned int rx_pipe;
256 unsigned char tx[1];
257 unsigned char rx[64];
985cafcc 258 int cnt;
3faad673 259
da7b18ee
HS
260 tx_pipe = usb_sndbulkpipe(usb, 0x01);
261 rx_pipe = usb_rcvbulkpipe(usb, 0x81);
3faad673 262
985cafcc
MG
263 tx[0] = VMK8061_CMD_RD_VERSION;
264
3a229fd5
AH
265 /*
266 * Read the firmware version info of IC3 and
267 * IC6 from the internal EEPROM of the IC
268 */
da7b18ee
HS
269 usb_bulk_msg(usb, tx_pipe, tx, 1, NULL, devpriv->ep_tx->bInterval);
270 usb_bulk_msg(usb, rx_pipe, rx, 64, &cnt, HZ * 10);
985cafcc
MG
271
272 rx[cnt] = '\0';
273
274 if (flag & IC3_VERSION)
da7b18ee 275 strncpy(devpriv->fw.ic3_vers, rx + 1, 24);
0a85b6f0 276 else /* IC6_VERSION */
da7b18ee 277 strncpy(devpriv->fw.ic6_vers, rx + 25, 24);
985cafcc
MG
278}
279
985cafcc
MG
280static void vmk80xx_build_int_urb(struct urb *urb, int flag)
281{
da7b18ee
HS
282 struct vmk80xx_private *devpriv = urb->context;
283 struct usb_device *usb = devpriv->usb;
3a229fd5
AH
284 __u8 rx_addr;
285 __u8 tx_addr;
985cafcc
MG
286 unsigned int pipe;
287 unsigned char *buf;
288 size_t size;
0a85b6f0 289 void (*callback) (struct urb *);
985cafcc
MG
290 int ival;
291
985cafcc 292 if (flag & URB_RCV_FLAG) {
da7b18ee
HS
293 rx_addr = devpriv->ep_rx->bEndpointAddress;
294 pipe = usb_rcvintpipe(usb, rx_addr);
295 buf = devpriv->usb_rx_buf;
296 size = le16_to_cpu(devpriv->ep_rx->wMaxPacketSize);
985cafcc 297 callback = vmk80xx_rx_callback;
da7b18ee 298 ival = devpriv->ep_rx->bInterval;
0a85b6f0 299 } else { /* URB_SND_FLAG */
da7b18ee
HS
300 tx_addr = devpriv->ep_tx->bEndpointAddress;
301 pipe = usb_sndintpipe(usb, tx_addr);
302 buf = devpriv->usb_tx_buf;
303 size = le16_to_cpu(devpriv->ep_tx->wMaxPacketSize);
985cafcc 304 callback = vmk80xx_tx_callback;
da7b18ee 305 ival = devpriv->ep_tx->bInterval;
3faad673
MG
306 }
307
da7b18ee 308 usb_fill_int_urb(urb, usb, pipe, buf, size, callback, devpriv, ival);
985cafcc 309}
3faad673 310
da7b18ee 311static void vmk80xx_do_bulk_msg(struct vmk80xx_private *devpriv)
985cafcc 312{
da7b18ee
HS
313 struct usb_device *usb = devpriv->usb;
314 unsigned long *flags = &devpriv->flags;
3a229fd5
AH
315 __u8 tx_addr;
316 __u8 rx_addr;
317 unsigned int tx_pipe;
318 unsigned int rx_pipe;
985cafcc 319 size_t size;
3faad673 320
da7b18ee
HS
321 set_bit(TRANS_IN_BUSY, flags);
322 set_bit(TRANS_OUT_BUSY, flags);
3faad673 323
da7b18ee
HS
324 tx_addr = devpriv->ep_tx->bEndpointAddress;
325 rx_addr = devpriv->ep_rx->bEndpointAddress;
326 tx_pipe = usb_sndbulkpipe(usb, tx_addr);
327 rx_pipe = usb_rcvbulkpipe(usb, rx_addr);
985cafcc 328
3a229fd5
AH
329 /*
330 * The max packet size attributes of the K8061
331 * input/output endpoints are identical
332 */
da7b18ee 333 size = le16_to_cpu(devpriv->ep_tx->wMaxPacketSize);
985cafcc 334
da7b18ee
HS
335 usb_bulk_msg(usb, tx_pipe, devpriv->usb_tx_buf,
336 size, NULL, devpriv->ep_tx->bInterval);
337 usb_bulk_msg(usb, rx_pipe, devpriv->usb_rx_buf, size, NULL, HZ * 10);
985cafcc 338
da7b18ee
HS
339 clear_bit(TRANS_OUT_BUSY, flags);
340 clear_bit(TRANS_IN_BUSY, flags);
3faad673
MG
341}
342
da7b18ee 343static int vmk80xx_read_packet(struct vmk80xx_private *devpriv)
3faad673 344{
da7b18ee 345 unsigned long *flags = &devpriv->flags;
985cafcc
MG
346 struct urb *urb;
347 int retval;
3faad673 348
da7b18ee 349 if (!devpriv->intf)
985cafcc 350 return -ENODEV;
3faad673 351
985cafcc 352 /* Only useful for interrupt transfers */
da7b18ee
HS
353 if (test_bit(TRANS_IN_BUSY, flags))
354 if (wait_event_interruptible(devpriv->read_wait,
355 !test_bit(TRANS_IN_BUSY, flags)))
985cafcc
MG
356 return -ERESTART;
357
52d895d3 358 if (devpriv->model == VMK8061_MODEL) {
da7b18ee 359 vmk80xx_do_bulk_msg(devpriv);
985cafcc
MG
360
361 return 0;
3faad673
MG
362 }
363
985cafcc
MG
364 urb = usb_alloc_urb(0, GFP_KERNEL);
365 if (!urb)
366 return -ENOMEM;
3faad673 367
da7b18ee 368 urb->context = devpriv;
985cafcc 369 vmk80xx_build_int_urb(urb, URB_RCV_FLAG);
3faad673 370
da7b18ee
HS
371 set_bit(TRANS_IN_RUNNING, flags);
372 set_bit(TRANS_IN_BUSY, flags);
3faad673 373
da7b18ee 374 usb_anchor_urb(urb, &devpriv->rx_anchor);
3faad673 375
985cafcc
MG
376 retval = usb_submit_urb(urb, GFP_KERNEL);
377 if (!retval)
378 goto exit;
3faad673 379
da7b18ee 380 clear_bit(TRANS_IN_RUNNING, flags);
985cafcc 381 usb_unanchor_urb(urb);
3faad673
MG
382
383exit:
985cafcc
MG
384 usb_free_urb(urb);
385
3faad673
MG
386 return retval;
387}
388
da7b18ee 389static int vmk80xx_write_packet(struct vmk80xx_private *devpriv, int cmd)
3faad673 390{
da7b18ee 391 unsigned long *flags = &devpriv->flags;
985cafcc
MG
392 struct urb *urb;
393 int retval;
3faad673 394
da7b18ee 395 if (!devpriv->intf)
985cafcc 396 return -ENODEV;
3faad673 397
da7b18ee
HS
398 if (test_bit(TRANS_OUT_BUSY, flags))
399 if (wait_event_interruptible(devpriv->write_wait,
400 !test_bit(TRANS_OUT_BUSY, flags)))
985cafcc 401 return -ERESTART;
3faad673 402
52d895d3 403 if (devpriv->model == VMK8061_MODEL) {
da7b18ee
HS
404 devpriv->usb_tx_buf[0] = cmd;
405 vmk80xx_do_bulk_msg(devpriv);
3faad673 406
985cafcc 407 return 0;
3faad673
MG
408 }
409
985cafcc
MG
410 urb = usb_alloc_urb(0, GFP_KERNEL);
411 if (!urb)
412 return -ENOMEM;
413
da7b18ee 414 urb->context = devpriv;
985cafcc 415 vmk80xx_build_int_urb(urb, URB_SND_FLAG);
3faad673 416
da7b18ee 417 set_bit(TRANS_OUT_BUSY, flags);
3faad673 418
da7b18ee 419 usb_anchor_urb(urb, &devpriv->tx_anchor);
3faad673 420
da7b18ee 421 devpriv->usb_tx_buf[0] = cmd;
3faad673 422
985cafcc
MG
423 retval = usb_submit_urb(urb, GFP_KERNEL);
424 if (!retval)
425 goto exit;
426
da7b18ee 427 clear_bit(TRANS_OUT_BUSY, flags);
985cafcc
MG
428 usb_unanchor_urb(urb);
429
430exit:
431 usb_free_urb(urb);
3faad673
MG
432
433 return retval;
434}
435
e8f311a5
IA
436static int vmk80xx_reset_device(struct vmk80xx_private *devpriv)
437{
438 size_t size;
439
440 size = le16_to_cpu(devpriv->ep_tx->wMaxPacketSize);
441 memset(devpriv->usb_tx_buf, 0, size);
442 return vmk80xx_write_packet(devpriv, VMK8055_CMD_RST);
443}
444
985cafcc
MG
445#define DIR_IN 1
446#define DIR_OUT 2
447
da7b18ee 448static int rudimentary_check(struct vmk80xx_private *devpriv, int dir)
587e500c 449{
da7b18ee 450 if (!devpriv)
587e500c 451 return -EFAULT;
587e500c 452 if (dir & DIR_IN) {
da7b18ee 453 if (test_bit(TRANS_IN_BUSY, &devpriv->flags))
587e500c 454 return -EBUSY;
510b9be3
AH
455 }
456 if (dir & DIR_OUT) {
da7b18ee 457 if (test_bit(TRANS_OUT_BUSY, &devpriv->flags))
587e500c
AH
458 return -EBUSY;
459 }
460
461 return 0;
462}
985cafcc 463
658cd3ac
HS
464static int vmk80xx_ai_insn_read(struct comedi_device *dev,
465 struct comedi_subdevice *s,
466 struct comedi_insn *insn,
467 unsigned int *data)
3faad673 468{
da7b18ee 469 struct vmk80xx_private *devpriv = dev->private;
3a229fd5
AH
470 int chan;
471 int reg[2];
985cafcc 472 int n;
3faad673 473
da7b18ee 474 n = rudimentary_check(devpriv, DIR_IN);
587e500c
AH
475 if (n)
476 return n;
3faad673 477
da7b18ee 478 down(&devpriv->limit_sem);
985cafcc 479 chan = CR_CHAN(insn->chanspec);
3faad673 480
52d895d3 481 switch (devpriv->model) {
985cafcc
MG
482 case VMK8055_MODEL:
483 if (!chan)
484 reg[0] = VMK8055_AI1_REG;
485 else
486 reg[0] = VMK8055_AI2_REG;
487 break;
488 case VMK8061_MODEL:
13f7952f 489 default:
985cafcc
MG
490 reg[0] = VMK8061_AI_REG1;
491 reg[1] = VMK8061_AI_REG2;
da7b18ee
HS
492 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AI;
493 devpriv->usb_tx_buf[VMK8061_CH_REG] = chan;
985cafcc 494 break;
3faad673
MG
495 }
496
985cafcc 497 for (n = 0; n < insn->n; n++) {
da7b18ee 498 if (vmk80xx_read_packet(devpriv))
985cafcc 499 break;
3faad673 500
52d895d3 501 if (devpriv->model == VMK8055_MODEL) {
da7b18ee 502 data[n] = devpriv->usb_rx_buf[reg[0]];
985cafcc
MG
503 continue;
504 }
3faad673 505
985cafcc 506 /* VMK8061_MODEL */
da7b18ee
HS
507 data[n] = devpriv->usb_rx_buf[reg[0]] + 256 *
508 devpriv->usb_rx_buf[reg[1]];
985cafcc 509 }
3faad673 510
da7b18ee 511 up(&devpriv->limit_sem);
3faad673 512
985cafcc 513 return n;
3faad673
MG
514}
515
8b3ec9f1
HS
516static int vmk80xx_ao_insn_write(struct comedi_device *dev,
517 struct comedi_subdevice *s,
518 struct comedi_insn *insn,
519 unsigned int *data)
3faad673 520{
da7b18ee 521 struct vmk80xx_private *devpriv = dev->private;
3a229fd5
AH
522 int chan;
523 int cmd;
524 int reg;
985cafcc 525 int n;
3faad673 526
da7b18ee 527 n = rudimentary_check(devpriv, DIR_OUT);
587e500c
AH
528 if (n)
529 return n;
3faad673 530
da7b18ee 531 down(&devpriv->limit_sem);
985cafcc 532 chan = CR_CHAN(insn->chanspec);
3faad673 533
52d895d3 534 switch (devpriv->model) {
985cafcc
MG
535 case VMK8055_MODEL:
536 cmd = VMK8055_CMD_WRT_AD;
537 if (!chan)
538 reg = VMK8055_AO1_REG;
539 else
540 reg = VMK8055_AO2_REG;
541 break;
0a85b6f0 542 default: /* NOTE: avoid compiler warnings */
985cafcc
MG
543 cmd = VMK8061_CMD_SET_AO;
544 reg = VMK8061_AO_REG;
da7b18ee 545 devpriv->usb_tx_buf[VMK8061_CH_REG] = chan;
985cafcc 546 break;
3faad673
MG
547 }
548
985cafcc 549 for (n = 0; n < insn->n; n++) {
da7b18ee 550 devpriv->usb_tx_buf[reg] = data[n];
3faad673 551
da7b18ee 552 if (vmk80xx_write_packet(devpriv, cmd))
985cafcc 553 break;
3faad673
MG
554 }
555
da7b18ee 556 up(&devpriv->limit_sem);
3faad673 557
985cafcc 558 return n;
3faad673
MG
559}
560
8b3ec9f1
HS
561static int vmk80xx_ao_insn_read(struct comedi_device *dev,
562 struct comedi_subdevice *s,
563 struct comedi_insn *insn,
564 unsigned int *data)
3faad673 565{
da7b18ee 566 struct vmk80xx_private *devpriv = dev->private;
3a229fd5
AH
567 int chan;
568 int reg;
985cafcc 569 int n;
3faad673 570
da7b18ee 571 n = rudimentary_check(devpriv, DIR_IN);
587e500c
AH
572 if (n)
573 return n;
3faad673 574
da7b18ee 575 down(&devpriv->limit_sem);
985cafcc 576 chan = CR_CHAN(insn->chanspec);
3faad673 577
985cafcc 578 reg = VMK8061_AO_REG - 1;
3faad673 579
da7b18ee 580 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AO;
985cafcc
MG
581
582 for (n = 0; n < insn->n; n++) {
da7b18ee 583 if (vmk80xx_read_packet(devpriv))
985cafcc
MG
584 break;
585
da7b18ee 586 data[n] = devpriv->usb_rx_buf[reg + chan];
3faad673
MG
587 }
588
da7b18ee 589 up(&devpriv->limit_sem);
3faad673 590
985cafcc
MG
591 return n;
592}
3faad673 593
268e5148
HS
594static int vmk80xx_di_insn_bits(struct comedi_device *dev,
595 struct comedi_subdevice *s,
596 struct comedi_insn *insn,
597 unsigned int *data)
c647ed56 598{
da7b18ee 599 struct vmk80xx_private *devpriv = dev->private;
c647ed56
AH
600 unsigned char *rx_buf;
601 int reg;
602 int retval;
603
da7b18ee 604 retval = rudimentary_check(devpriv, DIR_IN);
c647ed56
AH
605 if (retval)
606 return retval;
607
da7b18ee 608 down(&devpriv->limit_sem);
c647ed56 609
da7b18ee 610 rx_buf = devpriv->usb_rx_buf;
c647ed56 611
52d895d3 612 if (devpriv->model == VMK8061_MODEL) {
c647ed56 613 reg = VMK8061_DI_REG;
da7b18ee 614 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_DI;
c647ed56
AH
615 } else {
616 reg = VMK8055_DI_REG;
617 }
618
da7b18ee 619 retval = vmk80xx_read_packet(devpriv);
c647ed56
AH
620
621 if (!retval) {
52d895d3 622 if (devpriv->model == VMK8055_MODEL)
c647ed56
AH
623 data[1] = (((rx_buf[reg] >> 4) & 0x03) |
624 ((rx_buf[reg] << 2) & 0x04) |
625 ((rx_buf[reg] >> 3) & 0x18));
626 else
627 data[1] = rx_buf[reg];
628
629 retval = 2;
630 }
631
da7b18ee 632 up(&devpriv->limit_sem);
c647ed56
AH
633
634 return retval;
635}
636
b639e096
HS
637static int vmk80xx_do_insn_bits(struct comedi_device *dev,
638 struct comedi_subdevice *s,
639 struct comedi_insn *insn,
640 unsigned int *data)
c647ed56 641{
da7b18ee 642 struct vmk80xx_private *devpriv = dev->private;
c647ed56
AH
643 unsigned char *rx_buf, *tx_buf;
644 int dir, reg, cmd;
645 int retval;
646
c647ed56
AH
647 dir = 0;
648
649 if (data[0])
650 dir |= DIR_OUT;
651
fc9ca48e 652 if (devpriv->model == VMK8061_MODEL) {
c647ed56 653 dir |= DIR_IN;
fc9ca48e
PH
654 reg = VMK8061_DO_REG;
655 cmd = VMK8061_CMD_DO;
656 } else { /* VMK8055_MODEL */
657 reg = VMK8055_DO_REG;
658 cmd = VMK8055_CMD_WRT_AD;
659 }
c647ed56 660
da7b18ee 661 retval = rudimentary_check(devpriv, dir);
c647ed56
AH
662 if (retval)
663 return retval;
664
da7b18ee 665 down(&devpriv->limit_sem);
c647ed56 666
da7b18ee
HS
667 rx_buf = devpriv->usb_rx_buf;
668 tx_buf = devpriv->usb_tx_buf;
c647ed56
AH
669
670 if (data[0]) {
c647ed56
AH
671 tx_buf[reg] &= ~data[0];
672 tx_buf[reg] |= (data[0] & data[1]);
673
da7b18ee 674 retval = vmk80xx_write_packet(devpriv, cmd);
c647ed56
AH
675
676 if (retval)
677 goto out;
678 }
679
52d895d3 680 if (devpriv->model == VMK8061_MODEL) {
c647ed56
AH
681 tx_buf[0] = VMK8061_CMD_RD_DO;
682
da7b18ee 683 retval = vmk80xx_read_packet(devpriv);
c647ed56
AH
684
685 if (!retval) {
686 data[1] = rx_buf[reg];
687 retval = 2;
688 }
689 } else {
690 data[1] = tx_buf[reg];
691 retval = 2;
692 }
693
694out:
da7b18ee 695 up(&devpriv->limit_sem);
c647ed56
AH
696
697 return retval;
698}
699
75a45d92
HS
700static int vmk80xx_cnt_insn_read(struct comedi_device *dev,
701 struct comedi_subdevice *s,
702 struct comedi_insn *insn,
703 unsigned int *data)
985cafcc 704{
da7b18ee 705 struct vmk80xx_private *devpriv = dev->private;
3a229fd5
AH
706 int chan;
707 int reg[2];
985cafcc
MG
708 int n;
709
da7b18ee 710 n = rudimentary_check(devpriv, DIR_IN);
587e500c
AH
711 if (n)
712 return n;
3faad673 713
da7b18ee 714 down(&devpriv->limit_sem);
985cafcc 715 chan = CR_CHAN(insn->chanspec);
3faad673 716
52d895d3 717 switch (devpriv->model) {
985cafcc
MG
718 case VMK8055_MODEL:
719 if (!chan)
720 reg[0] = VMK8055_CNT1_REG;
721 else
722 reg[0] = VMK8055_CNT2_REG;
723 break;
724 case VMK8061_MODEL:
13f7952f 725 default:
985cafcc
MG
726 reg[0] = VMK8061_CNT_REG;
727 reg[1] = VMK8061_CNT_REG;
da7b18ee 728 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_CNT;
985cafcc 729 break;
3faad673
MG
730 }
731
985cafcc 732 for (n = 0; n < insn->n; n++) {
da7b18ee 733 if (vmk80xx_read_packet(devpriv))
985cafcc 734 break;
3faad673 735
52d895d3 736 if (devpriv->model == VMK8055_MODEL)
da7b18ee 737 data[n] = devpriv->usb_rx_buf[reg[0]];
3a229fd5 738 else /* VMK8061_MODEL */
da7b18ee
HS
739 data[n] = devpriv->usb_rx_buf[reg[0] * (chan + 1) + 1]
740 + 256 * devpriv->usb_rx_buf[reg[1] * 2 + 2];
985cafcc
MG
741 }
742
da7b18ee 743 up(&devpriv->limit_sem);
985cafcc
MG
744
745 return n;
3faad673
MG
746}
747
75a45d92
HS
748static int vmk80xx_cnt_insn_config(struct comedi_device *dev,
749 struct comedi_subdevice *s,
750 struct comedi_insn *insn,
751 unsigned int *data)
3faad673 752{
da7b18ee 753 struct vmk80xx_private *devpriv = dev->private;
985cafcc 754 unsigned int insn_cmd;
3a229fd5
AH
755 int chan;
756 int cmd;
757 int reg;
985cafcc 758 int n;
3faad673 759
da7b18ee 760 n = rudimentary_check(devpriv, DIR_OUT);
587e500c
AH
761 if (n)
762 return n;
3faad673 763
985cafcc
MG
764 insn_cmd = data[0];
765 if (insn_cmd != INSN_CONFIG_RESET && insn_cmd != GPCT_RESET)
766 return -EINVAL;
3faad673 767
da7b18ee 768 down(&devpriv->limit_sem);
8f9064a8 769
985cafcc 770 chan = CR_CHAN(insn->chanspec);
3faad673 771
52d895d3 772 if (devpriv->model == VMK8055_MODEL) {
985cafcc
MG
773 if (!chan) {
774 cmd = VMK8055_CMD_RST_CNT1;
775 reg = VMK8055_CNT1_REG;
776 } else {
777 cmd = VMK8055_CMD_RST_CNT2;
778 reg = VMK8055_CNT2_REG;
779 }
780
da7b18ee 781 devpriv->usb_tx_buf[reg] = 0x00;
3a229fd5 782 } else {
985cafcc 783 cmd = VMK8061_CMD_RST_CNT;
3a229fd5 784 }
985cafcc
MG
785
786 for (n = 0; n < insn->n; n++)
da7b18ee 787 if (vmk80xx_write_packet(devpriv, cmd))
985cafcc
MG
788 break;
789
da7b18ee 790 up(&devpriv->limit_sem);
985cafcc
MG
791
792 return n;
793}
794
75a45d92
HS
795static int vmk80xx_cnt_insn_write(struct comedi_device *dev,
796 struct comedi_subdevice *s,
797 struct comedi_insn *insn,
798 unsigned int *data)
985cafcc 799{
da7b18ee 800 struct vmk80xx_private *devpriv = dev->private;
3a229fd5
AH
801 unsigned long debtime;
802 unsigned long val;
803 int chan;
804 int cmd;
985cafcc
MG
805 int n;
806
da7b18ee 807 n = rudimentary_check(devpriv, DIR_OUT);
587e500c
AH
808 if (n)
809 return n;
985cafcc 810
da7b18ee 811 down(&devpriv->limit_sem);
985cafcc
MG
812 chan = CR_CHAN(insn->chanspec);
813
814 if (!chan)
815 cmd = VMK8055_CMD_DEB1_TIME;
816 else
817 cmd = VMK8055_CMD_DEB2_TIME;
818
819 for (n = 0; n < insn->n; n++) {
820 debtime = data[n];
3faad673
MG
821 if (debtime == 0)
822 debtime = 1;
985cafcc
MG
823
824 /* TODO: Prevent overflows */
825 if (debtime > 7450)
826 debtime = 7450;
827
3faad673
MG
828 val = int_sqrt(debtime * 1000 / 115);
829 if (((val + 1) * val) < debtime * 1000 / 115)
830 val += 1;
831
da7b18ee 832 devpriv->usb_tx_buf[6 + chan] = val;
3faad673 833
da7b18ee 834 if (vmk80xx_write_packet(devpriv, cmd))
985cafcc 835 break;
3faad673
MG
836 }
837
da7b18ee 838 up(&devpriv->limit_sem);
3faad673 839
985cafcc
MG
840 return n;
841}
3faad673 842
9a23a748
HS
843static int vmk80xx_pwm_insn_read(struct comedi_device *dev,
844 struct comedi_subdevice *s,
845 struct comedi_insn *insn,
846 unsigned int *data)
985cafcc 847{
da7b18ee
HS
848 struct vmk80xx_private *devpriv = dev->private;
849 unsigned char *tx_buf;
850 unsigned char *rx_buf;
985cafcc
MG
851 int reg[2];
852 int n;
853
da7b18ee 854 n = rudimentary_check(devpriv, DIR_IN);
587e500c
AH
855 if (n)
856 return n;
985cafcc 857
da7b18ee
HS
858 down(&devpriv->limit_sem);
859
860 tx_buf = devpriv->usb_tx_buf;
861 rx_buf = devpriv->usb_rx_buf;
985cafcc
MG
862
863 reg[0] = VMK8061_PWM_REG1;
864 reg[1] = VMK8061_PWM_REG2;
865
da7b18ee 866 tx_buf[0] = VMK8061_CMD_RD_PWM;
985cafcc
MG
867
868 for (n = 0; n < insn->n; n++) {
da7b18ee 869 if (vmk80xx_read_packet(devpriv))
985cafcc
MG
870 break;
871
da7b18ee 872 data[n] = rx_buf[reg[0]] + 4 * rx_buf[reg[1]];
985cafcc
MG
873 }
874
da7b18ee 875 up(&devpriv->limit_sem);
985cafcc
MG
876
877 return n;
3faad673
MG
878}
879
9a23a748
HS
880static int vmk80xx_pwm_insn_write(struct comedi_device *dev,
881 struct comedi_subdevice *s,
882 struct comedi_insn *insn,
883 unsigned int *data)
985cafcc 884{
da7b18ee 885 struct vmk80xx_private *devpriv = dev->private;
985cafcc 886 unsigned char *tx_buf;
3a229fd5
AH
887 int reg[2];
888 int cmd;
985cafcc 889 int n;
3faad673 890
da7b18ee 891 n = rudimentary_check(devpriv, DIR_OUT);
587e500c
AH
892 if (n)
893 return n;
985cafcc 894
da7b18ee 895 down(&devpriv->limit_sem);
985cafcc 896
da7b18ee 897 tx_buf = devpriv->usb_tx_buf;
985cafcc
MG
898
899 reg[0] = VMK8061_PWM_REG1;
900 reg[1] = VMK8061_PWM_REG2;
901
902 cmd = VMK8061_CMD_OUT_PWM;
903
904 /*
905 * The followin piece of code was translated from the inline
906 * assembler code in the DLL source code.
907 *
908 * asm
909 * mov eax, k ; k is the value (data[n])
910 * and al, 03h ; al are the lower 8 bits of eax
911 * mov lo, al ; lo is the low part (tx_buf[reg[0]])
912 * mov eax, k
913 * shr eax, 2 ; right shift eax register by 2
914 * mov hi, al ; hi is the high part (tx_buf[reg[1]])
915 * end;
916 */
917 for (n = 0; n < insn->n; n++) {
918 tx_buf[reg[0]] = (unsigned char)(data[n] & 0x03);
919 tx_buf[reg[1]] = (unsigned char)(data[n] >> 2) & 0xff;
920
da7b18ee 921 if (vmk80xx_write_packet(devpriv, cmd))
985cafcc
MG
922 break;
923 }
3faad673 924
da7b18ee 925 up(&devpriv->limit_sem);
985cafcc
MG
926
927 return n;
928}
929
57cf09ae 930static int vmk80xx_find_usb_endpoints(struct comedi_device *dev)
49253d54 931{
57cf09ae
HS
932 struct vmk80xx_private *devpriv = dev->private;
933 struct usb_interface *intf = devpriv->intf;
49253d54
HS
934 struct usb_host_interface *iface_desc = intf->cur_altsetting;
935 struct usb_endpoint_descriptor *ep_desc;
936 int i;
937
938 if (iface_desc->desc.bNumEndpoints != 2)
939 return -ENODEV;
940
941 for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
942 ep_desc = &iface_desc->endpoint[i].desc;
943
944 if (usb_endpoint_is_int_in(ep_desc) ||
945 usb_endpoint_is_bulk_in(ep_desc)) {
946 if (!devpriv->ep_rx)
947 devpriv->ep_rx = ep_desc;
948 continue;
949 }
950
951 if (usb_endpoint_is_int_out(ep_desc) ||
952 usb_endpoint_is_bulk_out(ep_desc)) {
953 if (!devpriv->ep_tx)
954 devpriv->ep_tx = ep_desc;
955 continue;
956 }
957 }
958
959 if (!devpriv->ep_rx || !devpriv->ep_tx)
960 return -ENODEV;
961
962 return 0;
963}
964
57cf09ae 965static int vmk80xx_alloc_usb_buffers(struct comedi_device *dev)
78f8fa7f 966{
57cf09ae 967 struct vmk80xx_private *devpriv = dev->private;
78f8fa7f
HS
968 size_t size;
969
970 size = le16_to_cpu(devpriv->ep_rx->wMaxPacketSize);
0cbfc826 971 devpriv->usb_rx_buf = kzalloc(size, GFP_KERNEL);
78f8fa7f
HS
972 if (!devpriv->usb_rx_buf)
973 return -ENOMEM;
974
975 size = le16_to_cpu(devpriv->ep_tx->wMaxPacketSize);
0cbfc826 976 devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL);
78f8fa7f
HS
977 if (!devpriv->usb_tx_buf) {
978 kfree(devpriv->usb_rx_buf);
979 return -ENOMEM;
980 }
981
982 return 0;
983}
984
66dbc7b1 985static int vmk80xx_init_subdevices(struct comedi_device *dev)
3faad673 986{
57cf09ae
HS
987 const struct vmk80xx_board *boardinfo = comedi_board(dev);
988 struct vmk80xx_private *devpriv = dev->private;
b153d83e 989 struct comedi_subdevice *s;
57cf09ae 990 int n_subd;
8b6c5694 991 int ret;
3faad673 992
da7b18ee 993 down(&devpriv->limit_sem);
0dd772bf 994
52d895d3 995 if (devpriv->model == VMK8055_MODEL)
985cafcc
MG
996 n_subd = 5;
997 else
998 n_subd = 6;
da7b18ee 999 ret = comedi_alloc_subdevices(dev, n_subd);
8b6c5694 1000 if (ret) {
da7b18ee 1001 up(&devpriv->limit_sem);
8b6c5694 1002 return ret;
3faad673 1003 }
0dd772bf 1004
985cafcc 1005 /* Analog input subdevice */
da7b18ee 1006 s = &dev->subdevices[0];
658cd3ac
HS
1007 s->type = COMEDI_SUBD_AI;
1008 s->subdev_flags = SDF_READABLE | SDF_GROUND;
1009 s->n_chan = boardinfo->ai_nchans;
1010 s->maxdata = boardinfo->ai_maxdata;
1011 s->range_table = boardinfo->range;
1012 s->insn_read = vmk80xx_ai_insn_read;
0dd772bf 1013
985cafcc 1014 /* Analog output subdevice */
da7b18ee 1015 s = &dev->subdevices[1];
8b3ec9f1
HS
1016 s->type = COMEDI_SUBD_AO;
1017 s->subdev_flags = SDF_WRITEABLE | SDF_GROUND;
1018 s->n_chan = boardinfo->ao_nchans;
1019 s->maxdata = 0x00ff;
1020 s->range_table = boardinfo->range;
1021 s->insn_write = vmk80xx_ao_insn_write;
52d895d3 1022 if (devpriv->model == VMK8061_MODEL) {
8b3ec9f1
HS
1023 s->subdev_flags |= SDF_READABLE;
1024 s->insn_read = vmk80xx_ao_insn_read;
985cafcc 1025 }
0dd772bf 1026
985cafcc 1027 /* Digital input subdevice */
da7b18ee 1028 s = &dev->subdevices[2];
268e5148
HS
1029 s->type = COMEDI_SUBD_DI;
1030 s->subdev_flags = SDF_READABLE;
1031 s->n_chan = boardinfo->di_nchans;
1032 s->maxdata = 1;
1033 s->range_table = &range_digital;
268e5148 1034 s->insn_bits = vmk80xx_di_insn_bits;
0dd772bf 1035
985cafcc 1036 /* Digital output subdevice */
da7b18ee 1037 s = &dev->subdevices[3];
b639e096
HS
1038 s->type = COMEDI_SUBD_DO;
1039 s->subdev_flags = SDF_WRITEABLE;
1040 s->n_chan = 8;
1041 s->maxdata = 1;
1042 s->range_table = &range_digital;
b639e096 1043 s->insn_bits = vmk80xx_do_insn_bits;
0dd772bf 1044
985cafcc 1045 /* Counter subdevice */
da7b18ee 1046 s = &dev->subdevices[4];
75a45d92
HS
1047 s->type = COMEDI_SUBD_COUNTER;
1048 s->subdev_flags = SDF_READABLE;
1049 s->n_chan = 2;
1050 s->maxdata = boardinfo->cnt_maxdata;
1051 s->insn_read = vmk80xx_cnt_insn_read;
1052 s->insn_config = vmk80xx_cnt_insn_config;
52d895d3 1053 if (devpriv->model == VMK8055_MODEL) {
75a45d92
HS
1054 s->subdev_flags |= SDF_WRITEABLE;
1055 s->insn_write = vmk80xx_cnt_insn_write;
985cafcc 1056 }
0dd772bf 1057
985cafcc 1058 /* PWM subdevice */
52d895d3 1059 if (devpriv->model == VMK8061_MODEL) {
da7b18ee 1060 s = &dev->subdevices[5];
9a23a748
HS
1061 s->type = COMEDI_SUBD_PWM;
1062 s->subdev_flags = SDF_READABLE | SDF_WRITEABLE;
1063 s->n_chan = boardinfo->pwm_nchans;
1064 s->maxdata = boardinfo->pwm_maxdata;
1065 s->insn_read = vmk80xx_pwm_insn_read;
1066 s->insn_write = vmk80xx_pwm_insn_write;
985cafcc 1067 }
0dd772bf 1068
da7b18ee 1069 up(&devpriv->limit_sem);
0dd772bf 1070
f7d4d3bc
IA
1071 return 0;
1072}
3faad673 1073
da7b18ee 1074static int vmk80xx_auto_attach(struct comedi_device *dev,
57cf09ae 1075 unsigned long context)
f7d4d3bc 1076{
da7b18ee 1077 struct usb_interface *intf = comedi_to_usb_interface(dev);
0dd772bf 1078 const struct vmk80xx_board *boardinfo;
da7b18ee 1079 struct vmk80xx_private *devpriv;
49253d54 1080 int ret;
3faad673 1081
57cf09ae
HS
1082 boardinfo = &vmk80xx_boardinfo[context];
1083 dev->board_ptr = boardinfo;
1084 dev->board_name = boardinfo->name;
3faad673 1085
57cf09ae
HS
1086 devpriv = kzalloc(sizeof(*devpriv), GFP_KERNEL);
1087 if (!devpriv)
1088 return -ENOMEM;
1089 dev->private = devpriv;
3faad673 1090
57cf09ae
HS
1091 devpriv->usb = interface_to_usbdev(intf);
1092 devpriv->intf = intf;
52d895d3 1093 devpriv->model = boardinfo->model;
985cafcc 1094
57cf09ae 1095 ret = vmk80xx_find_usb_endpoints(dev);
db7dabf7 1096 if (ret)
57cf09ae 1097 return ret;
3faad673 1098
57cf09ae 1099 ret = vmk80xx_alloc_usb_buffers(dev);
db7dabf7 1100 if (ret)
57cf09ae 1101 return ret;
985cafcc 1102
da7b18ee
HS
1103 sema_init(&devpriv->limit_sem, 8);
1104 init_waitqueue_head(&devpriv->read_wait);
1105 init_waitqueue_head(&devpriv->write_wait);
985cafcc 1106
da7b18ee
HS
1107 init_usb_anchor(&devpriv->rx_anchor);
1108 init_usb_anchor(&devpriv->tx_anchor);
985cafcc 1109
da7b18ee 1110 usb_set_intfdata(intf, devpriv);
985cafcc 1111
52d895d3 1112 if (devpriv->model == VMK8061_MODEL) {
da7b18ee
HS
1113 vmk80xx_read_eeprom(devpriv, IC3_VERSION);
1114 dev_info(&intf->dev, "%s\n", devpriv->fw.ic3_vers);
985cafcc 1115
da7b18ee
HS
1116 if (vmk80xx_check_data_link(devpriv)) {
1117 vmk80xx_read_eeprom(devpriv, IC6_VERSION);
1118 dev_info(&intf->dev, "%s\n", devpriv->fw.ic6_vers);
3a229fd5 1119 }
3faad673
MG
1120 }
1121
52d895d3 1122 if (devpriv->model == VMK8055_MODEL)
da7b18ee 1123 vmk80xx_reset_device(devpriv);
3faad673 1124
66dbc7b1 1125 return vmk80xx_init_subdevices(dev);
57cf09ae 1126}
3faad673 1127
57cf09ae
HS
1128static void vmk80xx_detach(struct comedi_device *dev)
1129{
1130 struct vmk80xx_private *devpriv = dev->private;
8ba69ce4 1131
57cf09ae
HS
1132 if (!devpriv)
1133 return;
db7dabf7 1134
57cf09ae
HS
1135 down(&devpriv->limit_sem);
1136
1137 usb_set_intfdata(devpriv->intf, NULL);
1138
1139 usb_kill_anchored_urbs(&devpriv->rx_anchor);
1140 usb_kill_anchored_urbs(&devpriv->tx_anchor);
1141
1142 kfree(devpriv->usb_rx_buf);
1143 kfree(devpriv->usb_tx_buf);
1144
1145 up(&devpriv->limit_sem);
1146}
1147
1148static struct comedi_driver vmk80xx_driver = {
1149 .module = THIS_MODULE,
1150 .driver_name = "vmk80xx",
1151 .auto_attach = vmk80xx_auto_attach,
1152 .detach = vmk80xx_detach,
1153};
1154
1155static int vmk80xx_usb_probe(struct usb_interface *intf,
1156 const struct usb_device_id *id)
1157{
1158 return comedi_usb_auto_config(intf, &vmk80xx_driver, id->driver_info);
3faad673
MG
1159}
1160
007ff2af
HS
1161static const struct usb_device_id vmk80xx_usb_id_table[] = {
1162 { USB_DEVICE(0x10cf, 0x5500), .driver_info = DEVICE_VMK8055 },
1163 { USB_DEVICE(0x10cf, 0x5501), .driver_info = DEVICE_VMK8055 },
1164 { USB_DEVICE(0x10cf, 0x5502), .driver_info = DEVICE_VMK8055 },
1165 { USB_DEVICE(0x10cf, 0x5503), .driver_info = DEVICE_VMK8055 },
1166 { USB_DEVICE(0x10cf, 0x8061), .driver_info = DEVICE_VMK8061 },
1167 { USB_DEVICE(0x10cf, 0x8062), .driver_info = DEVICE_VMK8061 },
1168 { USB_DEVICE(0x10cf, 0x8063), .driver_info = DEVICE_VMK8061 },
1169 { USB_DEVICE(0x10cf, 0x8064), .driver_info = DEVICE_VMK8061 },
1170 { USB_DEVICE(0x10cf, 0x8065), .driver_info = DEVICE_VMK8061 },
1171 { USB_DEVICE(0x10cf, 0x8066), .driver_info = DEVICE_VMK8061 },
1172 { USB_DEVICE(0x10cf, 0x8067), .driver_info = DEVICE_VMK8061 },
1173 { USB_DEVICE(0x10cf, 0x8068), .driver_info = DEVICE_VMK8061 },
1174 { }
1175};
1176MODULE_DEVICE_TABLE(usb, vmk80xx_usb_id_table);
1177
d6cc3ec8
HS
1178static struct usb_driver vmk80xx_usb_driver = {
1179 .name = "vmk80xx",
007ff2af 1180 .id_table = vmk80xx_usb_id_table,
ce874227
HS
1181 .probe = vmk80xx_usb_probe,
1182 .disconnect = comedi_usb_auto_unconfig,
3faad673 1183};
d6cc3ec8 1184module_comedi_usb_driver(vmk80xx_driver, vmk80xx_usb_driver);
007ff2af
HS
1185
1186MODULE_AUTHOR("Manuel Gebele <forensixs@gmx.de>");
1187MODULE_DESCRIPTION("Velleman USB Board Low-Level Driver");
1188MODULE_SUPPORTED_DEVICE("K8055/K8061 aka VM110/VM140");
1189MODULE_VERSION("0.8.01");
1190MODULE_LICENSE("GPL");