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