xref: /openbmc/linux/drivers/comedi/drivers/vmk80xx.c (revision 59f33af9)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * vmk80xx.c
4  * Velleman USB Board Low-Level Driver
5  *
6  * Copyright (C) 2009 Manuel Gebele <forensixs@gmx.de>, Germany
7  *
8  * COMEDI - Linux Control and Measurement Device Interface
9  * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
10  */
11 
12 /*
13  * Driver: vmk80xx
14  * Description: Velleman USB Board Low-Level Driver
15  * Devices: [Velleman] K8055 (K8055/VM110), K8061 (K8061/VM140),
16  *   VM110 (K8055/VM110), VM140 (K8061/VM140)
17  * Author: Manuel Gebele <forensixs@gmx.de>
18  * Updated: Sun, 10 May 2009 11:14:59 +0200
19  * Status: works
20  *
21  * Supports:
22  *  - analog input
23  *  - analog output
24  *  - digital input
25  *  - digital output
26  *  - counter
27  *  - pwm
28  */
29 
30 #include <linux/kernel.h>
31 #include <linux/module.h>
32 #include <linux/mutex.h>
33 #include <linux/errno.h>
34 #include <linux/input.h>
35 #include <linux/slab.h>
36 #include <linux/poll.h>
37 #include <linux/uaccess.h>
38 #include <linux/comedi/comedi_usb.h>
39 
40 enum {
41 	DEVICE_VMK8055,
42 	DEVICE_VMK8061
43 };
44 
45 #define VMK8055_DI_REG		0x00
46 #define VMK8055_DO_REG		0x01
47 #define VMK8055_AO1_REG		0x02
48 #define VMK8055_AO2_REG		0x03
49 #define VMK8055_AI1_REG		0x02
50 #define VMK8055_AI2_REG		0x03
51 #define VMK8055_CNT1_REG	0x04
52 #define VMK8055_CNT2_REG	0x06
53 
54 #define VMK8061_CH_REG		0x01
55 #define VMK8061_DI_REG		0x01
56 #define VMK8061_DO_REG		0x01
57 #define VMK8061_PWM_REG1	0x01
58 #define VMK8061_PWM_REG2	0x02
59 #define VMK8061_CNT_REG		0x02
60 #define VMK8061_AO_REG		0x02
61 #define VMK8061_AI_REG1		0x02
62 #define VMK8061_AI_REG2		0x03
63 
64 #define VMK8055_CMD_RST		0x00
65 #define VMK8055_CMD_DEB1_TIME	0x01
66 #define VMK8055_CMD_DEB2_TIME	0x02
67 #define VMK8055_CMD_RST_CNT1	0x03
68 #define VMK8055_CMD_RST_CNT2	0x04
69 #define VMK8055_CMD_WRT_AD	0x05
70 
71 #define VMK8061_CMD_RD_AI	0x00
72 #define VMK8061_CMR_RD_ALL_AI	0x01	/* !non-active! */
73 #define VMK8061_CMD_SET_AO	0x02
74 #define VMK8061_CMD_SET_ALL_AO	0x03	/* !non-active! */
75 #define VMK8061_CMD_OUT_PWM	0x04
76 #define VMK8061_CMD_RD_DI	0x05
77 #define VMK8061_CMD_DO		0x06	/* !non-active! */
78 #define VMK8061_CMD_CLR_DO	0x07
79 #define VMK8061_CMD_SET_DO	0x08
80 #define VMK8061_CMD_RD_CNT	0x09	/* TODO: completely pointless? */
81 #define VMK8061_CMD_RST_CNT	0x0a	/* TODO: completely pointless? */
82 #define VMK8061_CMD_RD_VERSION	0x0b	/* internal usage */
83 #define VMK8061_CMD_RD_JMP_STAT	0x0c	/* TODO: not implemented yet */
84 #define VMK8061_CMD_RD_PWR_STAT	0x0d	/* internal usage */
85 #define VMK8061_CMD_RD_DO	0x0e
86 #define VMK8061_CMD_RD_AO	0x0f
87 #define VMK8061_CMD_RD_PWM	0x10
88 
89 #define IC3_VERSION		BIT(0)
90 #define IC6_VERSION		BIT(1)
91 
92 #define MIN_BUF_SIZE		64
93 #define PACKET_TIMEOUT		10000	/* ms */
94 
95 enum vmk80xx_model {
96 	VMK8055_MODEL,
97 	VMK8061_MODEL
98 };
99 
100 static const struct comedi_lrange vmk8061_range = {
101 	2, {
102 		UNI_RANGE(5),
103 		UNI_RANGE(10)
104 	}
105 };
106 
107 struct vmk80xx_board {
108 	const char *name;
109 	enum vmk80xx_model model;
110 	const struct comedi_lrange *range;
111 	int ai_nchans;
112 	unsigned int ai_maxdata;
113 	int ao_nchans;
114 	int di_nchans;
115 	unsigned int cnt_maxdata;
116 	int pwm_nchans;
117 	unsigned int pwm_maxdata;
118 };
119 
120 static const struct vmk80xx_board vmk80xx_boardinfo[] = {
121 	[DEVICE_VMK8055] = {
122 		.name		= "K8055 (VM110)",
123 		.model		= VMK8055_MODEL,
124 		.range		= &range_unipolar5,
125 		.ai_nchans	= 2,
126 		.ai_maxdata	= 0x00ff,
127 		.ao_nchans	= 2,
128 		.di_nchans	= 6,
129 		.cnt_maxdata	= 0xffff,
130 	},
131 	[DEVICE_VMK8061] = {
132 		.name		= "K8061 (VM140)",
133 		.model		= VMK8061_MODEL,
134 		.range		= &vmk8061_range,
135 		.ai_nchans	= 8,
136 		.ai_maxdata	= 0x03ff,
137 		.ao_nchans	= 8,
138 		.di_nchans	= 8,
139 		.cnt_maxdata	= 0,	/* unknown, device is not writeable */
140 		.pwm_nchans	= 1,
141 		.pwm_maxdata	= 0x03ff,
142 	},
143 };
144 
145 struct vmk80xx_private {
146 	struct usb_endpoint_descriptor *ep_rx;
147 	struct usb_endpoint_descriptor *ep_tx;
148 	struct semaphore limit_sem;
149 	unsigned char *usb_rx_buf;
150 	unsigned char *usb_tx_buf;
151 	enum vmk80xx_model model;
152 };
153 
vmk80xx_do_bulk_msg(struct comedi_device * dev)154 static void vmk80xx_do_bulk_msg(struct comedi_device *dev)
155 {
156 	struct vmk80xx_private *devpriv = dev->private;
157 	struct usb_device *usb = comedi_to_usb_dev(dev);
158 	__u8 tx_addr;
159 	__u8 rx_addr;
160 	unsigned int tx_pipe;
161 	unsigned int rx_pipe;
162 	size_t tx_size;
163 	size_t rx_size;
164 
165 	tx_addr = devpriv->ep_tx->bEndpointAddress;
166 	rx_addr = devpriv->ep_rx->bEndpointAddress;
167 	tx_pipe = usb_sndbulkpipe(usb, tx_addr);
168 	rx_pipe = usb_rcvbulkpipe(usb, rx_addr);
169 	tx_size = usb_endpoint_maxp(devpriv->ep_tx);
170 	rx_size = usb_endpoint_maxp(devpriv->ep_rx);
171 
172 	usb_bulk_msg(usb, tx_pipe, devpriv->usb_tx_buf, tx_size, NULL,
173 		     PACKET_TIMEOUT);
174 
175 	usb_bulk_msg(usb, rx_pipe, devpriv->usb_rx_buf, rx_size, NULL,
176 		     PACKET_TIMEOUT);
177 }
178 
vmk80xx_read_packet(struct comedi_device * dev)179 static int vmk80xx_read_packet(struct comedi_device *dev)
180 {
181 	struct vmk80xx_private *devpriv = dev->private;
182 	struct usb_device *usb = comedi_to_usb_dev(dev);
183 	struct usb_endpoint_descriptor *ep;
184 	unsigned int pipe;
185 
186 	if (devpriv->model == VMK8061_MODEL) {
187 		vmk80xx_do_bulk_msg(dev);
188 		return 0;
189 	}
190 
191 	ep = devpriv->ep_rx;
192 	pipe = usb_rcvintpipe(usb, ep->bEndpointAddress);
193 	return usb_interrupt_msg(usb, pipe, devpriv->usb_rx_buf,
194 				 usb_endpoint_maxp(ep), NULL,
195 				 PACKET_TIMEOUT);
196 }
197 
vmk80xx_write_packet(struct comedi_device * dev,int cmd)198 static int vmk80xx_write_packet(struct comedi_device *dev, int cmd)
199 {
200 	struct vmk80xx_private *devpriv = dev->private;
201 	struct usb_device *usb = comedi_to_usb_dev(dev);
202 	struct usb_endpoint_descriptor *ep;
203 	unsigned int pipe;
204 
205 	devpriv->usb_tx_buf[0] = cmd;
206 
207 	if (devpriv->model == VMK8061_MODEL) {
208 		vmk80xx_do_bulk_msg(dev);
209 		return 0;
210 	}
211 
212 	ep = devpriv->ep_tx;
213 	pipe = usb_sndintpipe(usb, ep->bEndpointAddress);
214 	return usb_interrupt_msg(usb, pipe, devpriv->usb_tx_buf,
215 				 usb_endpoint_maxp(ep), NULL,
216 				 PACKET_TIMEOUT);
217 }
218 
vmk80xx_reset_device(struct comedi_device * dev)219 static int vmk80xx_reset_device(struct comedi_device *dev)
220 {
221 	struct vmk80xx_private *devpriv = dev->private;
222 	size_t size;
223 	int retval;
224 
225 	size = usb_endpoint_maxp(devpriv->ep_tx);
226 	memset(devpriv->usb_tx_buf, 0, size);
227 	retval = vmk80xx_write_packet(dev, VMK8055_CMD_RST);
228 	if (retval)
229 		return retval;
230 	/* set outputs to known state as we cannot read them */
231 	return vmk80xx_write_packet(dev, VMK8055_CMD_WRT_AD);
232 }
233 
vmk80xx_ai_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)234 static int vmk80xx_ai_insn_read(struct comedi_device *dev,
235 				struct comedi_subdevice *s,
236 				struct comedi_insn *insn,
237 				unsigned int *data)
238 {
239 	struct vmk80xx_private *devpriv = dev->private;
240 	int chan;
241 	int reg[2];
242 	int n;
243 
244 	down(&devpriv->limit_sem);
245 	chan = CR_CHAN(insn->chanspec);
246 
247 	switch (devpriv->model) {
248 	case VMK8055_MODEL:
249 		if (!chan)
250 			reg[0] = VMK8055_AI1_REG;
251 		else
252 			reg[0] = VMK8055_AI2_REG;
253 		break;
254 	case VMK8061_MODEL:
255 	default:
256 		reg[0] = VMK8061_AI_REG1;
257 		reg[1] = VMK8061_AI_REG2;
258 		devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AI;
259 		devpriv->usb_tx_buf[VMK8061_CH_REG] = chan;
260 		break;
261 	}
262 
263 	for (n = 0; n < insn->n; n++) {
264 		if (vmk80xx_read_packet(dev))
265 			break;
266 
267 		if (devpriv->model == VMK8055_MODEL) {
268 			data[n] = devpriv->usb_rx_buf[reg[0]];
269 			continue;
270 		}
271 
272 		/* VMK8061_MODEL */
273 		data[n] = devpriv->usb_rx_buf[reg[0]] + 256 *
274 		    devpriv->usb_rx_buf[reg[1]];
275 	}
276 
277 	up(&devpriv->limit_sem);
278 
279 	return n;
280 }
281 
vmk80xx_ao_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)282 static int vmk80xx_ao_insn_write(struct comedi_device *dev,
283 				 struct comedi_subdevice *s,
284 				 struct comedi_insn *insn,
285 				 unsigned int *data)
286 {
287 	struct vmk80xx_private *devpriv = dev->private;
288 	int chan;
289 	int cmd;
290 	int reg;
291 	int n;
292 
293 	down(&devpriv->limit_sem);
294 	chan = CR_CHAN(insn->chanspec);
295 
296 	switch (devpriv->model) {
297 	case VMK8055_MODEL:
298 		cmd = VMK8055_CMD_WRT_AD;
299 		if (!chan)
300 			reg = VMK8055_AO1_REG;
301 		else
302 			reg = VMK8055_AO2_REG;
303 		break;
304 	default:		/* NOTE: avoid compiler warnings */
305 		cmd = VMK8061_CMD_SET_AO;
306 		reg = VMK8061_AO_REG;
307 		devpriv->usb_tx_buf[VMK8061_CH_REG] = chan;
308 		break;
309 	}
310 
311 	for (n = 0; n < insn->n; n++) {
312 		devpriv->usb_tx_buf[reg] = data[n];
313 
314 		if (vmk80xx_write_packet(dev, cmd))
315 			break;
316 	}
317 
318 	up(&devpriv->limit_sem);
319 
320 	return n;
321 }
322 
vmk80xx_ao_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)323 static int vmk80xx_ao_insn_read(struct comedi_device *dev,
324 				struct comedi_subdevice *s,
325 				struct comedi_insn *insn,
326 				unsigned int *data)
327 {
328 	struct vmk80xx_private *devpriv = dev->private;
329 	int chan;
330 	int reg;
331 	int n;
332 
333 	down(&devpriv->limit_sem);
334 	chan = CR_CHAN(insn->chanspec);
335 
336 	reg = VMK8061_AO_REG - 1;
337 
338 	devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AO;
339 
340 	for (n = 0; n < insn->n; n++) {
341 		if (vmk80xx_read_packet(dev))
342 			break;
343 
344 		data[n] = devpriv->usb_rx_buf[reg + chan];
345 	}
346 
347 	up(&devpriv->limit_sem);
348 
349 	return n;
350 }
351 
vmk80xx_di_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)352 static int vmk80xx_di_insn_bits(struct comedi_device *dev,
353 				struct comedi_subdevice *s,
354 				struct comedi_insn *insn,
355 				unsigned int *data)
356 {
357 	struct vmk80xx_private *devpriv = dev->private;
358 	unsigned char *rx_buf;
359 	int reg;
360 	int retval;
361 
362 	down(&devpriv->limit_sem);
363 
364 	rx_buf = devpriv->usb_rx_buf;
365 
366 	if (devpriv->model == VMK8061_MODEL) {
367 		reg = VMK8061_DI_REG;
368 		devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_DI;
369 	} else {
370 		reg = VMK8055_DI_REG;
371 	}
372 
373 	retval = vmk80xx_read_packet(dev);
374 
375 	if (!retval) {
376 		if (devpriv->model == VMK8055_MODEL)
377 			data[1] = (((rx_buf[reg] >> 4) & 0x03) |
378 				  ((rx_buf[reg] << 2) & 0x04) |
379 				  ((rx_buf[reg] >> 3) & 0x18));
380 		else
381 			data[1] = rx_buf[reg];
382 
383 		retval = 2;
384 	}
385 
386 	up(&devpriv->limit_sem);
387 
388 	return retval;
389 }
390 
vmk80xx_do_insn_bits(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)391 static int vmk80xx_do_insn_bits(struct comedi_device *dev,
392 				struct comedi_subdevice *s,
393 				struct comedi_insn *insn,
394 				unsigned int *data)
395 {
396 	struct vmk80xx_private *devpriv = dev->private;
397 	unsigned char *rx_buf = devpriv->usb_rx_buf;
398 	unsigned char *tx_buf = devpriv->usb_tx_buf;
399 	int reg, cmd;
400 	int ret = 0;
401 
402 	if (devpriv->model == VMK8061_MODEL) {
403 		reg = VMK8061_DO_REG;
404 		cmd = VMK8061_CMD_DO;
405 	} else { /* VMK8055_MODEL */
406 		reg = VMK8055_DO_REG;
407 		cmd = VMK8055_CMD_WRT_AD;
408 	}
409 
410 	down(&devpriv->limit_sem);
411 
412 	if (comedi_dio_update_state(s, data)) {
413 		tx_buf[reg] = s->state;
414 		ret = vmk80xx_write_packet(dev, cmd);
415 		if (ret)
416 			goto out;
417 	}
418 
419 	if (devpriv->model == VMK8061_MODEL) {
420 		tx_buf[0] = VMK8061_CMD_RD_DO;
421 		ret = vmk80xx_read_packet(dev);
422 		if (ret)
423 			goto out;
424 		data[1] = rx_buf[reg];
425 	} else {
426 		data[1] = s->state;
427 	}
428 
429 out:
430 	up(&devpriv->limit_sem);
431 
432 	return ret ? ret : insn->n;
433 }
434 
vmk80xx_cnt_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)435 static int vmk80xx_cnt_insn_read(struct comedi_device *dev,
436 				 struct comedi_subdevice *s,
437 				 struct comedi_insn *insn,
438 				 unsigned int *data)
439 {
440 	struct vmk80xx_private *devpriv = dev->private;
441 	int chan;
442 	int reg[2];
443 	int n;
444 
445 	down(&devpriv->limit_sem);
446 	chan = CR_CHAN(insn->chanspec);
447 
448 	switch (devpriv->model) {
449 	case VMK8055_MODEL:
450 		if (!chan)
451 			reg[0] = VMK8055_CNT1_REG;
452 		else
453 			reg[0] = VMK8055_CNT2_REG;
454 		break;
455 	case VMK8061_MODEL:
456 	default:
457 		reg[0] = VMK8061_CNT_REG;
458 		reg[1] = VMK8061_CNT_REG;
459 		devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_CNT;
460 		break;
461 	}
462 
463 	for (n = 0; n < insn->n; n++) {
464 		if (vmk80xx_read_packet(dev))
465 			break;
466 
467 		if (devpriv->model == VMK8055_MODEL)
468 			data[n] = devpriv->usb_rx_buf[reg[0]];
469 		else /* VMK8061_MODEL */
470 			data[n] = devpriv->usb_rx_buf[reg[0] * (chan + 1) + 1]
471 			    + 256 * devpriv->usb_rx_buf[reg[1] * 2 + 2];
472 	}
473 
474 	up(&devpriv->limit_sem);
475 
476 	return n;
477 }
478 
vmk80xx_cnt_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)479 static int vmk80xx_cnt_insn_config(struct comedi_device *dev,
480 				   struct comedi_subdevice *s,
481 				   struct comedi_insn *insn,
482 				   unsigned int *data)
483 {
484 	struct vmk80xx_private *devpriv = dev->private;
485 	unsigned int chan = CR_CHAN(insn->chanspec);
486 	int cmd;
487 	int reg;
488 	int ret;
489 
490 	down(&devpriv->limit_sem);
491 	switch (data[0]) {
492 	case INSN_CONFIG_RESET:
493 		if (devpriv->model == VMK8055_MODEL) {
494 			if (!chan) {
495 				cmd = VMK8055_CMD_RST_CNT1;
496 				reg = VMK8055_CNT1_REG;
497 			} else {
498 				cmd = VMK8055_CMD_RST_CNT2;
499 				reg = VMK8055_CNT2_REG;
500 			}
501 			devpriv->usb_tx_buf[reg] = 0x00;
502 		} else {
503 			cmd = VMK8061_CMD_RST_CNT;
504 		}
505 		ret = vmk80xx_write_packet(dev, cmd);
506 		break;
507 	default:
508 		ret = -EINVAL;
509 		break;
510 	}
511 	up(&devpriv->limit_sem);
512 
513 	return ret ? ret : insn->n;
514 }
515 
vmk80xx_cnt_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)516 static int vmk80xx_cnt_insn_write(struct comedi_device *dev,
517 				  struct comedi_subdevice *s,
518 				  struct comedi_insn *insn,
519 				  unsigned int *data)
520 {
521 	struct vmk80xx_private *devpriv = dev->private;
522 	unsigned long debtime;
523 	unsigned long val;
524 	int chan;
525 	int cmd;
526 	int n;
527 
528 	down(&devpriv->limit_sem);
529 	chan = CR_CHAN(insn->chanspec);
530 
531 	if (!chan)
532 		cmd = VMK8055_CMD_DEB1_TIME;
533 	else
534 		cmd = VMK8055_CMD_DEB2_TIME;
535 
536 	for (n = 0; n < insn->n; n++) {
537 		debtime = data[n];
538 		if (debtime == 0)
539 			debtime = 1;
540 
541 		/* TODO: Prevent overflows */
542 		if (debtime > 7450)
543 			debtime = 7450;
544 
545 		val = int_sqrt(debtime * 1000 / 115);
546 		if (((val + 1) * val) < debtime * 1000 / 115)
547 			val += 1;
548 
549 		devpriv->usb_tx_buf[6 + chan] = val;
550 
551 		if (vmk80xx_write_packet(dev, cmd))
552 			break;
553 	}
554 
555 	up(&devpriv->limit_sem);
556 
557 	return n;
558 }
559 
vmk80xx_pwm_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)560 static int vmk80xx_pwm_insn_read(struct comedi_device *dev,
561 				 struct comedi_subdevice *s,
562 				 struct comedi_insn *insn,
563 				 unsigned int *data)
564 {
565 	struct vmk80xx_private *devpriv = dev->private;
566 	unsigned char *tx_buf;
567 	unsigned char *rx_buf;
568 	int reg[2];
569 	int n;
570 
571 	down(&devpriv->limit_sem);
572 
573 	tx_buf = devpriv->usb_tx_buf;
574 	rx_buf = devpriv->usb_rx_buf;
575 
576 	reg[0] = VMK8061_PWM_REG1;
577 	reg[1] = VMK8061_PWM_REG2;
578 
579 	tx_buf[0] = VMK8061_CMD_RD_PWM;
580 
581 	for (n = 0; n < insn->n; n++) {
582 		if (vmk80xx_read_packet(dev))
583 			break;
584 
585 		data[n] = rx_buf[reg[0]] + 4 * rx_buf[reg[1]];
586 	}
587 
588 	up(&devpriv->limit_sem);
589 
590 	return n;
591 }
592 
vmk80xx_pwm_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)593 static int vmk80xx_pwm_insn_write(struct comedi_device *dev,
594 				  struct comedi_subdevice *s,
595 				  struct comedi_insn *insn,
596 				  unsigned int *data)
597 {
598 	struct vmk80xx_private *devpriv = dev->private;
599 	unsigned char *tx_buf;
600 	int reg[2];
601 	int cmd;
602 	int n;
603 
604 	down(&devpriv->limit_sem);
605 
606 	tx_buf = devpriv->usb_tx_buf;
607 
608 	reg[0] = VMK8061_PWM_REG1;
609 	reg[1] = VMK8061_PWM_REG2;
610 
611 	cmd = VMK8061_CMD_OUT_PWM;
612 
613 	/*
614 	 * The followin piece of code was translated from the inline
615 	 * assembler code in the DLL source code.
616 	 *
617 	 * asm
618 	 *   mov eax, k  ; k is the value (data[n])
619 	 *   and al, 03h ; al are the lower 8 bits of eax
620 	 *   mov lo, al  ; lo is the low part (tx_buf[reg[0]])
621 	 *   mov eax, k
622 	 *   shr eax, 2  ; right shift eax register by 2
623 	 *   mov hi, al  ; hi is the high part (tx_buf[reg[1]])
624 	 * end;
625 	 */
626 	for (n = 0; n < insn->n; n++) {
627 		tx_buf[reg[0]] = (unsigned char)(data[n] & 0x03);
628 		tx_buf[reg[1]] = (unsigned char)(data[n] >> 2) & 0xff;
629 
630 		if (vmk80xx_write_packet(dev, cmd))
631 			break;
632 	}
633 
634 	up(&devpriv->limit_sem);
635 
636 	return n;
637 }
638 
vmk80xx_find_usb_endpoints(struct comedi_device * dev)639 static int vmk80xx_find_usb_endpoints(struct comedi_device *dev)
640 {
641 	struct vmk80xx_private *devpriv = dev->private;
642 	struct usb_interface *intf = comedi_to_usb_interface(dev);
643 	struct usb_host_interface *iface_desc = intf->cur_altsetting;
644 	struct usb_endpoint_descriptor *ep_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 
vmk80xx_alloc_usb_buffers(struct comedi_device * dev)666 static 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 
vmk80xx_init_subdevices(struct comedi_device * dev)684 static 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 
vmk80xx_auto_attach(struct comedi_device * dev,unsigned long context)773 static 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 
vmk80xx_detach(struct comedi_device * dev)812 static 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 
830 static 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 
vmk80xx_usb_probe(struct usb_interface * intf,const struct usb_device_id * id)837 static 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 
843 static 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 };
858 MODULE_DEVICE_TABLE(usb, vmk80xx_usb_id_table);
859 
860 static 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 };
866 module_comedi_usb_driver(vmk80xx_driver, vmk80xx_usb_driver);
867 
868 MODULE_AUTHOR("Manuel Gebele <forensixs@gmx.de>");
869 MODULE_DESCRIPTION("Velleman USB Board Low-Level Driver");
870 MODULE_LICENSE("GPL");
871