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