linux/drivers/comedi/drivers/vmk80xx.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * vmk80xx.c
   4 * Velleman USB Board Low-Level Driver
   5 *
   6 * Copyright (C) 2009 Manuel Gebele <forensixs@gmx.de>, Germany
   7 *
   8 * COMEDI - Linux Control and Measurement Device Interface
   9 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
  10 */
  11
  12/*
  13 * Driver: vmk80xx
  14 * Description: Velleman USB Board Low-Level Driver
  15 * Devices: [Velleman] K8055 (K8055/VM110), K8061 (K8061/VM140),
  16 *   VM110 (K8055/VM110), VM140 (K8061/VM140)
  17 * Author: Manuel Gebele <forensixs@gmx.de>
  18 * Updated: Sun, 10 May 2009 11:14:59 +0200
  19 * Status: works
  20 *
  21 * Supports:
  22 *  - analog input
  23 *  - analog output
  24 *  - digital input
  25 *  - digital output
  26 *  - counter
  27 *  - pwm
  28 */
  29
  30#include <linux/kernel.h>
  31#include <linux/module.h>
  32#include <linux/mutex.h>
  33#include <linux/errno.h>
  34#include <linux/input.h>
  35#include <linux/slab.h>
  36#include <linux/poll.h>
  37#include <linux/uaccess.h>
  38#include <linux/comedi/comedi_usb.h>
  39
  40enum {
  41        DEVICE_VMK8055,
  42        DEVICE_VMK8061
  43};
  44
  45#define VMK8055_DI_REG          0x00
  46#define VMK8055_DO_REG          0x01
  47#define VMK8055_AO1_REG         0x02
  48#define VMK8055_AO2_REG         0x03
  49#define VMK8055_AI1_REG         0x02
  50#define VMK8055_AI2_REG         0x03
  51#define VMK8055_CNT1_REG        0x04
  52#define VMK8055_CNT2_REG        0x06
  53
  54#define VMK8061_CH_REG          0x01
  55#define VMK8061_DI_REG          0x01
  56#define VMK8061_DO_REG          0x01
  57#define VMK8061_PWM_REG1        0x01
  58#define VMK8061_PWM_REG2        0x02
  59#define VMK8061_CNT_REG         0x02
  60#define VMK8061_AO_REG          0x02
  61#define VMK8061_AI_REG1         0x02
  62#define VMK8061_AI_REG2         0x03
  63
  64#define VMK8055_CMD_RST         0x00
  65#define VMK8055_CMD_DEB1_TIME   0x01
  66#define VMK8055_CMD_DEB2_TIME   0x02
  67#define VMK8055_CMD_RST_CNT1    0x03
  68#define VMK8055_CMD_RST_CNT2    0x04
  69#define VMK8055_CMD_WRT_AD      0x05
  70
  71#define VMK8061_CMD_RD_AI       0x00
  72#define VMK8061_CMR_RD_ALL_AI   0x01    /* !non-active! */
  73#define VMK8061_CMD_SET_AO      0x02
  74#define VMK8061_CMD_SET_ALL_AO  0x03    /* !non-active! */
  75#define VMK8061_CMD_OUT_PWM     0x04
  76#define VMK8061_CMD_RD_DI       0x05
  77#define VMK8061_CMD_DO          0x06    /* !non-active! */
  78#define VMK8061_CMD_CLR_DO      0x07
  79#define VMK8061_CMD_SET_DO      0x08
  80#define VMK8061_CMD_RD_CNT      0x09    /* TODO: completely pointless? */
  81#define VMK8061_CMD_RST_CNT     0x0a    /* TODO: completely pointless? */
  82#define VMK8061_CMD_RD_VERSION  0x0b    /* internal usage */
  83#define VMK8061_CMD_RD_JMP_STAT 0x0c    /* TODO: not implemented yet */
  84#define VMK8061_CMD_RD_PWR_STAT 0x0d    /* internal usage */
  85#define VMK8061_CMD_RD_DO       0x0e
  86#define VMK8061_CMD_RD_AO       0x0f
  87#define VMK8061_CMD_RD_PWM      0x10
  88
  89#define IC3_VERSION             BIT(0)
  90#define IC6_VERSION             BIT(1)
  91
  92#define MIN_BUF_SIZE            64
  93#define PACKET_TIMEOUT          10000   /* ms */
  94
  95enum vmk80xx_model {
  96        VMK8055_MODEL,
  97        VMK8061_MODEL
  98};
  99
 100static const struct comedi_lrange vmk8061_range = {
 101        2, {
 102                UNI_RANGE(5),
 103                UNI_RANGE(10)
 104        }
 105};
 106
 107struct vmk80xx_board {
 108        const char *name;
 109        enum vmk80xx_model model;
 110        const struct comedi_lrange *range;
 111        int ai_nchans;
 112        unsigned int ai_maxdata;
 113        int ao_nchans;
 114        int di_nchans;
 115        unsigned int cnt_maxdata;
 116        int pwm_nchans;
 117        unsigned int pwm_maxdata;
 118};
 119
 120static const struct vmk80xx_board vmk80xx_boardinfo[] = {
 121        [DEVICE_VMK8055] = {
 122                .name           = "K8055 (VM110)",
 123                .model          = VMK8055_MODEL,
 124                .range          = &range_unipolar5,
 125                .ai_nchans      = 2,
 126                .ai_maxdata     = 0x00ff,
 127                .ao_nchans      = 2,
 128                .di_nchans      = 6,
 129                .cnt_maxdata    = 0xffff,
 130        },
 131        [DEVICE_VMK8061] = {
 132                .name           = "K8061 (VM140)",
 133                .model          = VMK8061_MODEL,
 134                .range          = &vmk8061_range,
 135                .ai_nchans      = 8,
 136                .ai_maxdata     = 0x03ff,
 137                .ao_nchans      = 8,
 138                .di_nchans      = 8,
 139                .cnt_maxdata    = 0,    /* unknown, device is not writeable */
 140                .pwm_nchans     = 1,
 141                .pwm_maxdata    = 0x03ff,
 142        },
 143};
 144
 145struct vmk80xx_private {
 146        struct usb_endpoint_descriptor *ep_rx;
 147        struct usb_endpoint_descriptor *ep_tx;
 148        struct semaphore limit_sem;
 149        unsigned char *usb_rx_buf;
 150        unsigned char *usb_tx_buf;
 151        enum vmk80xx_model model;
 152};
 153
 154static void vmk80xx_do_bulk_msg(struct comedi_device *dev)
 155{
 156        struct vmk80xx_private *devpriv = dev->private;
 157        struct usb_device *usb = comedi_to_usb_dev(dev);
 158        __u8 tx_addr;
 159        __u8 rx_addr;
 160        unsigned int tx_pipe;
 161        unsigned int rx_pipe;
 162        size_t tx_size;
 163        size_t rx_size;
 164
 165        tx_addr = devpriv->ep_tx->bEndpointAddress;
 166        rx_addr = devpriv->ep_rx->bEndpointAddress;
 167        tx_pipe = usb_sndbulkpipe(usb, tx_addr);
 168        rx_pipe = usb_rcvbulkpipe(usb, rx_addr);
 169        tx_size = usb_endpoint_maxp(devpriv->ep_tx);
 170        rx_size = usb_endpoint_maxp(devpriv->ep_rx);
 171
 172        usb_bulk_msg(usb, tx_pipe, devpriv->usb_tx_buf, tx_size, NULL,
 173                     PACKET_TIMEOUT);
 174
 175        usb_bulk_msg(usb, rx_pipe, devpriv->usb_rx_buf, rx_size, NULL,
 176                     PACKET_TIMEOUT);
 177}
 178
 179static int vmk80xx_read_packet(struct comedi_device *dev)
 180{
 181        struct vmk80xx_private *devpriv = dev->private;
 182        struct usb_device *usb = comedi_to_usb_dev(dev);
 183        struct usb_endpoint_descriptor *ep;
 184        unsigned int pipe;
 185
 186        if (devpriv->model == VMK8061_MODEL) {
 187                vmk80xx_do_bulk_msg(dev);
 188                return 0;
 189        }
 190
 191        ep = devpriv->ep_rx;
 192        pipe = usb_rcvintpipe(usb, ep->bEndpointAddress);
 193        return usb_interrupt_msg(usb, pipe, devpriv->usb_rx_buf,
 194                                 usb_endpoint_maxp(ep), NULL,
 195                                 PACKET_TIMEOUT);
 196}
 197
 198static int vmk80xx_write_packet(struct comedi_device *dev, int cmd)
 199{
 200        struct vmk80xx_private *devpriv = dev->private;
 201        struct usb_device *usb = comedi_to_usb_dev(dev);
 202        struct usb_endpoint_descriptor *ep;
 203        unsigned int pipe;
 204
 205        devpriv->usb_tx_buf[0] = cmd;
 206
 207        if (devpriv->model == VMK8061_MODEL) {
 208                vmk80xx_do_bulk_msg(dev);
 209                return 0;
 210        }
 211
 212        ep = devpriv->ep_tx;
 213        pipe = usb_sndintpipe(usb, ep->bEndpointAddress);
 214        return usb_interrupt_msg(usb, pipe, devpriv->usb_tx_buf,
 215                                 usb_endpoint_maxp(ep), NULL,
 216                                 PACKET_TIMEOUT);
 217}
 218
 219static int vmk80xx_reset_device(struct comedi_device *dev)
 220{
 221        struct vmk80xx_private *devpriv = dev->private;
 222        size_t size;
 223        int retval;
 224
 225        size = usb_endpoint_maxp(devpriv->ep_tx);
 226        memset(devpriv->usb_tx_buf, 0, size);
 227        retval = vmk80xx_write_packet(dev, VMK8055_CMD_RST);
 228        if (retval)
 229                return retval;
 230        /* set outputs to known state as we cannot read them */
 231        return vmk80xx_write_packet(dev, VMK8055_CMD_WRT_AD);
 232}
 233
 234static int vmk80xx_ai_insn_read(struct comedi_device *dev,
 235                                struct comedi_subdevice *s,
 236                                struct comedi_insn *insn,
 237                                unsigned int *data)
 238{
 239        struct vmk80xx_private *devpriv = dev->private;
 240        int chan;
 241        int reg[2];
 242        int n;
 243
 244        down(&devpriv->limit_sem);
 245        chan = CR_CHAN(insn->chanspec);
 246
 247        switch (devpriv->model) {
 248        case VMK8055_MODEL:
 249                if (!chan)
 250                        reg[0] = VMK8055_AI1_REG;
 251                else
 252                        reg[0] = VMK8055_AI2_REG;
 253                break;
 254        case VMK8061_MODEL:
 255        default:
 256                reg[0] = VMK8061_AI_REG1;
 257                reg[1] = VMK8061_AI_REG2;
 258                devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AI;
 259                devpriv->usb_tx_buf[VMK8061_CH_REG] = chan;
 260                break;
 261        }
 262
 263        for (n = 0; n < insn->n; n++) {
 264                if (vmk80xx_read_packet(dev))
 265                        break;
 266
 267                if (devpriv->model == VMK8055_MODEL) {
 268                        data[n] = devpriv->usb_rx_buf[reg[0]];
 269                        continue;
 270                }
 271
 272                /* VMK8061_MODEL */
 273                data[n] = devpriv->usb_rx_buf[reg[0]] + 256 *
 274                    devpriv->usb_rx_buf[reg[1]];
 275        }
 276
 277        up(&devpriv->limit_sem);
 278
 279        return n;
 280}
 281
 282static int vmk80xx_ao_insn_write(struct comedi_device *dev,
 283                                 struct comedi_subdevice *s,
 284                                 struct comedi_insn *insn,
 285                                 unsigned int *data)
 286{
 287        struct vmk80xx_private *devpriv = dev->private;
 288        int chan;
 289        int cmd;
 290        int reg;
 291        int n;
 292
 293        down(&devpriv->limit_sem);
 294        chan = CR_CHAN(insn->chanspec);
 295
 296        switch (devpriv->model) {
 297        case VMK8055_MODEL:
 298                cmd = VMK8055_CMD_WRT_AD;
 299                if (!chan)
 300                        reg = VMK8055_AO1_REG;
 301                else
 302                        reg = VMK8055_AO2_REG;
 303                break;
 304        default:                /* NOTE: avoid compiler warnings */
 305                cmd = VMK8061_CMD_SET_AO;
 306                reg = VMK8061_AO_REG;
 307                devpriv->usb_tx_buf[VMK8061_CH_REG] = chan;
 308                break;
 309        }
 310
 311        for (n = 0; n < insn->n; n++) {
 312                devpriv->usb_tx_buf[reg] = data[n];
 313
 314                if (vmk80xx_write_packet(dev, cmd))
 315                        break;
 316        }
 317
 318        up(&devpriv->limit_sem);
 319
 320        return n;
 321}
 322
 323static int vmk80xx_ao_insn_read(struct comedi_device *dev,
 324                                struct comedi_subdevice *s,
 325                                struct comedi_insn *insn,
 326                                unsigned int *data)
 327{
 328        struct vmk80xx_private *devpriv = dev->private;
 329        int chan;
 330        int reg;
 331        int n;
 332
 333        down(&devpriv->limit_sem);
 334        chan = CR_CHAN(insn->chanspec);
 335
 336        reg = VMK8061_AO_REG - 1;
 337
 338        devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_AO;
 339
 340        for (n = 0; n < insn->n; n++) {
 341                if (vmk80xx_read_packet(dev))
 342                        break;
 343
 344                data[n] = devpriv->usb_rx_buf[reg + chan];
 345        }
 346
 347        up(&devpriv->limit_sem);
 348
 349        return n;
 350}
 351
 352static int vmk80xx_di_insn_bits(struct comedi_device *dev,
 353                                struct comedi_subdevice *s,
 354                                struct comedi_insn *insn,
 355                                unsigned int *data)
 356{
 357        struct vmk80xx_private *devpriv = dev->private;
 358        unsigned char *rx_buf;
 359        int reg;
 360        int retval;
 361
 362        down(&devpriv->limit_sem);
 363
 364        rx_buf = devpriv->usb_rx_buf;
 365
 366        if (devpriv->model == VMK8061_MODEL) {
 367                reg = VMK8061_DI_REG;
 368                devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_DI;
 369        } else {
 370                reg = VMK8055_DI_REG;
 371        }
 372
 373        retval = vmk80xx_read_packet(dev);
 374
 375        if (!retval) {
 376                if (devpriv->model == VMK8055_MODEL)
 377                        data[1] = (((rx_buf[reg] >> 4) & 0x03) |
 378                                  ((rx_buf[reg] << 2) & 0x04) |
 379                                  ((rx_buf[reg] >> 3) & 0x18));
 380                else
 381                        data[1] = rx_buf[reg];
 382
 383                retval = 2;
 384        }
 385
 386        up(&devpriv->limit_sem);
 387
 388        return retval;
 389}
 390
 391static int vmk80xx_do_insn_bits(struct comedi_device *dev,
 392                                struct comedi_subdevice *s,
 393                                struct comedi_insn *insn,
 394                                unsigned int *data)
 395{
 396        struct vmk80xx_private *devpriv = dev->private;
 397        unsigned char *rx_buf = devpriv->usb_rx_buf;
 398        unsigned char *tx_buf = devpriv->usb_tx_buf;
 399        int reg, cmd;
 400        int ret = 0;
 401
 402        if (devpriv->model == VMK8061_MODEL) {
 403                reg = VMK8061_DO_REG;
 404                cmd = VMK8061_CMD_DO;
 405        } else { /* VMK8055_MODEL */
 406                reg = VMK8055_DO_REG;
 407                cmd = VMK8055_CMD_WRT_AD;
 408        }
 409
 410        down(&devpriv->limit_sem);
 411
 412        if (comedi_dio_update_state(s, data)) {
 413                tx_buf[reg] = s->state;
 414                ret = vmk80xx_write_packet(dev, cmd);
 415                if (ret)
 416                        goto out;
 417        }
 418
 419        if (devpriv->model == VMK8061_MODEL) {
 420                tx_buf[0] = VMK8061_CMD_RD_DO;
 421                ret = vmk80xx_read_packet(dev);
 422                if (ret)
 423                        goto out;
 424                data[1] = rx_buf[reg];
 425        } else {
 426                data[1] = s->state;
 427        }
 428
 429out:
 430        up(&devpriv->limit_sem);
 431
 432        return ret ? ret : insn->n;
 433}
 434
 435static int vmk80xx_cnt_insn_read(struct comedi_device *dev,
 436                                 struct comedi_subdevice *s,
 437                                 struct comedi_insn *insn,
 438                                 unsigned int *data)
 439{
 440        struct vmk80xx_private *devpriv = dev->private;
 441        int chan;
 442        int reg[2];
 443        int n;
 444
 445        down(&devpriv->limit_sem);
 446        chan = CR_CHAN(insn->chanspec);
 447
 448        switch (devpriv->model) {
 449        case VMK8055_MODEL:
 450                if (!chan)
 451                        reg[0] = VMK8055_CNT1_REG;
 452                else
 453                        reg[0] = VMK8055_CNT2_REG;
 454                break;
 455        case VMK8061_MODEL:
 456        default:
 457                reg[0] = VMK8061_CNT_REG;
 458                reg[1] = VMK8061_CNT_REG;
 459                devpriv->usb_tx_buf[0] = VMK8061_CMD_RD_CNT;
 460                break;
 461        }
 462
 463        for (n = 0; n < insn->n; n++) {
 464                if (vmk80xx_read_packet(dev))
 465                        break;
 466
 467                if (devpriv->model == VMK8055_MODEL)
 468                        data[n] = devpriv->usb_rx_buf[reg[0]];
 469                else /* VMK8061_MODEL */
 470                        data[n] = devpriv->usb_rx_buf[reg[0] * (chan + 1) + 1]
 471                            + 256 * devpriv->usb_rx_buf[reg[1] * 2 + 2];
 472        }
 473
 474        up(&devpriv->limit_sem);
 475
 476        return n;
 477}
 478
 479static int vmk80xx_cnt_insn_config(struct comedi_device *dev,
 480                                   struct comedi_subdevice *s,
 481                                   struct comedi_insn *insn,
 482                                   unsigned int *data)
 483{
 484        struct vmk80xx_private *devpriv = dev->private;
 485        unsigned int chan = CR_CHAN(insn->chanspec);
 486        int cmd;
 487        int reg;
 488        int ret;
 489
 490        down(&devpriv->limit_sem);
 491        switch (data[0]) {
 492        case INSN_CONFIG_RESET:
 493                if (devpriv->model == VMK8055_MODEL) {
 494                        if (!chan) {
 495                                cmd = VMK8055_CMD_RST_CNT1;
 496                                reg = VMK8055_CNT1_REG;
 497                        } else {
 498                                cmd = VMK8055_CMD_RST_CNT2;
 499                                reg = VMK8055_CNT2_REG;
 500                        }
 501                        devpriv->usb_tx_buf[reg] = 0x00;
 502                } else {
 503                        cmd = VMK8061_CMD_RST_CNT;
 504                }
 505                ret = vmk80xx_write_packet(dev, cmd);
 506                break;
 507        default:
 508                ret = -EINVAL;
 509                break;
 510        }
 511        up(&devpriv->limit_sem);
 512
 513        return ret ? ret : insn->n;
 514}
 515
 516static int vmk80xx_cnt_insn_write(struct comedi_device *dev,
 517                                  struct comedi_subdevice *s,
 518                                  struct comedi_insn *insn,
 519                                  unsigned int *data)
 520{
 521        struct vmk80xx_private *devpriv = dev->private;
 522        unsigned long debtime;
 523        unsigned long val;
 524        int chan;
 525        int cmd;
 526        int n;
 527
 528        down(&devpriv->limit_sem);
 529        chan = CR_CHAN(insn->chanspec);
 530
 531        if (!chan)
 532                cmd = VMK8055_CMD_DEB1_TIME;
 533        else
 534                cmd = VMK8055_CMD_DEB2_TIME;
 535
 536        for (n = 0; n < insn->n; n++) {
 537                debtime = data[n];
 538                if (debtime == 0)
 539                        debtime = 1;
 540
 541                /* TODO: Prevent overflows */
 542                if (debtime > 7450)
 543                        debtime = 7450;
 544
 545                val = int_sqrt(debtime * 1000 / 115);
 546                if (((val + 1) * val) < debtime * 1000 / 115)
 547                        val += 1;
 548
 549                devpriv->usb_tx_buf[6 + chan] = val;
 550
 551                if (vmk80xx_write_packet(dev, cmd))
 552                        break;
 553        }
 554
 555        up(&devpriv->limit_sem);
 556
 557        return n;
 558}
 559
 560static int vmk80xx_pwm_insn_read(struct comedi_device *dev,
 561                                 struct comedi_subdevice *s,
 562                                 struct comedi_insn *insn,
 563                                 unsigned int *data)
 564{
 565        struct vmk80xx_private *devpriv = dev->private;
 566        unsigned char *tx_buf;
 567        unsigned char *rx_buf;
 568        int reg[2];
 569        int n;
 570
 571        down(&devpriv->limit_sem);
 572
 573        tx_buf = devpriv->usb_tx_buf;
 574        rx_buf = devpriv->usb_rx_buf;
 575
 576        reg[0] = VMK8061_PWM_REG1;
 577        reg[1] = VMK8061_PWM_REG2;
 578
 579        tx_buf[0] = VMK8061_CMD_RD_PWM;
 580
 581        for (n = 0; n < insn->n; n++) {
 582                if (vmk80xx_read_packet(dev))
 583                        break;
 584
 585                data[n] = rx_buf[reg[0]] + 4 * rx_buf[reg[1]];
 586        }
 587
 588        up(&devpriv->limit_sem);
 589
 590        return n;
 591}
 592
 593static int vmk80xx_pwm_insn_write(struct comedi_device *dev,
 594                                  struct comedi_subdevice *s,
 595                                  struct comedi_insn *insn,
 596                                  unsigned int *data)
 597{
 598        struct vmk80xx_private *devpriv = dev->private;
 599        unsigned char *tx_buf;
 600        int reg[2];
 601        int cmd;
 602        int n;
 603
 604        down(&devpriv->limit_sem);
 605
 606        tx_buf = devpriv->usb_tx_buf;
 607
 608        reg[0] = VMK8061_PWM_REG1;
 609        reg[1] = VMK8061_PWM_REG2;
 610
 611        cmd = VMK8061_CMD_OUT_PWM;
 612
 613        /*
 614         * The followin piece of code was translated from the inline
 615         * assembler code in the DLL source code.
 616         *
 617         * asm
 618         *   mov eax, k  ; k is the value (data[n])
 619         *   and al, 03h ; al are the lower 8 bits of eax
 620         *   mov lo, al  ; lo is the low part (tx_buf[reg[0]])
 621         *   mov eax, k
 622         *   shr eax, 2  ; right shift eax register by 2
 623         *   mov hi, al  ; hi is the high part (tx_buf[reg[1]])
 624         * end;
 625         */
 626        for (n = 0; n < insn->n; n++) {
 627                tx_buf[reg[0]] = (unsigned char)(data[n] & 0x03);
 628                tx_buf[reg[1]] = (unsigned char)(data[n] >> 2) & 0xff;
 629
 630                if (vmk80xx_write_packet(dev, cmd))
 631                        break;
 632        }
 633
 634        up(&devpriv->limit_sem);
 635
 636        return n;
 637}
 638
 639static int vmk80xx_find_usb_endpoints(struct comedi_device *dev)
 640{
 641        struct vmk80xx_private *devpriv = dev->private;
 642        struct usb_interface *intf = comedi_to_usb_interface(dev);
 643        struct usb_host_interface *iface_desc = intf->cur_altsetting;
 644        struct usb_endpoint_descriptor *ep_desc;
 645        int i;
 646
 647        if (iface_desc->desc.bNumEndpoints != 2)
 648                return -ENODEV;
 649
 650        for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
 651                ep_desc = &iface_desc->endpoint[i].desc;
 652
 653                if (usb_endpoint_is_int_in(ep_desc) ||
 654                    usb_endpoint_is_bulk_in(ep_desc)) {
 655                        if (!devpriv->ep_rx)
 656                                devpriv->ep_rx = ep_desc;
 657                        continue;
 658                }
 659
 660                if (usb_endpoint_is_int_out(ep_desc) ||
 661                    usb_endpoint_is_bulk_out(ep_desc)) {
 662                        if (!devpriv->ep_tx)
 663                                devpriv->ep_tx = ep_desc;
 664                        continue;
 665                }
 666        }
 667
 668        if (!devpriv->ep_rx || !devpriv->ep_tx)
 669                return -ENODEV;
 670
 671        if (!usb_endpoint_maxp(devpriv->ep_rx) || !usb_endpoint_maxp(devpriv->ep_tx))
 672                return -EINVAL;
 673
 674        return 0;
 675}
 676
 677static int vmk80xx_alloc_usb_buffers(struct comedi_device *dev)
 678{
 679        struct vmk80xx_private *devpriv = dev->private;
 680        size_t size;
 681
 682        size = max(usb_endpoint_maxp(devpriv->ep_rx), MIN_BUF_SIZE);
 683        devpriv->usb_rx_buf = kzalloc(size, GFP_KERNEL);
 684        if (!devpriv->usb_rx_buf)
 685                return -ENOMEM;
 686
 687        size = max(usb_endpoint_maxp(devpriv->ep_rx), MIN_BUF_SIZE);
 688        devpriv->usb_tx_buf = kzalloc(size, GFP_KERNEL);
 689        if (!devpriv->usb_tx_buf)
 690                return -ENOMEM;
 691
 692        return 0;
 693}
 694
 695static int vmk80xx_init_subdevices(struct comedi_device *dev)
 696{
 697        const struct vmk80xx_board *board = dev->board_ptr;
 698        struct vmk80xx_private *devpriv = dev->private;
 699        struct comedi_subdevice *s;
 700        int n_subd;
 701        int ret;
 702
 703        down(&devpriv->limit_sem);
 704
 705        if (devpriv->model == VMK8055_MODEL)
 706                n_subd = 5;
 707        else
 708                n_subd = 6;
 709        ret = comedi_alloc_subdevices(dev, n_subd);
 710        if (ret) {
 711                up(&devpriv->limit_sem);
 712                return ret;
 713        }
 714
 715        /* Analog input subdevice */
 716        s = &dev->subdevices[0];
 717        s->type         = COMEDI_SUBD_AI;
 718        s->subdev_flags = SDF_READABLE | SDF_GROUND;
 719        s->n_chan       = board->ai_nchans;
 720        s->maxdata      = board->ai_maxdata;
 721        s->range_table  = board->range;
 722        s->insn_read    = vmk80xx_ai_insn_read;
 723
 724        /* Analog output subdevice */
 725        s = &dev->subdevices[1];
 726        s->type         = COMEDI_SUBD_AO;
 727        s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
 728        s->n_chan       = board->ao_nchans;
 729        s->maxdata      = 0x00ff;
 730        s->range_table  = board->range;
 731        s->insn_write   = vmk80xx_ao_insn_write;
 732        if (devpriv->model == VMK8061_MODEL) {
 733                s->subdev_flags |= SDF_READABLE;
 734                s->insn_read    = vmk80xx_ao_insn_read;
 735        }
 736
 737        /* Digital input subdevice */
 738        s = &dev->subdevices[2];
 739        s->type         = COMEDI_SUBD_DI;
 740        s->subdev_flags = SDF_READABLE;
 741        s->n_chan       = board->di_nchans;
 742        s->maxdata      = 1;
 743        s->range_table  = &range_digital;
 744        s->insn_bits    = vmk80xx_di_insn_bits;
 745
 746        /* Digital output subdevice */
 747        s = &dev->subdevices[3];
 748        s->type         = COMEDI_SUBD_DO;
 749        s->subdev_flags = SDF_WRITABLE;
 750        s->n_chan       = 8;
 751        s->maxdata      = 1;
 752        s->range_table  = &range_digital;
 753        s->insn_bits    = vmk80xx_do_insn_bits;
 754
 755        /* Counter subdevice */
 756        s = &dev->subdevices[4];
 757        s->type         = COMEDI_SUBD_COUNTER;
 758        s->subdev_flags = SDF_READABLE;
 759        s->n_chan       = 2;
 760        s->maxdata      = board->cnt_maxdata;
 761        s->insn_read    = vmk80xx_cnt_insn_read;
 762        s->insn_config  = vmk80xx_cnt_insn_config;
 763        if (devpriv->model == VMK8055_MODEL) {
 764                s->subdev_flags |= SDF_WRITABLE;
 765                s->insn_write   = vmk80xx_cnt_insn_write;
 766        }
 767
 768        /* PWM subdevice */
 769        if (devpriv->model == VMK8061_MODEL) {
 770                s = &dev->subdevices[5];
 771                s->type         = COMEDI_SUBD_PWM;
 772                s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
 773                s->n_chan       = board->pwm_nchans;
 774                s->maxdata      = board->pwm_maxdata;
 775                s->insn_read    = vmk80xx_pwm_insn_read;
 776                s->insn_write   = vmk80xx_pwm_insn_write;
 777        }
 778
 779        up(&devpriv->limit_sem);
 780
 781        return 0;
 782}
 783
 784static int vmk80xx_auto_attach(struct comedi_device *dev,
 785                               unsigned long context)
 786{
 787        struct usb_interface *intf = comedi_to_usb_interface(dev);
 788        const struct vmk80xx_board *board = NULL;
 789        struct vmk80xx_private *devpriv;
 790        int ret;
 791
 792        if (context < ARRAY_SIZE(vmk80xx_boardinfo))
 793                board = &vmk80xx_boardinfo[context];
 794        if (!board)
 795                return -ENODEV;
 796        dev->board_ptr = board;
 797        dev->board_name = board->name;
 798
 799        devpriv = comedi_alloc_devpriv(dev, sizeof(*devpriv));
 800        if (!devpriv)
 801                return -ENOMEM;
 802
 803        devpriv->model = board->model;
 804
 805        sema_init(&devpriv->limit_sem, 8);
 806
 807        ret = vmk80xx_find_usb_endpoints(dev);
 808        if (ret)
 809                return ret;
 810
 811        ret = vmk80xx_alloc_usb_buffers(dev);
 812        if (ret)
 813                return ret;
 814
 815        usb_set_intfdata(intf, devpriv);
 816
 817        if (devpriv->model == VMK8055_MODEL)
 818                vmk80xx_reset_device(dev);
 819
 820        return vmk80xx_init_subdevices(dev);
 821}
 822
 823static void vmk80xx_detach(struct comedi_device *dev)
 824{
 825        struct usb_interface *intf = comedi_to_usb_interface(dev);
 826        struct vmk80xx_private *devpriv = dev->private;
 827
 828        if (!devpriv)
 829                return;
 830
 831        down(&devpriv->limit_sem);
 832
 833        usb_set_intfdata(intf, NULL);
 834
 835        kfree(devpriv->usb_rx_buf);
 836        kfree(devpriv->usb_tx_buf);
 837
 838        up(&devpriv->limit_sem);
 839}
 840
 841static struct comedi_driver vmk80xx_driver = {
 842        .module         = THIS_MODULE,
 843        .driver_name    = "vmk80xx",
 844        .auto_attach    = vmk80xx_auto_attach,
 845        .detach         = vmk80xx_detach,
 846};
 847
 848static int vmk80xx_usb_probe(struct usb_interface *intf,
 849                             const struct usb_device_id *id)
 850{
 851        return comedi_usb_auto_config(intf, &vmk80xx_driver, id->driver_info);
 852}
 853
 854static const struct usb_device_id vmk80xx_usb_id_table[] = {
 855        { USB_DEVICE(0x10cf, 0x5500), .driver_info = DEVICE_VMK8055 },
 856        { USB_DEVICE(0x10cf, 0x5501), .driver_info = DEVICE_VMK8055 },
 857        { USB_DEVICE(0x10cf, 0x5502), .driver_info = DEVICE_VMK8055 },
 858        { USB_DEVICE(0x10cf, 0x5503), .driver_info = DEVICE_VMK8055 },
 859        { USB_DEVICE(0x10cf, 0x8061), .driver_info = DEVICE_VMK8061 },
 860        { USB_DEVICE(0x10cf, 0x8062), .driver_info = DEVICE_VMK8061 },
 861        { USB_DEVICE(0x10cf, 0x8063), .driver_info = DEVICE_VMK8061 },
 862        { USB_DEVICE(0x10cf, 0x8064), .driver_info = DEVICE_VMK8061 },
 863        { USB_DEVICE(0x10cf, 0x8065), .driver_info = DEVICE_VMK8061 },
 864        { USB_DEVICE(0x10cf, 0x8066), .driver_info = DEVICE_VMK8061 },
 865        { USB_DEVICE(0x10cf, 0x8067), .driver_info = DEVICE_VMK8061 },
 866        { USB_DEVICE(0x10cf, 0x8068), .driver_info = DEVICE_VMK8061 },
 867        { }
 868};
 869MODULE_DEVICE_TABLE(usb, vmk80xx_usb_id_table);
 870
 871static struct usb_driver vmk80xx_usb_driver = {
 872        .name           = "vmk80xx",
 873        .id_table       = vmk80xx_usb_id_table,
 874        .probe          = vmk80xx_usb_probe,
 875        .disconnect     = comedi_usb_auto_unconfig,
 876};
 877module_comedi_usb_driver(vmk80xx_driver, vmk80xx_usb_driver);
 878
 879MODULE_AUTHOR("Manuel Gebele <forensixs@gmx.de>");
 880MODULE_DESCRIPTION("Velleman USB Board Low-Level Driver");
 881MODULE_LICENSE("GPL");
 882