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