1/*
2 * comedi/drivers/ni_usb6501.c
3 * Comedi driver for National Instruments USB-6501
4 *
5 * COMEDI - Linux Control and Measurement Device Interface
6 * Copyright (C) 2014 Luca Ellero <luca.ellero@brickedbrain.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 */
18
19/*
20 * Driver: ni_usb6501
21 * Description: National Instruments USB-6501 module
22 * Devices: [National Instruments] USB-6501 (ni_usb6501)
23 * Author: Luca Ellero <luca.ellero@brickedbrain.com>
24 * Updated: 8 Sep 2014
25 * Status: works
26 *
27 *
28 * Configuration Options:
29 * none
30 */
31
32/*
33 * NI-6501 - USB PROTOCOL DESCRIPTION
34 *
35 * Every command is composed by two USB packets:
36 *	- request (out)
37 *	- response (in)
38 *
39 * Every packet is at least 12 bytes long, here is the meaning of
40 * every field (all values are hex):
41 *
42 *	byte 0 is always 00
43 *	byte 1 is always 01
44 *	byte 2 is always 00
45 *	byte 3 is the total packet length
46 *
47 *	byte 4 is always 00
48 *	byte 5 is is the total packet length - 4
49 *	byte 6 is always 01
50 *	byte 7 is the command
51 *
52 *	byte 8 is 02 (request) or 00 (response)
53 *	byte 9 is 00 (response) or 10 (port request) or 20 (counter request)
54 *	byte 10 is always 00
55 *	byte 11 is 00 (request) or 02 (response)
56 *
57 * PORT PACKETS
58 *
59 *	CMD: 0xE READ_PORT
60 *	REQ: 00 01 00 10 00 0C 01 0E 02 10 00 00 00 03 <PORT> 00
61 *	RES: 00 01 00 10 00 0C 01 00 00 00 00 02 00 03 <BMAP> 00
62 *
63 *	CMD: 0xF WRITE_PORT
64 *	REQ: 00 01 00 14 00 10 01 0F 02 10 00 00 00 03 <PORT> 00 03 <BMAP> 00 00
65 *	RES: 00 01 00 0C 00 08 01 00 00 00 00 02
66 *
67 *	CMD: 0x12 SET_PORT_DIR (0 = input, 1 = output)
68 *	REQ: 00 01 00 18 00 14 01 12 02 10 00 00
69 *	     00 05 <PORT 0> <PORT 1> <PORT 2> 00 05 00 00 00 00 00
70 *	RES: 00 01 00 0C 00 08 01 00 00 00 00 02
71 *
72 * COUNTER PACKETS
73 *
74 *	CMD 0x9: START_COUNTER
75 *	REQ: 00 01 00 0C 00 08 01 09 02 20 00 00
76 *	RES: 00 01 00 0C 00 08 01 00 00 00 00 02
77 *
78 *	CMD 0xC: STOP_COUNTER
79 *	REQ: 00 01 00 0C 00 08 01 0C 02 20 00 00
80 *	RES: 00 01 00 0C 00 08 01 00 00 00 00 02
81 *
82 *	CMD 0xE: READ_COUNTER
83 *	REQ: 00 01 00 0C 00 08 01 0E 02 20 00 00
84 *	RES: 00 01 00 10 00 0C 01 00 00 00 00 02 <u32 counter value, Big Endian>
85 *
86 *	CMD 0xF: WRITE_COUNTER
87 *	REQ: 00 01 00 10 00 0C 01 0F 02 20 00 00 <u32 counter value, Big Endian>
88 *	RES: 00 01 00 0C 00 08 01 00 00 00 00 02
89 *
90 *
91 *	Please  visit http://www.brickedbrain.com if you need
92 *	additional information or have any questions.
93 *
94 */
95
96#include <linux/kernel.h>
97#include <linux/module.h>
98#include <linux/slab.h>
99#include <linux/usb.h>
100
101#include "../comedidev.h"
102
103#define	NI6501_TIMEOUT	1000
104
105/* Port request packets */
106static const u8 READ_PORT_REQUEST[]	= {0x00, 0x01, 0x00, 0x10,
107					   0x00, 0x0C, 0x01, 0x0E,
108					   0x02, 0x10, 0x00, 0x00,
109					   0x00, 0x03, 0x00, 0x00};
110
111static const u8 WRITE_PORT_REQUEST[]	= {0x00, 0x01, 0x00, 0x14,
112					   0x00, 0x10, 0x01, 0x0F,
113					   0x02, 0x10, 0x00, 0x00,
114					   0x00, 0x03, 0x00, 0x00,
115					   0x03, 0x00, 0x00, 0x00};
116
117static const u8 SET_PORT_DIR_REQUEST[]	= {0x00, 0x01, 0x00, 0x18,
118					   0x00, 0x14, 0x01, 0x12,
119					   0x02, 0x10, 0x00, 0x00,
120					   0x00, 0x05, 0x00, 0x00,
121					   0x00, 0x00, 0x05, 0x00,
122					   0x00, 0x00, 0x00, 0x00};
123
124/* Counter request packets */
125static const u8 START_COUNTER_REQUEST[]	= {0x00, 0x01, 0x00, 0x0C,
126					   0x00, 0x08, 0x01, 0x09,
127					   0x02, 0x20, 0x00, 0x00};
128
129static const u8 STOP_COUNTER_REQUEST[]	= {0x00, 0x01, 0x00, 0x0C,
130					   0x00, 0x08, 0x01, 0x0C,
131					   0x02, 0x20, 0x00, 0x00};
132
133static const u8 READ_COUNTER_REQUEST[]	= {0x00, 0x01, 0x00, 0x0C,
134					   0x00, 0x08, 0x01, 0x0E,
135					   0x02, 0x20, 0x00, 0x00};
136
137static const u8 WRITE_COUNTER_REQUEST[]	= {0x00, 0x01, 0x00, 0x10,
138					   0x00, 0x0C, 0x01, 0x0F,
139					   0x02, 0x20, 0x00, 0x00,
140					   0x00, 0x00, 0x00, 0x00};
141
142/* Response packets */
143static const u8 GENERIC_RESPONSE[]	= {0x00, 0x01, 0x00, 0x0C,
144					   0x00, 0x08, 0x01, 0x00,
145					   0x00, 0x00, 0x00, 0x02};
146
147static const u8 READ_PORT_RESPONSE[]	= {0x00, 0x01, 0x00, 0x10,
148					   0x00, 0x0C, 0x01, 0x00,
149					   0x00, 0x00, 0x00, 0x02,
150					   0x00, 0x03, 0x00, 0x00};
151
152static const u8 READ_COUNTER_RESPONSE[]	= {0x00, 0x01, 0x00, 0x10,
153					   0x00, 0x0C, 0x01, 0x00,
154					   0x00, 0x00, 0x00, 0x02,
155					   0x00, 0x00, 0x00, 0x00};
156
157enum commands {
158	READ_PORT,
159	WRITE_PORT,
160	SET_PORT_DIR,
161	START_COUNTER,
162	STOP_COUNTER,
163	READ_COUNTER,
164	WRITE_COUNTER
165};
166
167struct ni6501_private {
168	struct usb_endpoint_descriptor *ep_rx;
169	struct usb_endpoint_descriptor *ep_tx;
170	struct semaphore sem;
171	u8 *usb_rx_buf;
172	u8 *usb_tx_buf;
173};
174
175static int ni6501_port_command(struct comedi_device *dev, int command,
176			       const u8 *port, u8 *bitmap)
177{
178	struct usb_device *usb = comedi_to_usb_dev(dev);
179	struct ni6501_private *devpriv = dev->private;
180	int request_size, response_size;
181	u8 *tx = devpriv->usb_tx_buf;
182	int ret;
183
184	if (command != SET_PORT_DIR && !bitmap)
185		return -EINVAL;
186
187	down(&devpriv->sem);
188
189	switch (command) {
190	case READ_PORT:
191		request_size = sizeof(READ_PORT_REQUEST);
192		response_size = sizeof(READ_PORT_RESPONSE);
193		memcpy(tx, READ_PORT_REQUEST, request_size);
194		tx[14] = port[0];
195		break;
196	case WRITE_PORT:
197		request_size = sizeof(WRITE_PORT_REQUEST);
198		response_size = sizeof(GENERIC_RESPONSE);
199		memcpy(tx, WRITE_PORT_REQUEST, request_size);
200		tx[14] = port[0];
201		tx[17] = bitmap[0];
202		break;
203	case SET_PORT_DIR:
204		request_size = sizeof(SET_PORT_DIR_REQUEST);
205		response_size = sizeof(GENERIC_RESPONSE);
206		memcpy(tx, SET_PORT_DIR_REQUEST, request_size);
207		tx[14] = port[0];
208		tx[15] = port[1];
209		tx[16] = port[2];
210		break;
211	default:
212		ret = -EINVAL;
213		goto end;
214	}
215
216	ret = usb_bulk_msg(usb,
217			   usb_sndbulkpipe(usb,
218					   devpriv->ep_tx->bEndpointAddress),
219			   devpriv->usb_tx_buf,
220			   request_size,
221			   NULL,
222			   NI6501_TIMEOUT);
223	if (ret)
224		goto end;
225
226	ret = usb_bulk_msg(usb,
227			   usb_rcvbulkpipe(usb,
228					   devpriv->ep_rx->bEndpointAddress),
229			   devpriv->usb_rx_buf,
230			   response_size,
231			   NULL,
232			   NI6501_TIMEOUT);
233	if (ret)
234		goto end;
235
236	/* Check if results are valid */
237
238	if (command == READ_PORT) {
239		bitmap[0] = devpriv->usb_rx_buf[14];
240		/* mask bitmap for comparing */
241		devpriv->usb_rx_buf[14] = 0x00;
242
243		if (memcmp(devpriv->usb_rx_buf, READ_PORT_RESPONSE,
244			   sizeof(READ_PORT_RESPONSE))) {
245			ret = -EINVAL;
246		}
247	} else if (memcmp(devpriv->usb_rx_buf, GENERIC_RESPONSE,
248			  sizeof(GENERIC_RESPONSE))) {
249		ret = -EINVAL;
250	}
251end:
252	up(&devpriv->sem);
253
254	return ret;
255}
256
257static int ni6501_counter_command(struct comedi_device *dev, int command,
258				  u32 *val)
259{
260	struct usb_device *usb = comedi_to_usb_dev(dev);
261	struct ni6501_private *devpriv = dev->private;
262	int request_size, response_size;
263	u8 *tx = devpriv->usb_tx_buf;
264	int ret;
265
266	if ((command == READ_COUNTER || command ==  WRITE_COUNTER) && !val)
267		return -EINVAL;
268
269	down(&devpriv->sem);
270
271	switch (command) {
272	case START_COUNTER:
273		request_size = sizeof(START_COUNTER_REQUEST);
274		response_size = sizeof(GENERIC_RESPONSE);
275		memcpy(tx, START_COUNTER_REQUEST, request_size);
276		break;
277	case STOP_COUNTER:
278		request_size = sizeof(STOP_COUNTER_REQUEST);
279		response_size = sizeof(GENERIC_RESPONSE);
280		memcpy(tx, STOP_COUNTER_REQUEST, request_size);
281		break;
282	case READ_COUNTER:
283		request_size = sizeof(READ_COUNTER_REQUEST);
284		response_size = sizeof(READ_COUNTER_RESPONSE);
285		memcpy(tx, READ_COUNTER_REQUEST, request_size);
286		break;
287	case WRITE_COUNTER:
288		request_size = sizeof(WRITE_COUNTER_REQUEST);
289		response_size = sizeof(GENERIC_RESPONSE);
290		memcpy(tx, WRITE_COUNTER_REQUEST, request_size);
291		/* Setup tx packet: bytes 12,13,14,15 hold the */
292		/* u32 counter value (Big Endian)	       */
293		*((__be32 *)&tx[12]) = cpu_to_be32(*val);
294		break;
295	default:
296		ret = -EINVAL;
297		goto end;
298	}
299
300	ret = usb_bulk_msg(usb,
301			   usb_sndbulkpipe(usb,
302					   devpriv->ep_tx->bEndpointAddress),
303			   devpriv->usb_tx_buf,
304			   request_size,
305			   NULL,
306			   NI6501_TIMEOUT);
307	if (ret)
308		goto end;
309
310	ret = usb_bulk_msg(usb,
311			   usb_rcvbulkpipe(usb,
312					   devpriv->ep_rx->bEndpointAddress),
313			   devpriv->usb_rx_buf,
314			   response_size,
315			   NULL,
316			   NI6501_TIMEOUT);
317	if (ret)
318		goto end;
319
320	/* Check if results are valid */
321
322	if (command == READ_COUNTER) {
323		int i;
324
325		/* Read counter value: bytes 12,13,14,15 of rx packet */
326		/* hold the u32 counter value (Big Endian)	      */
327		*val = be32_to_cpu(*((__be32 *)&devpriv->usb_rx_buf[12]));
328
329		/* mask counter value for comparing */
330		for (i = 12; i < sizeof(READ_COUNTER_RESPONSE); ++i)
331			devpriv->usb_rx_buf[i] = 0x00;
332
333		if (memcmp(devpriv->usb_rx_buf, READ_COUNTER_RESPONSE,
334			   sizeof(READ_COUNTER_RESPONSE))) {
335			ret = -EINVAL;
336		}
337	} else if (memcmp(devpriv->usb_rx_buf, GENERIC_RESPONSE,
338			  sizeof(GENERIC_RESPONSE))) {
339		ret = -EINVAL;
340	}
341end:
342	up(&devpriv->sem);
343
344	return ret;
345}
346
347static int ni6501_dio_insn_config(struct comedi_device *dev,
348				  struct comedi_subdevice *s,
349				  struct comedi_insn *insn,
350				  unsigned int *data)
351{
352	int ret;
353	u8 port[3];
354
355	ret = comedi_dio_insn_config(dev, s, insn, data, 0);
356	if (ret)
357		return ret;
358
359	port[0] = (s->io_bits) & 0xff;
360	port[1] = (s->io_bits >> 8) & 0xff;
361	port[2] = (s->io_bits >> 16) & 0xff;
362
363	ret = ni6501_port_command(dev, SET_PORT_DIR, port, NULL);
364	if (ret)
365		return ret;
366
367	return insn->n;
368}
369
370static int ni6501_dio_insn_bits(struct comedi_device *dev,
371				struct comedi_subdevice *s,
372				struct comedi_insn *insn,
373				unsigned int *data)
374{
375	unsigned int mask;
376	int ret;
377	u8 port;
378	u8 bitmap;
379
380	mask = comedi_dio_update_state(s, data);
381
382	for (port = 0; port < 3; port++) {
383		if (mask & (0xFF << port * 8)) {
384			bitmap = (s->state >> port * 8) & 0xFF;
385			ret = ni6501_port_command(dev, WRITE_PORT,
386						  &port, &bitmap);
387			if (ret)
388				return ret;
389		}
390	}
391
392	data[1] = 0;
393
394	for (port = 0; port < 3; port++) {
395		ret = ni6501_port_command(dev, READ_PORT, &port, &bitmap);
396		if (ret)
397			return ret;
398		data[1] |= bitmap << port * 8;
399	}
400
401	return insn->n;
402}
403
404static int ni6501_cnt_insn_config(struct comedi_device *dev,
405				  struct comedi_subdevice *s,
406				  struct comedi_insn *insn,
407				  unsigned int *data)
408{
409	int ret;
410	u32 val = 0;
411
412	switch (data[0]) {
413	case INSN_CONFIG_ARM:
414		ret = ni6501_counter_command(dev, START_COUNTER, NULL);
415		break;
416	case INSN_CONFIG_DISARM:
417		ret = ni6501_counter_command(dev, STOP_COUNTER, NULL);
418		break;
419	case INSN_CONFIG_RESET:
420		ret = ni6501_counter_command(dev, STOP_COUNTER, NULL);
421		if (ret)
422			break;
423		ret = ni6501_counter_command(dev, WRITE_COUNTER, &val);
424		break;
425	default:
426		return -EINVAL;
427	}
428
429	return ret ? ret : insn->n;
430}
431
432static int ni6501_cnt_insn_read(struct comedi_device *dev,
433				struct comedi_subdevice *s,
434				struct comedi_insn *insn,
435				unsigned int *data)
436{
437	int ret;
438	u32 val;
439	unsigned int i;
440
441	for (i = 0; i < insn->n; i++) {
442		ret = ni6501_counter_command(dev, READ_COUNTER,	&val);
443		if (ret)
444			return ret;
445		data[i] = val;
446	}
447
448	return insn->n;
449}
450
451static int ni6501_cnt_insn_write(struct comedi_device *dev,
452				 struct comedi_subdevice *s,
453				 struct comedi_insn *insn,
454				 unsigned int *data)
455{
456	int ret;
457
458	if (insn->n) {
459		u32 val = data[insn->n - 1];
460
461		ret = ni6501_counter_command(dev, WRITE_COUNTER, &val);
462		if (ret)
463			return ret;
464	}
465
466	return insn->n;
467}
468
469static int ni6501_alloc_usb_buffers(struct comedi_device *dev)
470{
471	struct ni6501_private *devpriv = dev->private;
472	size_t size;
473
474	size = le16_to_cpu(devpriv->ep_rx->wMaxPacketSize);
475	devpriv->usb_rx_buf = kzalloc(size, GFP_KERNEL);
476	if (!devpriv->usb_rx_buf)
477		return -ENOMEM;
478
479	size = le16_to_cpu(devpriv->ep_tx->wMaxPacketSize);
480	devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL);
481	if (!devpriv->usb_tx_buf) {
482		kfree(devpriv->usb_rx_buf);
483		return -ENOMEM;
484	}
485
486	return 0;
487}
488
489static int ni6501_find_endpoints(struct comedi_device *dev)
490{
491	struct usb_interface *intf = comedi_to_usb_interface(dev);
492	struct ni6501_private *devpriv = dev->private;
493	struct usb_host_interface *iface_desc = intf->cur_altsetting;
494	struct usb_endpoint_descriptor *ep_desc;
495	int i;
496
497	if (iface_desc->desc.bNumEndpoints != 2) {
498		dev_err(dev->class_dev, "Wrong number of endpoints\n");
499		return -ENODEV;
500	}
501
502	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
503		ep_desc = &iface_desc->endpoint[i].desc;
504
505		if (usb_endpoint_is_bulk_in(ep_desc)) {
506			if (!devpriv->ep_rx)
507				devpriv->ep_rx = ep_desc;
508			continue;
509		}
510
511		if (usb_endpoint_is_bulk_out(ep_desc)) {
512			if (!devpriv->ep_tx)
513				devpriv->ep_tx = ep_desc;
514			continue;
515		}
516	}
517
518	if (!devpriv->ep_rx || !devpriv->ep_tx)
519		return -ENODEV;
520
521	return 0;
522}
523
524static int ni6501_auto_attach(struct comedi_device *dev,
525			      unsigned long context)
526{
527	struct usb_interface *intf = comedi_to_usb_interface(dev);
528	struct ni6501_private *devpriv;
529	struct comedi_subdevice *s;
530	int ret;
531
532	devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
533	if (!devpriv)
534		return -ENOMEM;
535
536	ret = ni6501_find_endpoints(dev);
537	if (ret)
538		return ret;
539
540	ret = ni6501_alloc_usb_buffers(dev);
541	if (ret)
542		return ret;
543
544	sema_init(&devpriv->sem, 1);
545	usb_set_intfdata(intf, devpriv);
546
547	ret = comedi_alloc_subdevices(dev, 2);
548	if (ret)
549		return ret;
550
551	/* Digital Input/Output subdevice */
552	s = &dev->subdevices[0];
553	s->type		= COMEDI_SUBD_DIO;
554	s->subdev_flags	= SDF_READABLE | SDF_WRITABLE;
555	s->n_chan	= 24;
556	s->maxdata	= 1;
557	s->range_table	= &range_digital;
558	s->insn_bits	= ni6501_dio_insn_bits;
559	s->insn_config	= ni6501_dio_insn_config;
560
561	/* Counter subdevice */
562	s = &dev->subdevices[1];
563	s->type		= COMEDI_SUBD_COUNTER;
564	s->subdev_flags	= SDF_READABLE | SDF_WRITEABLE | SDF_LSAMPL;
565	s->n_chan	= 1;
566	s->maxdata	= 0xffffffff;
567	s->insn_read	= ni6501_cnt_insn_read;
568	s->insn_write	= ni6501_cnt_insn_write;
569	s->insn_config	= ni6501_cnt_insn_config;
570
571	return 0;
572}
573
574static void ni6501_detach(struct comedi_device *dev)
575{
576	struct usb_interface *intf = comedi_to_usb_interface(dev);
577	struct ni6501_private *devpriv = dev->private;
578
579	if (!devpriv)
580		return;
581
582	down(&devpriv->sem);
583
584	usb_set_intfdata(intf, NULL);
585
586	kfree(devpriv->usb_rx_buf);
587	kfree(devpriv->usb_tx_buf);
588
589	up(&devpriv->sem);
590}
591
592static struct comedi_driver ni6501_driver = {
593	.module		= THIS_MODULE,
594	.driver_name	= "ni6501",
595	.auto_attach	= ni6501_auto_attach,
596	.detach		= ni6501_detach,
597};
598
599static int ni6501_usb_probe(struct usb_interface *intf,
600			    const struct usb_device_id *id)
601{
602	return comedi_usb_auto_config(intf, &ni6501_driver, id->driver_info);
603}
604
605static const struct usb_device_id ni6501_usb_table[] = {
606	{ USB_DEVICE(0x3923, 0x718a) },
607	{ }
608};
609MODULE_DEVICE_TABLE(usb, ni6501_usb_table);
610
611static struct usb_driver ni6501_usb_driver = {
612	.name		= "ni6501",
613	.id_table	= ni6501_usb_table,
614	.probe		= ni6501_usb_probe,
615	.disconnect	= comedi_usb_auto_unconfig,
616};
617module_comedi_usb_driver(ni6501_driver, ni6501_usb_driver);
618
619MODULE_AUTHOR("Luca Ellero");
620MODULE_DESCRIPTION("Comedi driver for National Instruments USB-6501");
621MODULE_LICENSE("GPL");
622