linux/drivers/staging/comedi/drivers/ni_usb6501.c
<<
>>
Prefs
   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
 100#include "../comedi_usb.h"
 101
 102#define NI6501_TIMEOUT  1000
 103
 104/* Port request packets */
 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/* Counter request packets */
 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/* Response packets */
 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        /* Check if results are valid */
 236
 237        if (command == READ_PORT) {
 238                bitmap[0] = devpriv->usb_rx_buf[14];
 239                /* mask bitmap for comparing */
 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                /* Setup tx packet: bytes 12,13,14,15 hold the */
 291                /* u32 counter value (Big Endian)              */
 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        /* Check if results are valid */
 320
 321        if (command == READ_COUNTER) {
 322                int i;
 323
 324                /* Read counter value: bytes 12,13,14,15 of rx packet */
 325                /* hold the u32 counter value (Big Endian)            */
 326                *val = be32_to_cpu(*((__be32 *)&devpriv->usb_rx_buf[12]));
 327
 328                /* mask counter value for comparing */
 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        /* Digital Input/Output subdevice */
 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        /* Counter subdevice */
 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