staging: comedi: vmk80xx: factor out usb endpoint detection
[deliverable/linux.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*/
41/*
42Changelog:
43
440.8.81 -3- code completely rewritten (adjust driver logic)
450.8.81 -2- full support for K8061
460.8.81 -1- fix some mistaken among others the number of
47 supported boards and I/O handling
48
490.7.76 -4- renamed to vmk80xx
500.7.76 -3- detect K8061 (only theoretically supported)
510.7.76 -2- code completely rewritten (adjust driver logic)
520.7.76 -1- support for digital and counter subdevice
3faad673
MG
53*/
54
55#include <linux/kernel.h>
3faad673
MG
56#include <linux/module.h>
57#include <linux/mutex.h>
58#include <linux/errno.h>
59#include <linux/input.h>
60#include <linux/slab.h>
61#include <linux/poll.h>
62#include <linux/usb.h>
985cafcc
MG
63#include <linux/uaccess.h>
64
65#include "../comedidev.h"
66
985cafcc
MG
67enum {
68 DEVICE_VMK8055,
69 DEVICE_VMK8061
70};
71
985cafcc
MG
72#define VMK8055_DI_REG 0x00
73#define VMK8055_DO_REG 0x01
74#define VMK8055_AO1_REG 0x02
75#define VMK8055_AO2_REG 0x03
76#define VMK8055_AI1_REG 0x02
77#define VMK8055_AI2_REG 0x03
78#define VMK8055_CNT1_REG 0x04
79#define VMK8055_CNT2_REG 0x06
80
81#define VMK8061_CH_REG 0x01
82#define VMK8061_DI_REG 0x01
83#define VMK8061_DO_REG 0x01
84#define VMK8061_PWM_REG1 0x01
85#define VMK8061_PWM_REG2 0x02
86#define VMK8061_CNT_REG 0x02
87#define VMK8061_AO_REG 0x02
88#define VMK8061_AI_REG1 0x02
89#define VMK8061_AI_REG2 0x03
90
91#define VMK8055_CMD_RST 0x00
92#define VMK8055_CMD_DEB1_TIME 0x01
93#define VMK8055_CMD_DEB2_TIME 0x02
94#define VMK8055_CMD_RST_CNT1 0x03
95#define VMK8055_CMD_RST_CNT2 0x04
96#define VMK8055_CMD_WRT_AD 0x05
97
98#define VMK8061_CMD_RD_AI 0x00
0a85b6f0 99#define VMK8061_CMR_RD_ALL_AI 0x01 /* !non-active! */
985cafcc 100#define VMK8061_CMD_SET_AO 0x02
0a85b6f0 101#define VMK8061_CMD_SET_ALL_AO 0x03 /* !non-active! */
985cafcc
MG
102#define VMK8061_CMD_OUT_PWM 0x04
103#define VMK8061_CMD_RD_DI 0x05
0a85b6f0 104#define VMK8061_CMD_DO 0x06 /* !non-active! */
985cafcc
MG
105#define VMK8061_CMD_CLR_DO 0x07
106#define VMK8061_CMD_SET_DO 0x08
0a85b6f0
MT
107#define VMK8061_CMD_RD_CNT 0x09 /* TODO: completely pointless? */
108#define VMK8061_CMD_RST_CNT 0x0a /* TODO: completely pointless? */
109#define VMK8061_CMD_RD_VERSION 0x0b /* internal usage */
110#define VMK8061_CMD_RD_JMP_STAT 0x0c /* TODO: not implemented yet */
111#define VMK8061_CMD_RD_PWR_STAT 0x0d /* internal usage */
985cafcc
MG
112#define VMK8061_CMD_RD_DO 0x0e
113#define VMK8061_CMD_RD_AO 0x0f
114#define VMK8061_CMD_RD_PWM 0x10
115
116#define VMK80XX_MAX_BOARDS COMEDI_NUM_BOARD_MINORS
117
118#define TRANS_OUT_BUSY 1
119#define TRANS_IN_BUSY 2
120#define TRANS_IN_RUNNING 3
121
122#define IC3_VERSION (1 << 0)
123#define IC6_VERSION (1 << 1)
124
125#define URB_RCV_FLAG (1 << 0)
126#define URB_SND_FLAG (1 << 1)
3faad673 127
3faad673 128#ifdef CONFIG_COMEDI_DEBUG
0a85b6f0 129static int dbgcm = 1;
985cafcc 130#else
0a85b6f0 131static int dbgcm;
985cafcc
MG
132#endif
133
985cafcc
MG
134#define dbgcm(fmt, arg...) \
135do { \
136 if (dbgcm) \
137 printk(KERN_DEBUG fmt, ##arg); \
138} while (0)
139
140enum vmk80xx_model {
141 VMK8055_MODEL,
142 VMK8061_MODEL
143};
3faad673 144
985cafcc 145struct firmware_version {
0a85b6f0
MT
146 unsigned char ic3_vers[32]; /* USB-Controller */
147 unsigned char ic6_vers[32]; /* CPU */
3faad673
MG
148};
149
985cafcc 150static const struct comedi_lrange vmk8055_range = {
0a85b6f0 151 1, {UNI_RANGE(5)}
985cafcc 152};
3faad673 153
985cafcc 154static const struct comedi_lrange vmk8061_range = {
0a85b6f0 155 2, {UNI_RANGE(5), UNI_RANGE(10)}
985cafcc 156};
3faad673 157
985cafcc
MG
158struct vmk80xx_board {
159 const char *name;
160 enum vmk80xx_model model;
161 const struct comedi_lrange *range;
0a85b6f0 162 __u8 ai_chans;
985cafcc 163 __le16 ai_bits;
0a85b6f0 164 __u8 ao_chans;
0a85b6f0 165 __u8 di_chans;
985cafcc 166 __le16 cnt_bits;
0a85b6f0 167 __u8 pwm_chans;
985cafcc
MG
168 __le16 pwm_bits;
169};
3faad673 170
20d60077
HS
171static const struct vmk80xx_board vmk80xx_boardinfo[] = {
172 [DEVICE_VMK8055] = {
173 .name = "K8055 (VM110)",
174 .model = VMK8055_MODEL,
175 .range = &vmk8055_range,
176 .ai_chans = 2,
177 .ai_bits = 8,
178 .ao_chans = 2,
20d60077 179 .di_chans = 6,
20d60077
HS
180 .cnt_bits = 16,
181 .pwm_chans = 0,
182 .pwm_bits = 0,
183 },
184 [DEVICE_VMK8061] = {
185 .name = "K8061 (VM140)",
186 .model = VMK8061_MODEL,
187 .range = &vmk8061_range,
188 .ai_chans = 8,
189 .ai_bits = 10,
190 .ao_chans = 8,
20d60077 191 .di_chans = 8,
20d60077
HS
192 .cnt_bits = 0,
193 .pwm_chans = 1,
194 .pwm_bits = 10,
195 },
196};
197
dc49cbfc 198struct vmk80xx_private {
da7b18ee 199 struct usb_device *usb;
985cafcc
MG
200 struct usb_interface *intf;
201 struct usb_endpoint_descriptor *ep_rx;
202 struct usb_endpoint_descriptor *ep_tx;
203 struct usb_anchor rx_anchor;
204 struct usb_anchor tx_anchor;
20d60077 205 const struct vmk80xx_board *board;
985cafcc
MG
206 struct firmware_version fw;
207 struct semaphore limit_sem;
208 wait_queue_head_t read_wait;
209 wait_queue_head_t write_wait;
210 unsigned char *usb_rx_buf;
211 unsigned char *usb_tx_buf;
212 unsigned long flags;
213 int probed;
214 int attached;
215 int count;
216};
217
dc49cbfc 218static struct vmk80xx_private vmb[VMK80XX_MAX_BOARDS];
985cafcc
MG
219
220static DEFINE_MUTEX(glb_mutex);
221
222static void vmk80xx_tx_callback(struct urb *urb)
3faad673 223{
da7b18ee
HS
224 struct vmk80xx_private *devpriv = urb->context;
225 unsigned long *flags = &devpriv->flags;
985cafcc 226 int stat = urb->status;
3faad673 227
985cafcc 228 if (stat && !(stat == -ENOENT
0a85b6f0 229 || stat == -ECONNRESET || stat == -ESHUTDOWN))
985cafcc
MG
230 dbgcm("comedi#: vmk80xx: %s - nonzero urb status (%d)\n",
231 __func__, stat);
3faad673 232
da7b18ee 233 if (!test_bit(TRANS_OUT_BUSY, flags))
985cafcc 234 return;
3faad673 235
da7b18ee 236 clear_bit(TRANS_OUT_BUSY, flags);
3faad673 237
da7b18ee 238 wake_up_interruptible(&devpriv->write_wait);
3faad673
MG
239}
240
985cafcc 241static void vmk80xx_rx_callback(struct urb *urb)
3faad673 242{
da7b18ee
HS
243 struct vmk80xx_private *devpriv = urb->context;
244 unsigned long *flags = &devpriv->flags;
985cafcc 245 int stat = urb->status;
3faad673 246
985cafcc
MG
247 switch (stat) {
248 case 0:
3faad673
MG
249 break;
250 case -ENOENT:
251 case -ECONNRESET:
252 case -ESHUTDOWN:
253 break;
254 default:
985cafcc
MG
255 dbgcm("comedi#: vmk80xx: %s - nonzero urb status (%d)\n",
256 __func__, stat);
257 goto resubmit;
3faad673
MG
258 }
259
260 goto exit;
261resubmit:
da7b18ee
HS
262 if (test_bit(TRANS_IN_RUNNING, flags) && devpriv->intf) {
263 usb_anchor_urb(urb, &devpriv->rx_anchor);
985cafcc
MG
264
265 if (!usb_submit_urb(urb, GFP_KERNEL))
266 goto exit;
267
151373aa
GKH
268 dev_err(&urb->dev->dev,
269 "comedi#: vmk80xx: %s - submit urb failed\n",
270 __func__);
985cafcc
MG
271
272 usb_unanchor_urb(urb);
3faad673
MG
273 }
274exit:
da7b18ee 275 clear_bit(TRANS_IN_BUSY, flags);
3faad673 276
da7b18ee 277 wake_up_interruptible(&devpriv->read_wait);
3faad673
MG
278}
279
da7b18ee 280static int vmk80xx_check_data_link(struct vmk80xx_private *devpriv)
3faad673 281{
da7b18ee 282 struct usb_device *usb = devpriv->usb;
3a229fd5
AH
283 unsigned int tx_pipe;
284 unsigned int rx_pipe;
285 unsigned char tx[1];
286 unsigned char rx[2];
985cafcc 287
da7b18ee
HS
288 tx_pipe = usb_sndbulkpipe(usb, 0x01);
289 rx_pipe = usb_rcvbulkpipe(usb, 0x81);
3faad673 290
985cafcc 291 tx[0] = VMK8061_CMD_RD_PWR_STAT;
3faad673 292
3a229fd5
AH
293 /*
294 * Check that IC6 (PIC16F871) is powered and
985cafcc 295 * running and the data link between IC3 and
3a229fd5
AH
296 * IC6 is working properly
297 */
da7b18ee
HS
298 usb_bulk_msg(usb, tx_pipe, tx, 1, NULL, devpriv->ep_tx->bInterval);
299 usb_bulk_msg(usb, rx_pipe, rx, 2, NULL, HZ * 10);
3faad673 300
985cafcc 301 return (int)rx[1];
3faad673
MG
302}
303
da7b18ee 304static void vmk80xx_read_eeprom(struct vmk80xx_private *devpriv, int flag)
3faad673 305{
da7b18ee 306 struct usb_device *usb = devpriv->usb;
3a229fd5
AH
307 unsigned int tx_pipe;
308 unsigned int rx_pipe;
309 unsigned char tx[1];
310 unsigned char rx[64];
985cafcc 311 int cnt;
3faad673 312
da7b18ee
HS
313 tx_pipe = usb_sndbulkpipe(usb, 0x01);
314 rx_pipe = usb_rcvbulkpipe(usb, 0x81);
3faad673 315
985cafcc
MG
316 tx[0] = VMK8061_CMD_RD_VERSION;
317
3a229fd5
AH
318 /*
319 * Read the firmware version info of IC3 and
320 * IC6 from the internal EEPROM of the IC
321 */
da7b18ee
HS
322 usb_bulk_msg(usb, tx_pipe, tx, 1, NULL, devpriv->ep_tx->bInterval);
323 usb_bulk_msg(usb, rx_pipe, rx, 64, &cnt, HZ * 10);
985cafcc
MG
324
325 rx[cnt] = '\0';
326
327 if (flag & IC3_VERSION)
da7b18ee 328 strncpy(devpriv->fw.ic3_vers, rx + 1, 24);
0a85b6f0 329 else /* IC6_VERSION */
da7b18ee 330 strncpy(devpriv->fw.ic6_vers, rx + 25, 24);
985cafcc
MG
331}
332
da7b18ee 333static int vmk80xx_reset_device(struct vmk80xx_private *devpriv)
985cafcc 334{
da7b18ee
HS
335 struct usb_device *usb = devpriv->usb;
336 unsigned char *tx_buf = devpriv->usb_tx_buf;
985cafcc
MG
337 struct urb *urb;
338 unsigned int tx_pipe;
339 int ival;
340 size_t size;
341
985cafcc
MG
342 urb = usb_alloc_urb(0, GFP_KERNEL);
343 if (!urb)
344 return -ENOMEM;
345
da7b18ee 346 tx_pipe = usb_sndintpipe(usb, 0x01);
985cafcc 347
da7b18ee
HS
348 ival = devpriv->ep_tx->bInterval;
349 size = le16_to_cpu(devpriv->ep_tx->wMaxPacketSize);
985cafcc 350
da7b18ee
HS
351 tx_buf[0] = VMK8055_CMD_RST;
352 tx_buf[1] = 0x00;
353 tx_buf[2] = 0x00;
354 tx_buf[3] = 0x00;
355 tx_buf[4] = 0x00;
356 tx_buf[5] = 0x00;
357 tx_buf[6] = 0x00;
358 tx_buf[7] = 0x00;
985cafcc 359
da7b18ee
HS
360 usb_fill_int_urb(urb, usb, tx_pipe, tx_buf, size,
361 vmk80xx_tx_callback, devpriv, ival);
985cafcc 362
da7b18ee 363 usb_anchor_urb(urb, &devpriv->tx_anchor);
985cafcc
MG
364
365 return usb_submit_urb(urb, GFP_KERNEL);
366}
367
368static void vmk80xx_build_int_urb(struct urb *urb, int flag)
369{
da7b18ee
HS
370 struct vmk80xx_private *devpriv = urb->context;
371 struct usb_device *usb = devpriv->usb;
3a229fd5
AH
372 __u8 rx_addr;
373 __u8 tx_addr;
985cafcc
MG
374 unsigned int pipe;
375 unsigned char *buf;
376 size_t size;
0a85b6f0 377 void (*callback) (struct urb *);
985cafcc
MG
378 int ival;
379
985cafcc 380 if (flag & URB_RCV_FLAG) {
da7b18ee
HS
381 rx_addr = devpriv->ep_rx->bEndpointAddress;
382 pipe = usb_rcvintpipe(usb, rx_addr);
383 buf = devpriv->usb_rx_buf;
384 size = le16_to_cpu(devpriv->ep_rx->wMaxPacketSize);
985cafcc 385 callback = vmk80xx_rx_callback;
da7b18ee 386 ival = devpriv->ep_rx->bInterval;
0a85b6f0 387 } else { /* URB_SND_FLAG */
da7b18ee
HS
388 tx_addr = devpriv->ep_tx->bEndpointAddress;
389 pipe = usb_sndintpipe(usb, tx_addr);
390 buf = devpriv->usb_tx_buf;
391 size = le16_to_cpu(devpriv->ep_tx->wMaxPacketSize);
985cafcc 392 callback = vmk80xx_tx_callback;
da7b18ee 393 ival = devpriv->ep_tx->bInterval;
3faad673
MG
394 }
395
da7b18ee 396 usb_fill_int_urb(urb, usb, pipe, buf, size, callback, devpriv, ival);
985cafcc 397}
3faad673 398
da7b18ee 399static void vmk80xx_do_bulk_msg(struct vmk80xx_private *devpriv)
985cafcc 400{
da7b18ee
HS
401 struct usb_device *usb = devpriv->usb;
402 unsigned long *flags = &devpriv->flags;
3a229fd5
AH
403 __u8 tx_addr;
404 __u8 rx_addr;
405 unsigned int tx_pipe;
406 unsigned int rx_pipe;
985cafcc 407 size_t size;
3faad673 408
da7b18ee
HS
409 set_bit(TRANS_IN_BUSY, flags);
410 set_bit(TRANS_OUT_BUSY, flags);
3faad673 411
da7b18ee
HS
412 tx_addr = devpriv->ep_tx->bEndpointAddress;
413 rx_addr = devpriv->ep_rx->bEndpointAddress;
414 tx_pipe = usb_sndbulkpipe(usb, tx_addr);
415 rx_pipe = usb_rcvbulkpipe(usb, rx_addr);
985cafcc 416
3a229fd5
AH
417 /*
418 * The max packet size attributes of the K8061
419 * input/output endpoints are identical
420 */
da7b18ee 421 size = le16_to_cpu(devpriv->ep_tx->wMaxPacketSize);
985cafcc 422
da7b18ee
HS
423 usb_bulk_msg(usb, tx_pipe, devpriv->usb_tx_buf,
424 size, NULL, devpriv->ep_tx->bInterval);
425 usb_bulk_msg(usb, rx_pipe, devpriv->usb_rx_buf, size, NULL, HZ * 10);
985cafcc 426
da7b18ee
HS
427 clear_bit(TRANS_OUT_BUSY, flags);
428 clear_bit(TRANS_IN_BUSY, flags);
3faad673
MG
429}
430
da7b18ee 431static int vmk80xx_read_packet(struct vmk80xx_private *devpriv)
3faad673 432{
da7b18ee
HS
433 const struct vmk80xx_board *boardinfo = devpriv->board;
434 unsigned long *flags = &devpriv->flags;
985cafcc
MG
435 struct urb *urb;
436 int retval;
3faad673 437
da7b18ee 438 if (!devpriv->intf)
985cafcc 439 return -ENODEV;
3faad673 440
985cafcc 441 /* Only useful for interrupt transfers */
da7b18ee
HS
442 if (test_bit(TRANS_IN_BUSY, flags))
443 if (wait_event_interruptible(devpriv->read_wait,
444 !test_bit(TRANS_IN_BUSY, flags)))
985cafcc
MG
445 return -ERESTART;
446
0dd772bf 447 if (boardinfo->model == VMK8061_MODEL) {
da7b18ee 448 vmk80xx_do_bulk_msg(devpriv);
985cafcc
MG
449
450 return 0;
3faad673
MG
451 }
452
985cafcc
MG
453 urb = usb_alloc_urb(0, GFP_KERNEL);
454 if (!urb)
455 return -ENOMEM;
3faad673 456
da7b18ee 457 urb->context = devpriv;
985cafcc 458 vmk80xx_build_int_urb(urb, URB_RCV_FLAG);
3faad673 459
da7b18ee
HS
460 set_bit(TRANS_IN_RUNNING, flags);
461 set_bit(TRANS_IN_BUSY, flags);
3faad673 462
da7b18ee 463 usb_anchor_urb(urb, &devpriv->rx_anchor);
3faad673 464
985cafcc
MG
465 retval = usb_submit_urb(urb, GFP_KERNEL);
466 if (!retval)
467 goto exit;
3faad673 468
da7b18ee 469 clear_bit(TRANS_IN_RUNNING, flags);
985cafcc 470 usb_unanchor_urb(urb);
3faad673
MG
471
472exit:
985cafcc
MG
473 usb_free_urb(urb);
474
3faad673
MG
475 return retval;
476}
477
da7b18ee 478static int vmk80xx_write_packet(struct vmk80xx_private *devpriv, int cmd)
3faad673 479{
da7b18ee
HS
480 const struct vmk80xx_board *boardinfo = devpriv->board;
481 unsigned long *flags = &devpriv->flags;
985cafcc
MG
482 struct urb *urb;
483 int retval;
3faad673 484
da7b18ee 485 if (!devpriv->intf)
985cafcc 486 return -ENODEV;
3faad673 487
da7b18ee
HS
488 if (test_bit(TRANS_OUT_BUSY, flags))
489 if (wait_event_interruptible(devpriv->write_wait,
490 !test_bit(TRANS_OUT_BUSY, flags)))
985cafcc 491 return -ERESTART;
3faad673 492
0dd772bf 493 if (boardinfo->model == VMK8061_MODEL) {
da7b18ee
HS
494 devpriv->usb_tx_buf[0] = cmd;
495 vmk80xx_do_bulk_msg(devpriv);
3faad673 496
985cafcc 497 return 0;
3faad673
MG
498 }
499
985cafcc
MG
500 urb = usb_alloc_urb(0, GFP_KERNEL);
501 if (!urb)
502 return -ENOMEM;
503
da7b18ee 504 urb->context = devpriv;
985cafcc 505 vmk80xx_build_int_urb(urb, URB_SND_FLAG);
3faad673 506
da7b18ee 507 set_bit(TRANS_OUT_BUSY, flags);
3faad673 508
da7b18ee 509 usb_anchor_urb(urb, &devpriv->tx_anchor);
3faad673 510
da7b18ee 511 devpriv->usb_tx_buf[0] = cmd;
3faad673 512
985cafcc
MG
513 retval = usb_submit_urb(urb, GFP_KERNEL);
514 if (!retval)
515 goto exit;
516
da7b18ee 517 clear_bit(TRANS_OUT_BUSY, flags);
985cafcc
MG
518 usb_unanchor_urb(urb);
519
520exit:
521 usb_free_urb(urb);
3faad673
MG
522
523 return retval;
524}
525
985cafcc
MG
526#define DIR_IN 1
527#define DIR_OUT 2
528
da7b18ee 529static int rudimentary_check(struct vmk80xx_private *devpriv, int dir)
587e500c 530{
da7b18ee 531 if (!devpriv)
587e500c 532 return -EFAULT;
da7b18ee 533 if (!devpriv->probed)
587e500c 534 return -ENODEV;
da7b18ee 535 if (!devpriv->attached)
587e500c
AH
536 return -ENODEV;
537 if (dir & DIR_IN) {
da7b18ee 538 if (test_bit(TRANS_IN_BUSY, &devpriv->flags))
587e500c 539 return -EBUSY;
510b9be3
AH
540 }
541 if (dir & DIR_OUT) {
da7b18ee 542 if (test_bit(TRANS_OUT_BUSY, &devpriv->flags))
587e500c
AH
543 return -EBUSY;
544 }
545
546 return 0;
547}
985cafcc 548
da7b18ee 549static int vmk80xx_ai_rinsn(struct comedi_device *dev,
985cafcc
MG
550 struct comedi_subdevice *s,
551 struct comedi_insn *insn, unsigned int *data)
3faad673 552{
da7b18ee
HS
553 const struct vmk80xx_board *boardinfo = comedi_board(dev);
554 struct vmk80xx_private *devpriv = dev->private;
3a229fd5
AH
555 int chan;
556 int reg[2];
985cafcc 557 int n;
3faad673 558
da7b18ee 559 n = rudimentary_check(devpriv, DIR_IN);
587e500c
AH
560 if (n)
561 return n;
3faad673 562
da7b18ee 563 down(&devpriv->limit_sem);
985cafcc 564 chan = CR_CHAN(insn->chanspec);
3faad673 565
0dd772bf 566 switch (boardinfo->model) {
985cafcc
MG
567 case VMK8055_MODEL:
568 if (!chan)
569 reg[0] = VMK8055_AI1_REG;
570 else
571 reg[0] = VMK8055_AI2_REG;
572 break;
573 case VMK8061_MODEL:
13f7952f 574 default:
985cafcc
MG
575 reg[0] = VMK8061_AI_REG1;
576 reg[1] = VMK8061_AI_REG2;
da7b18ee
HS
577 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AI;
578 devpriv->usb_tx_buf[VMK8061_CH_REG] = chan;
985cafcc 579 break;
3faad673
MG
580 }
581
985cafcc 582 for (n = 0; n < insn->n; n++) {
da7b18ee 583 if (vmk80xx_read_packet(devpriv))
985cafcc 584 break;
3faad673 585
0dd772bf 586 if (boardinfo->model == VMK8055_MODEL) {
da7b18ee 587 data[n] = devpriv->usb_rx_buf[reg[0]];
985cafcc
MG
588 continue;
589 }
3faad673 590
985cafcc 591 /* VMK8061_MODEL */
da7b18ee
HS
592 data[n] = devpriv->usb_rx_buf[reg[0]] + 256 *
593 devpriv->usb_rx_buf[reg[1]];
985cafcc 594 }
3faad673 595
da7b18ee 596 up(&devpriv->limit_sem);
3faad673 597
985cafcc 598 return n;
3faad673
MG
599}
600
da7b18ee 601static int vmk80xx_ao_winsn(struct comedi_device *dev,
985cafcc
MG
602 struct comedi_subdevice *s,
603 struct comedi_insn *insn, unsigned int *data)
3faad673 604{
da7b18ee
HS
605 const struct vmk80xx_board *boardinfo = comedi_board(dev);
606 struct vmk80xx_private *devpriv = dev->private;
3a229fd5
AH
607 int chan;
608 int cmd;
609 int reg;
985cafcc 610 int n;
3faad673 611
da7b18ee 612 n = rudimentary_check(devpriv, DIR_OUT);
587e500c
AH
613 if (n)
614 return n;
3faad673 615
da7b18ee 616 down(&devpriv->limit_sem);
985cafcc 617 chan = CR_CHAN(insn->chanspec);
3faad673 618
0dd772bf 619 switch (boardinfo->model) {
985cafcc
MG
620 case VMK8055_MODEL:
621 cmd = VMK8055_CMD_WRT_AD;
622 if (!chan)
623 reg = VMK8055_AO1_REG;
624 else
625 reg = VMK8055_AO2_REG;
626 break;
0a85b6f0 627 default: /* NOTE: avoid compiler warnings */
985cafcc
MG
628 cmd = VMK8061_CMD_SET_AO;
629 reg = VMK8061_AO_REG;
da7b18ee 630 devpriv->usb_tx_buf[VMK8061_CH_REG] = chan;
985cafcc 631 break;
3faad673
MG
632 }
633
985cafcc 634 for (n = 0; n < insn->n; n++) {
da7b18ee 635 devpriv->usb_tx_buf[reg] = data[n];
3faad673 636
da7b18ee 637 if (vmk80xx_write_packet(devpriv, cmd))
985cafcc 638 break;
3faad673
MG
639 }
640
da7b18ee 641 up(&devpriv->limit_sem);
3faad673 642
985cafcc 643 return n;
3faad673
MG
644}
645
da7b18ee 646static int vmk80xx_ao_rinsn(struct comedi_device *dev,
985cafcc
MG
647 struct comedi_subdevice *s,
648 struct comedi_insn *insn, unsigned int *data)
3faad673 649{
da7b18ee 650 struct vmk80xx_private *devpriv = dev->private;
3a229fd5
AH
651 int chan;
652 int reg;
985cafcc 653 int n;
3faad673 654
da7b18ee 655 n = rudimentary_check(devpriv, DIR_IN);
587e500c
AH
656 if (n)
657 return n;
3faad673 658
da7b18ee 659 down(&devpriv->limit_sem);
985cafcc 660 chan = CR_CHAN(insn->chanspec);
3faad673 661
985cafcc 662 reg = VMK8061_AO_REG - 1;
3faad673 663
da7b18ee 664 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AO;
985cafcc
MG
665
666 for (n = 0; n < insn->n; n++) {
da7b18ee 667 if (vmk80xx_read_packet(devpriv))
985cafcc
MG
668 break;
669
da7b18ee 670 data[n] = devpriv->usb_rx_buf[reg + chan];
3faad673
MG
671 }
672
da7b18ee 673 up(&devpriv->limit_sem);
3faad673 674
985cafcc
MG
675 return n;
676}
3faad673 677
da7b18ee 678static int vmk80xx_di_bits(struct comedi_device *dev,
c647ed56
AH
679 struct comedi_subdevice *s,
680 struct comedi_insn *insn, unsigned int *data)
681{
da7b18ee
HS
682 const struct vmk80xx_board *boardinfo = comedi_board(dev);
683 struct vmk80xx_private *devpriv = dev->private;
c647ed56
AH
684 unsigned char *rx_buf;
685 int reg;
686 int retval;
687
da7b18ee 688 retval = rudimentary_check(devpriv, DIR_IN);
c647ed56
AH
689 if (retval)
690 return retval;
691
da7b18ee 692 down(&devpriv->limit_sem);
c647ed56 693
da7b18ee 694 rx_buf = devpriv->usb_rx_buf;
c647ed56 695
0dd772bf 696 if (boardinfo->model == VMK8061_MODEL) {
c647ed56 697 reg = VMK8061_DI_REG;
da7b18ee 698 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_DI;
c647ed56
AH
699 } else {
700 reg = VMK8055_DI_REG;
701 }
702
da7b18ee 703 retval = vmk80xx_read_packet(devpriv);
c647ed56
AH
704
705 if (!retval) {
0dd772bf 706 if (boardinfo->model == VMK8055_MODEL)
c647ed56
AH
707 data[1] = (((rx_buf[reg] >> 4) & 0x03) |
708 ((rx_buf[reg] << 2) & 0x04) |
709 ((rx_buf[reg] >> 3) & 0x18));
710 else
711 data[1] = rx_buf[reg];
712
713 retval = 2;
714 }
715
da7b18ee 716 up(&devpriv->limit_sem);
c647ed56
AH
717
718 return retval;
719}
720
da7b18ee 721static int vmk80xx_di_rinsn(struct comedi_device *dev,
985cafcc
MG
722 struct comedi_subdevice *s,
723 struct comedi_insn *insn, unsigned int *data)
724{
da7b18ee
HS
725 const struct vmk80xx_board *boardinfo = comedi_board(dev);
726 struct vmk80xx_private *devpriv = dev->private;
985cafcc
MG
727 int chan;
728 unsigned char *rx_buf;
3a229fd5
AH
729 int reg;
730 int inp;
985cafcc 731 int n;
3faad673 732
da7b18ee 733 n = rudimentary_check(devpriv, DIR_IN);
587e500c
AH
734 if (n)
735 return n;
3faad673 736
da7b18ee 737 down(&devpriv->limit_sem);
985cafcc
MG
738 chan = CR_CHAN(insn->chanspec);
739
da7b18ee 740 rx_buf = devpriv->usb_rx_buf;
985cafcc 741
0dd772bf 742 if (boardinfo->model == VMK8061_MODEL) {
985cafcc 743 reg = VMK8061_DI_REG;
da7b18ee 744 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_DI;
3a229fd5 745 } else {
985cafcc 746 reg = VMK8055_DI_REG;
3a229fd5 747 }
985cafcc 748 for (n = 0; n < insn->n; n++) {
da7b18ee 749 if (vmk80xx_read_packet(devpriv))
985cafcc
MG
750 break;
751
0dd772bf 752 if (boardinfo->model == VMK8055_MODEL)
985cafcc
MG
753 inp = (((rx_buf[reg] >> 4) & 0x03) |
754 ((rx_buf[reg] << 2) & 0x04) |
755 ((rx_buf[reg] >> 3) & 0x18));
756 else
757 inp = rx_buf[reg];
758
9dc99895 759 data[n] = (inp >> chan) & 1;
985cafcc
MG
760 }
761
da7b18ee 762 up(&devpriv->limit_sem);
985cafcc
MG
763
764 return n;
3faad673
MG
765}
766
da7b18ee 767static int vmk80xx_do_winsn(struct comedi_device *dev,
985cafcc
MG
768 struct comedi_subdevice *s,
769 struct comedi_insn *insn, unsigned int *data)
3faad673 770{
da7b18ee
HS
771 const struct vmk80xx_board *boardinfo = comedi_board(dev);
772 struct vmk80xx_private *devpriv = dev->private;
985cafcc
MG
773 int chan;
774 unsigned char *tx_buf;
3a229fd5
AH
775 int reg;
776 int cmd;
985cafcc 777 int n;
3faad673 778
da7b18ee 779 n = rudimentary_check(devpriv, DIR_OUT);
587e500c
AH
780 if (n)
781 return n;
3faad673 782
da7b18ee 783 down(&devpriv->limit_sem);
985cafcc 784 chan = CR_CHAN(insn->chanspec);
3faad673 785
da7b18ee 786 tx_buf = devpriv->usb_tx_buf;
3faad673 787
985cafcc 788 for (n = 0; n < insn->n; n++) {
0dd772bf 789 if (boardinfo->model == VMK8055_MODEL) {
985cafcc
MG
790 reg = VMK8055_DO_REG;
791 cmd = VMK8055_CMD_WRT_AD;
792 if (data[n] == 1)
793 tx_buf[reg] |= (1 << chan);
794 else
795 tx_buf[reg] ^= (1 << chan);
3a229fd5
AH
796 } else { /* VMK8061_MODEL */
797 reg = VMK8061_DO_REG;
798 if (data[n] == 1) {
799 cmd = VMK8061_CMD_SET_DO;
800 tx_buf[reg] = 1 << chan;
801 } else {
802 cmd = VMK8061_CMD_CLR_DO;
803 tx_buf[reg] = 0xff - (1 << chan);
804 }
985cafcc 805 }
3faad673 806
da7b18ee 807 if (vmk80xx_write_packet(devpriv, cmd))
985cafcc
MG
808 break;
809 }
810
da7b18ee 811 up(&devpriv->limit_sem);
985cafcc
MG
812
813 return n;
3faad673
MG
814}
815
da7b18ee 816static int vmk80xx_do_rinsn(struct comedi_device *dev,
985cafcc
MG
817 struct comedi_subdevice *s,
818 struct comedi_insn *insn, unsigned int *data)
3faad673 819{
da7b18ee 820 struct vmk80xx_private *devpriv = dev->private;
3a229fd5
AH
821 int chan;
822 int reg;
985cafcc 823 int n;
3faad673 824
da7b18ee 825 n = rudimentary_check(devpriv, DIR_IN);
587e500c
AH
826 if (n)
827 return n;
3faad673 828
da7b18ee 829 down(&devpriv->limit_sem);
985cafcc 830 chan = CR_CHAN(insn->chanspec);
3faad673 831
985cafcc 832 reg = VMK8061_DO_REG;
3faad673 833
da7b18ee 834 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_DO;
985cafcc
MG
835
836 for (n = 0; n < insn->n; n++) {
da7b18ee 837 if (vmk80xx_read_packet(devpriv))
985cafcc
MG
838 break;
839
da7b18ee 840 data[n] = (devpriv->usb_rx_buf[reg] >> chan) & 1;
3faad673
MG
841 }
842
da7b18ee 843 up(&devpriv->limit_sem);
985cafcc
MG
844
845 return n;
846}
847
da7b18ee 848static int vmk80xx_do_bits(struct comedi_device *dev,
c647ed56
AH
849 struct comedi_subdevice *s,
850 struct comedi_insn *insn, unsigned int *data)
851{
da7b18ee
HS
852 const struct vmk80xx_board *boardinfo = comedi_board(dev);
853 struct vmk80xx_private *devpriv = dev->private;
c647ed56
AH
854 unsigned char *rx_buf, *tx_buf;
855 int dir, reg, cmd;
856 int retval;
857
c647ed56
AH
858 dir = 0;
859
860 if (data[0])
861 dir |= DIR_OUT;
862
0dd772bf 863 if (boardinfo->model == VMK8061_MODEL)
c647ed56
AH
864 dir |= DIR_IN;
865
da7b18ee 866 retval = rudimentary_check(devpriv, dir);
c647ed56
AH
867 if (retval)
868 return retval;
869
da7b18ee 870 down(&devpriv->limit_sem);
c647ed56 871
da7b18ee
HS
872 rx_buf = devpriv->usb_rx_buf;
873 tx_buf = devpriv->usb_tx_buf;
c647ed56
AH
874
875 if (data[0]) {
0dd772bf 876 if (boardinfo->model == VMK8055_MODEL) {
c647ed56
AH
877 reg = VMK8055_DO_REG;
878 cmd = VMK8055_CMD_WRT_AD;
879 } else { /* VMK8061_MODEL */
880 reg = VMK8061_DO_REG;
881 cmd = VMK8061_CMD_DO;
882 }
883
884 tx_buf[reg] &= ~data[0];
885 tx_buf[reg] |= (data[0] & data[1]);
886
da7b18ee 887 retval = vmk80xx_write_packet(devpriv, cmd);
c647ed56
AH
888
889 if (retval)
890 goto out;
891 }
892
0dd772bf 893 if (boardinfo->model == VMK8061_MODEL) {
c647ed56
AH
894 reg = VMK8061_DO_REG;
895 tx_buf[0] = VMK8061_CMD_RD_DO;
896
da7b18ee 897 retval = vmk80xx_read_packet(devpriv);
c647ed56
AH
898
899 if (!retval) {
900 data[1] = rx_buf[reg];
901 retval = 2;
902 }
903 } else {
904 data[1] = tx_buf[reg];
905 retval = 2;
906 }
907
908out:
da7b18ee 909 up(&devpriv->limit_sem);
c647ed56
AH
910
911 return retval;
912}
913
da7b18ee 914static int vmk80xx_cnt_rinsn(struct comedi_device *dev,
985cafcc
MG
915 struct comedi_subdevice *s,
916 struct comedi_insn *insn, unsigned int *data)
917{
da7b18ee
HS
918 const struct vmk80xx_board *boardinfo = comedi_board(dev);
919 struct vmk80xx_private *devpriv = dev->private;
3a229fd5
AH
920 int chan;
921 int reg[2];
985cafcc
MG
922 int n;
923
da7b18ee 924 n = rudimentary_check(devpriv, DIR_IN);
587e500c
AH
925 if (n)
926 return n;
3faad673 927
da7b18ee 928 down(&devpriv->limit_sem);
985cafcc 929 chan = CR_CHAN(insn->chanspec);
3faad673 930
0dd772bf 931 switch (boardinfo->model) {
985cafcc
MG
932 case VMK8055_MODEL:
933 if (!chan)
934 reg[0] = VMK8055_CNT1_REG;
935 else
936 reg[0] = VMK8055_CNT2_REG;
937 break;
938 case VMK8061_MODEL:
13f7952f 939 default:
985cafcc
MG
940 reg[0] = VMK8061_CNT_REG;
941 reg[1] = VMK8061_CNT_REG;
da7b18ee 942 devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_CNT;
985cafcc 943 break;
3faad673
MG
944 }
945
985cafcc 946 for (n = 0; n < insn->n; n++) {
da7b18ee 947 if (vmk80xx_read_packet(devpriv))
985cafcc 948 break;
3faad673 949
0dd772bf 950 if (boardinfo->model == VMK8055_MODEL)
da7b18ee 951 data[n] = devpriv->usb_rx_buf[reg[0]];
3a229fd5 952 else /* VMK8061_MODEL */
da7b18ee
HS
953 data[n] = devpriv->usb_rx_buf[reg[0] * (chan + 1) + 1]
954 + 256 * devpriv->usb_rx_buf[reg[1] * 2 + 2];
985cafcc
MG
955 }
956
da7b18ee 957 up(&devpriv->limit_sem);
985cafcc
MG
958
959 return n;
3faad673
MG
960}
961
da7b18ee 962static int vmk80xx_cnt_cinsn(struct comedi_device *dev,
985cafcc
MG
963 struct comedi_subdevice *s,
964 struct comedi_insn *insn, unsigned int *data)
3faad673 965{
da7b18ee
HS
966 const struct vmk80xx_board *boardinfo = comedi_board(dev);
967 struct vmk80xx_private *devpriv = dev->private;
985cafcc 968 unsigned int insn_cmd;
3a229fd5
AH
969 int chan;
970 int cmd;
971 int reg;
985cafcc 972 int n;
3faad673 973
da7b18ee 974 n = rudimentary_check(devpriv, DIR_OUT);
587e500c
AH
975 if (n)
976 return n;
3faad673 977
985cafcc
MG
978 insn_cmd = data[0];
979 if (insn_cmd != INSN_CONFIG_RESET && insn_cmd != GPCT_RESET)
980 return -EINVAL;
3faad673 981
da7b18ee 982 down(&devpriv->limit_sem);
8f9064a8 983
985cafcc 984 chan = CR_CHAN(insn->chanspec);
3faad673 985
0dd772bf 986 if (boardinfo->model == VMK8055_MODEL) {
985cafcc
MG
987 if (!chan) {
988 cmd = VMK8055_CMD_RST_CNT1;
989 reg = VMK8055_CNT1_REG;
990 } else {
991 cmd = VMK8055_CMD_RST_CNT2;
992 reg = VMK8055_CNT2_REG;
993 }
994
da7b18ee 995 devpriv->usb_tx_buf[reg] = 0x00;
3a229fd5 996 } else {
985cafcc 997 cmd = VMK8061_CMD_RST_CNT;
3a229fd5 998 }
985cafcc
MG
999
1000 for (n = 0; n < insn->n; n++)
da7b18ee 1001 if (vmk80xx_write_packet(devpriv, cmd))
985cafcc
MG
1002 break;
1003
da7b18ee 1004 up(&devpriv->limit_sem);
985cafcc
MG
1005
1006 return n;
1007}
1008
da7b18ee 1009static int vmk80xx_cnt_winsn(struct comedi_device *dev,
985cafcc
MG
1010 struct comedi_subdevice *s,
1011 struct comedi_insn *insn, unsigned int *data)
1012{
da7b18ee 1013 struct vmk80xx_private *devpriv = dev->private;
3a229fd5
AH
1014 unsigned long debtime;
1015 unsigned long val;
1016 int chan;
1017 int cmd;
985cafcc
MG
1018 int n;
1019
da7b18ee 1020 n = rudimentary_check(devpriv, DIR_OUT);
587e500c
AH
1021 if (n)
1022 return n;
985cafcc 1023
da7b18ee 1024 down(&devpriv->limit_sem);
985cafcc
MG
1025 chan = CR_CHAN(insn->chanspec);
1026
1027 if (!chan)
1028 cmd = VMK8055_CMD_DEB1_TIME;
1029 else
1030 cmd = VMK8055_CMD_DEB2_TIME;
1031
1032 for (n = 0; n < insn->n; n++) {
1033 debtime = data[n];
3faad673
MG
1034 if (debtime == 0)
1035 debtime = 1;
985cafcc
MG
1036
1037 /* TODO: Prevent overflows */
1038 if (debtime > 7450)
1039 debtime = 7450;
1040
3faad673
MG
1041 val = int_sqrt(debtime * 1000 / 115);
1042 if (((val + 1) * val) < debtime * 1000 / 115)
1043 val += 1;
1044
da7b18ee 1045 devpriv->usb_tx_buf[6 + chan] = val;
3faad673 1046
da7b18ee 1047 if (vmk80xx_write_packet(devpriv, cmd))
985cafcc 1048 break;
3faad673
MG
1049 }
1050
da7b18ee 1051 up(&devpriv->limit_sem);
3faad673 1052
985cafcc
MG
1053 return n;
1054}
3faad673 1055
da7b18ee 1056static int vmk80xx_pwm_rinsn(struct comedi_device *dev,
985cafcc
MG
1057 struct comedi_subdevice *s,
1058 struct comedi_insn *insn, unsigned int *data)
1059{
da7b18ee
HS
1060 struct vmk80xx_private *devpriv = dev->private;
1061 unsigned char *tx_buf;
1062 unsigned char *rx_buf;
985cafcc
MG
1063 int reg[2];
1064 int n;
1065
da7b18ee 1066 n = rudimentary_check(devpriv, DIR_IN);
587e500c
AH
1067 if (n)
1068 return n;
985cafcc 1069
da7b18ee
HS
1070 down(&devpriv->limit_sem);
1071
1072 tx_buf = devpriv->usb_tx_buf;
1073 rx_buf = devpriv->usb_rx_buf;
985cafcc
MG
1074
1075 reg[0] = VMK8061_PWM_REG1;
1076 reg[1] = VMK8061_PWM_REG2;
1077
da7b18ee 1078 tx_buf[0] = VMK8061_CMD_RD_PWM;
985cafcc
MG
1079
1080 for (n = 0; n < insn->n; n++) {
da7b18ee 1081 if (vmk80xx_read_packet(devpriv))
985cafcc
MG
1082 break;
1083
da7b18ee 1084 data[n] = rx_buf[reg[0]] + 4 * rx_buf[reg[1]];
985cafcc
MG
1085 }
1086
da7b18ee 1087 up(&devpriv->limit_sem);
985cafcc
MG
1088
1089 return n;
3faad673
MG
1090}
1091
da7b18ee 1092static int vmk80xx_pwm_winsn(struct comedi_device *dev,
985cafcc
MG
1093 struct comedi_subdevice *s,
1094 struct comedi_insn *insn, unsigned int *data)
1095{
da7b18ee 1096 struct vmk80xx_private *devpriv = dev->private;
985cafcc 1097 unsigned char *tx_buf;
3a229fd5
AH
1098 int reg[2];
1099 int cmd;
985cafcc 1100 int n;
3faad673 1101
da7b18ee 1102 n = rudimentary_check(devpriv, DIR_OUT);
587e500c
AH
1103 if (n)
1104 return n;
985cafcc 1105
da7b18ee 1106 down(&devpriv->limit_sem);
985cafcc 1107
da7b18ee 1108 tx_buf = devpriv->usb_tx_buf;
985cafcc
MG
1109
1110 reg[0] = VMK8061_PWM_REG1;
1111 reg[1] = VMK8061_PWM_REG2;
1112
1113 cmd = VMK8061_CMD_OUT_PWM;
1114
1115 /*
1116 * The followin piece of code was translated from the inline
1117 * assembler code in the DLL source code.
1118 *
1119 * asm
1120 * mov eax, k ; k is the value (data[n])
1121 * and al, 03h ; al are the lower 8 bits of eax
1122 * mov lo, al ; lo is the low part (tx_buf[reg[0]])
1123 * mov eax, k
1124 * shr eax, 2 ; right shift eax register by 2
1125 * mov hi, al ; hi is the high part (tx_buf[reg[1]])
1126 * end;
1127 */
1128 for (n = 0; n < insn->n; n++) {
1129 tx_buf[reg[0]] = (unsigned char)(data[n] & 0x03);
1130 tx_buf[reg[1]] = (unsigned char)(data[n] >> 2) & 0xff;
1131
da7b18ee 1132 if (vmk80xx_write_packet(devpriv, cmd))
985cafcc
MG
1133 break;
1134 }
3faad673 1135
da7b18ee 1136 up(&devpriv->limit_sem);
985cafcc
MG
1137
1138 return n;
1139}
1140
49253d54
HS
1141static int vmk80xx_find_usb_endpoints(struct vmk80xx_private *devpriv,
1142 struct usb_interface *intf)
1143{
1144 struct usb_host_interface *iface_desc = intf->cur_altsetting;
1145 struct usb_endpoint_descriptor *ep_desc;
1146 int i;
1147
1148 if (iface_desc->desc.bNumEndpoints != 2)
1149 return -ENODEV;
1150
1151 for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
1152 ep_desc = &iface_desc->endpoint[i].desc;
1153
1154 if (usb_endpoint_is_int_in(ep_desc) ||
1155 usb_endpoint_is_bulk_in(ep_desc)) {
1156 if (!devpriv->ep_rx)
1157 devpriv->ep_rx = ep_desc;
1158 continue;
1159 }
1160
1161 if (usb_endpoint_is_int_out(ep_desc) ||
1162 usb_endpoint_is_bulk_out(ep_desc)) {
1163 if (!devpriv->ep_tx)
1164 devpriv->ep_tx = ep_desc;
1165 continue;
1166 }
1167 }
1168
1169 if (!devpriv->ep_rx || !devpriv->ep_tx)
1170 return -ENODEV;
1171
1172 return 0;
1173}
1174
da7b18ee
HS
1175static int vmk80xx_attach_common(struct comedi_device *dev,
1176 struct vmk80xx_private *devpriv)
3faad673 1177{
0dd772bf 1178 const struct vmk80xx_board *boardinfo;
985cafcc 1179 int n_subd;
b153d83e 1180 struct comedi_subdevice *s;
8b6c5694 1181 int ret;
3faad673 1182
da7b18ee 1183 down(&devpriv->limit_sem);
0dd772bf 1184
da7b18ee
HS
1185 boardinfo = devpriv->board;
1186 dev->board_ptr = boardinfo;
1187 dev->board_name = boardinfo->name;
1188 dev->private = devpriv;
0dd772bf
HS
1189
1190 if (boardinfo->model == VMK8055_MODEL)
985cafcc
MG
1191 n_subd = 5;
1192 else
1193 n_subd = 6;
da7b18ee 1194 ret = comedi_alloc_subdevices(dev, n_subd);
8b6c5694 1195 if (ret) {
da7b18ee 1196 up(&devpriv->limit_sem);
8b6c5694 1197 return ret;
3faad673 1198 }
0dd772bf 1199
985cafcc 1200 /* Analog input subdevice */
da7b18ee 1201 s = &dev->subdevices[0];
3faad673
MG
1202 s->type = COMEDI_SUBD_AI;
1203 s->subdev_flags = SDF_READABLE | SDF_GROUND;
0dd772bf
HS
1204 s->n_chan = boardinfo->ai_chans;
1205 s->maxdata = (1 << boardinfo->ai_bits) - 1;
1206 s->range_table = boardinfo->range;
985cafcc 1207 s->insn_read = vmk80xx_ai_rinsn;
0dd772bf 1208
985cafcc 1209 /* Analog output subdevice */
da7b18ee 1210 s = &dev->subdevices[1];
3faad673
MG
1211 s->type = COMEDI_SUBD_AO;
1212 s->subdev_flags = SDF_WRITEABLE | SDF_GROUND;
0dd772bf 1213 s->n_chan = boardinfo->ao_chans;
70ba1a59 1214 s->maxdata = 0x00ff;
0dd772bf 1215 s->range_table = boardinfo->range;
985cafcc 1216 s->insn_write = vmk80xx_ao_winsn;
0dd772bf 1217 if (boardinfo->model == VMK8061_MODEL) {
985cafcc
MG
1218 s->subdev_flags |= SDF_READABLE;
1219 s->insn_read = vmk80xx_ao_rinsn;
1220 }
0dd772bf 1221
985cafcc 1222 /* Digital input subdevice */
da7b18ee 1223 s = &dev->subdevices[2];
3faad673
MG
1224 s->type = COMEDI_SUBD_DI;
1225 s->subdev_flags = SDF_READABLE | SDF_GROUND;
0dd772bf 1226 s->n_chan = boardinfo->di_chans;
85a2f34f 1227 s->maxdata = 1;
985cafcc 1228 s->insn_read = vmk80xx_di_rinsn;
c647ed56 1229 s->insn_bits = vmk80xx_di_bits;
0dd772bf 1230
985cafcc 1231 /* Digital output subdevice */
da7b18ee 1232 s = &dev->subdevices[3];
3faad673
MG
1233 s->type = COMEDI_SUBD_DO;
1234 s->subdev_flags = SDF_WRITEABLE | SDF_GROUND;
70ba1a59 1235 s->n_chan = 8;
85a2f34f 1236 s->maxdata = 1;
985cafcc 1237 s->insn_write = vmk80xx_do_winsn;
c647ed56 1238 s->insn_bits = vmk80xx_do_bits;
0dd772bf 1239 if (boardinfo->model == VMK8061_MODEL) {
985cafcc
MG
1240 s->subdev_flags |= SDF_READABLE;
1241 s->insn_read = vmk80xx_do_rinsn;
1242 }
0dd772bf 1243
985cafcc 1244 /* Counter subdevice */
da7b18ee 1245 s = &dev->subdevices[4];
3faad673 1246 s->type = COMEDI_SUBD_COUNTER;
985cafcc 1247 s->subdev_flags = SDF_READABLE;
70ba1a59 1248 s->n_chan = 2;
985cafcc
MG
1249 s->insn_read = vmk80xx_cnt_rinsn;
1250 s->insn_config = vmk80xx_cnt_cinsn;
0dd772bf 1251 if (boardinfo->model == VMK8055_MODEL) {
985cafcc 1252 s->subdev_flags |= SDF_WRITEABLE;
0dd772bf 1253 s->maxdata = (1 << boardinfo->cnt_bits) - 1;
985cafcc
MG
1254 s->insn_write = vmk80xx_cnt_winsn;
1255 }
0dd772bf 1256
985cafcc 1257 /* PWM subdevice */
0dd772bf 1258 if (boardinfo->model == VMK8061_MODEL) {
da7b18ee 1259 s = &dev->subdevices[5];
985cafcc
MG
1260 s->type = COMEDI_SUBD_PWM;
1261 s->subdev_flags = SDF_READABLE | SDF_WRITEABLE;
0dd772bf
HS
1262 s->n_chan = boardinfo->pwm_chans;
1263 s->maxdata = (1 << boardinfo->pwm_bits) - 1;
985cafcc
MG
1264 s->insn_read = vmk80xx_pwm_rinsn;
1265 s->insn_write = vmk80xx_pwm_winsn;
1266 }
0dd772bf 1267
da7b18ee
HS
1268 devpriv->attached = 1;
1269 dev_info(dev->class_dev, "vmk80xx: board #%d [%s] attached\n",
1270 devpriv->count, boardinfo->name);
0dd772bf 1271
da7b18ee 1272 up(&devpriv->limit_sem);
0dd772bf 1273
f7d4d3bc
IA
1274 return 0;
1275}
3faad673 1276
da7b18ee 1277static int vmk80xx_auto_attach(struct comedi_device *dev,
392ba7bc 1278 unsigned long context_unused)
f7d4d3bc 1279{
da7b18ee 1280 struct usb_interface *intf = comedi_to_usb_interface(dev);
f7d4d3bc
IA
1281 int i;
1282 int ret;
1283
1284 mutex_lock(&glb_mutex);
1285 for (i = 0; i < VMK80XX_MAX_BOARDS; i++)
1286 if (vmb[i].probed && vmb[i].intf == intf)
1287 break;
1288 if (i == VMK80XX_MAX_BOARDS)
1289 ret = -ENODEV;
1290 else if (vmb[i].attached)
1291 ret = -EBUSY;
1292 else
da7b18ee 1293 ret = vmk80xx_attach_common(dev, &vmb[i]);
f7d4d3bc
IA
1294 mutex_unlock(&glb_mutex);
1295 return ret;
3faad673
MG
1296}
1297
484ecc95 1298static void vmk80xx_detach(struct comedi_device *dev)
3faad673 1299{
da7b18ee 1300 struct vmk80xx_private *devpriv = dev->private;
3faad673 1301
da7b18ee 1302 if (!devpriv)
9377b923
HS
1303 return;
1304
1305 mutex_lock(&glb_mutex);
da7b18ee 1306 down(&devpriv->limit_sem);
9377b923
HS
1307
1308 dev->private = NULL;
1309
da7b18ee
HS
1310 devpriv->attached = 0;
1311 devpriv->probed = 0;
1312 usb_set_intfdata(devpriv->intf, NULL);
9377b923 1313
da7b18ee
HS
1314 usb_kill_anchored_urbs(&devpriv->rx_anchor);
1315 usb_kill_anchored_urbs(&devpriv->tx_anchor);
9377b923 1316
da7b18ee
HS
1317 kfree(devpriv->usb_rx_buf);
1318 kfree(devpriv->usb_tx_buf);
9377b923 1319
da7b18ee 1320 up(&devpriv->limit_sem);
9377b923 1321 mutex_unlock(&glb_mutex);
3faad673
MG
1322}
1323
007ff2af
HS
1324static struct comedi_driver vmk80xx_driver = {
1325 .module = THIS_MODULE,
1326 .driver_name = "vmk80xx",
392ba7bc 1327 .auto_attach = vmk80xx_auto_attach,
07b502f5 1328 .detach = vmk80xx_detach,
007ff2af
HS
1329};
1330
1331static int vmk80xx_usb_probe(struct usb_interface *intf,
1332 const struct usb_device_id *id)
3faad673 1333{
0dd772bf 1334 const struct vmk80xx_board *boardinfo;
da7b18ee 1335 struct vmk80xx_private *devpriv;
985cafcc 1336 size_t size;
49253d54 1337 int ret;
da7b18ee 1338 int i;
3faad673 1339
3faad673
MG
1340 mutex_lock(&glb_mutex);
1341
985cafcc
MG
1342 for (i = 0; i < VMK80XX_MAX_BOARDS; i++)
1343 if (!vmb[i].probed)
1344 break;
3faad673 1345
985cafcc 1346 if (i == VMK80XX_MAX_BOARDS) {
3faad673 1347 mutex_unlock(&glb_mutex);
985cafcc 1348 return -EMFILE;
3faad673
MG
1349 }
1350
da7b18ee 1351 devpriv = &vmb[i];
985cafcc 1352
da7b18ee
HS
1353 memset(devpriv, 0x00, sizeof(*devpriv));
1354 devpriv->count = i;
985cafcc 1355
49253d54
HS
1356 ret = vmk80xx_find_usb_endpoints(devpriv, intf);
1357 if (ret) {
1358 mutex_unlock(&glb_mutex);
1359 return ret;
3faad673
MG
1360 }
1361
da7b18ee
HS
1362 size = le16_to_cpu(devpriv->ep_rx->wMaxPacketSize);
1363 devpriv->usb_rx_buf = kmalloc(size, GFP_KERNEL);
1364 if (!devpriv->usb_rx_buf) {
985cafcc
MG
1365 mutex_unlock(&glb_mutex);
1366 return -ENOMEM;
3faad673
MG
1367 }
1368
da7b18ee
HS
1369 size = le16_to_cpu(devpriv->ep_tx->wMaxPacketSize);
1370 devpriv->usb_tx_buf = kmalloc(size, GFP_KERNEL);
1371 if (!devpriv->usb_tx_buf) {
1372 kfree(devpriv->usb_rx_buf);
985cafcc
MG
1373 mutex_unlock(&glb_mutex);
1374 return -ENOMEM;
3faad673
MG
1375 }
1376
da7b18ee
HS
1377 devpriv->usb = interface_to_usbdev(intf);
1378 devpriv->intf = intf;
985cafcc 1379
da7b18ee
HS
1380 sema_init(&devpriv->limit_sem, 8);
1381 init_waitqueue_head(&devpriv->read_wait);
1382 init_waitqueue_head(&devpriv->write_wait);
985cafcc 1383
da7b18ee
HS
1384 init_usb_anchor(&devpriv->rx_anchor);
1385 init_usb_anchor(&devpriv->tx_anchor);
985cafcc 1386
da7b18ee 1387 usb_set_intfdata(intf, devpriv);
985cafcc 1388
0dd772bf 1389 boardinfo = &vmk80xx_boardinfo[id->driver_info];
da7b18ee 1390 devpriv->board = boardinfo;
3faad673 1391
0dd772bf 1392 if (boardinfo->model == VMK8061_MODEL) {
da7b18ee
HS
1393 vmk80xx_read_eeprom(devpriv, IC3_VERSION);
1394 dev_info(&intf->dev, "%s\n", devpriv->fw.ic3_vers);
985cafcc 1395
da7b18ee
HS
1396 if (vmk80xx_check_data_link(devpriv)) {
1397 vmk80xx_read_eeprom(devpriv, IC6_VERSION);
1398 dev_info(&intf->dev, "%s\n", devpriv->fw.ic6_vers);
3a229fd5 1399 } else {
985cafcc 1400 dbgcm("comedi#: vmk80xx: no conn. to CPU\n");
3a229fd5 1401 }
3faad673
MG
1402 }
1403
0dd772bf 1404 if (boardinfo->model == VMK8055_MODEL)
da7b18ee 1405 vmk80xx_reset_device(devpriv);
3faad673 1406
da7b18ee 1407 devpriv->probed = 1;
3faad673 1408
7194d0e1 1409 dev_info(&intf->dev, "board #%d [%s] now attached\n",
da7b18ee 1410 devpriv->count, boardinfo->name);
3faad673
MG
1411
1412 mutex_unlock(&glb_mutex);
1413
d6cc3ec8 1414 comedi_usb_auto_config(intf, &vmk80xx_driver);
8ba69ce4 1415
985cafcc 1416 return 0;
3faad673
MG
1417}
1418
007ff2af
HS
1419static const struct usb_device_id vmk80xx_usb_id_table[] = {
1420 { USB_DEVICE(0x10cf, 0x5500), .driver_info = DEVICE_VMK8055 },
1421 { USB_DEVICE(0x10cf, 0x5501), .driver_info = DEVICE_VMK8055 },
1422 { USB_DEVICE(0x10cf, 0x5502), .driver_info = DEVICE_VMK8055 },
1423 { USB_DEVICE(0x10cf, 0x5503), .driver_info = DEVICE_VMK8055 },
1424 { USB_DEVICE(0x10cf, 0x8061), .driver_info = DEVICE_VMK8061 },
1425 { USB_DEVICE(0x10cf, 0x8062), .driver_info = DEVICE_VMK8061 },
1426 { USB_DEVICE(0x10cf, 0x8063), .driver_info = DEVICE_VMK8061 },
1427 { USB_DEVICE(0x10cf, 0x8064), .driver_info = DEVICE_VMK8061 },
1428 { USB_DEVICE(0x10cf, 0x8065), .driver_info = DEVICE_VMK8061 },
1429 { USB_DEVICE(0x10cf, 0x8066), .driver_info = DEVICE_VMK8061 },
1430 { USB_DEVICE(0x10cf, 0x8067), .driver_info = DEVICE_VMK8061 },
1431 { USB_DEVICE(0x10cf, 0x8068), .driver_info = DEVICE_VMK8061 },
1432 { }
1433};
1434MODULE_DEVICE_TABLE(usb, vmk80xx_usb_id_table);
1435
d6cc3ec8
HS
1436static struct usb_driver vmk80xx_usb_driver = {
1437 .name = "vmk80xx",
007ff2af 1438 .id_table = vmk80xx_usb_id_table,
ce874227
HS
1439 .probe = vmk80xx_usb_probe,
1440 .disconnect = comedi_usb_auto_unconfig,
3faad673 1441};
d6cc3ec8 1442module_comedi_usb_driver(vmk80xx_driver, vmk80xx_usb_driver);
007ff2af
HS
1443
1444MODULE_AUTHOR("Manuel Gebele <forensixs@gmx.de>");
1445MODULE_DESCRIPTION("Velleman USB Board Low-Level Driver");
1446MODULE_SUPPORTED_DEVICE("K8055/K8061 aka VM110/VM140");
1447MODULE_VERSION("0.8.01");
1448MODULE_LICENSE("GPL");
This page took 0.492553 seconds and 5 git commands to generate.