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