Logo Search packages:      
Sourcecode: baycomusb version File versions  Download package

usbdrvlinux.c

/*****************************************************************************/

/*
 *    usbdrvlinux.c  --  Linux USB driver interface.
 *
 *    Copyright (C) 1999-2001
 *          Thomas Sailer (t.sailer@alumni.ethz.ch)
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  $Id: usbdrvlinux.c,v 1.4 2000/01/12 15:54:07 tom Exp $   
 *
 *  History:
 *   0.1  23.06.1999  Created
 *   0.2  07.01.2000  Expanded to usbdevfs capabilities
 *
 */

/*****************************************************************************/

#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/time.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif

#include "usbdrv.h"

/* --------------------------------------------------------------------- */

struct usbdevice {
      int fd;
      struct usb_device_descriptor desc;
};

static char usb_devicefs_mountpoint[256] = "/proc/bus/usb";

/* --------------------------------------------------------------------- */

/*
 * Parse and show the different USB descriptors.
 */
void usb_show_device_descriptor(FILE *f, struct usb_device_descriptor *desc)
{
        fprintf(f, "  Length              = %2d%s\n", desc->bLength,
            desc->bLength == USB_DT_DEVICE_SIZE ? "" : " (!!!)");
        fprintf(f, "  DescriptorType      = %02x\n", desc->bDescriptorType);
        fprintf(f, "  USB version         = %x.%02x\n",
             desc->bcdUSB[1], desc->bcdUSB[0]);
        fprintf(f, "  Vendor:Product      = %02x%02x:%02x%02x\n",
             desc->idVendor[1], desc->idVendor[0], desc->idProduct[1], desc->idProduct[0]);
        fprintf(f, "  MaxPacketSize0      = %d\n", desc->bMaxPacketSize0);
        fprintf(f, "  NumConfigurations   = %d\n", desc->bNumConfigurations);
        fprintf(f, "  Device version      = %x.%02x\n",
            desc->bcdDevice[1], desc->bcdDevice[0]);
        fprintf(f, "  Device Class:SubClass:Protocol = %02x:%02x:%02x\n",
            desc->bDeviceClass, desc->bDeviceSubClass, desc->bDeviceProtocol);
        switch (desc->bDeviceClass) {
        case 0:
                fprintf(f, "    Per-interface classes\n");
                break;
        case 9:
                fprintf(f, "    Hub device class\n");
                break;
        case 0xff:
                fprintf(f, "    Vendor class\n");
                break;
        default:
                fprintf(f, "    Unknown class\n");
        }
}

/* ---------------------------------------------------------------------- */

void usb_setmountpoint(const char *mnt)
{
      if (!mnt)
            mnt = "/proc/bus/usb";
      strncpy(usb_devicefs_mountpoint, mnt, sizeof(usb_devicefs_mountpoint));
}

const char *usb_getmountpoint(void)
{
      return usb_devicefs_mountpoint;
}

void usb_close(struct usbdevice *dev)
{
      if (!dev)
            return;
      close(dev->fd);
      lprintf(20, "usb_close: fd %d\n", dev->fd);
      free(dev);
}

static int parsedev(int fd, unsigned int *bus, unsigned int *dev, int vendorid, int productid, unsigned int index)
{
      char buf[16384];
      char *start, *end, *lineend, *cp;
      int devnum = -1, busnum = -1, vendor = -1, product = -1;
      int ret;

      if (lseek(fd, 0, SEEK_SET) == (off_t)-1)
            return -1;
      ret = read(fd, buf, sizeof(buf)-1);
        if (ret == -1)
                return -1;
        end = buf + ret;
        *end = 0;
        start = buf;
      ret = 0;
        while (start < end) {
                lineend = strchr(start, '\n');
                if (!lineend)
                        break;
                *lineend = 0;
                switch (start[0]) {
            case 'T':  /* topology line */
                        if ((cp = strstr(start, "Dev#="))) {
                                devnum = strtoul(cp + 5, NULL, 0);
                        } else
                                devnum = -1;
                        if ((cp = strstr(start, "Bus="))) {
                                busnum = strtoul(cp + 4, NULL, 0);
                        } else
                                busnum = -1;
                  break;

                case 'P':
                        if ((cp = strstr(start, "Vendor="))) {
                                vendor = strtoul(cp + 7, NULL, 16);
                        } else
                                vendor = -1;
                        if ((cp = strstr(start, "ProdID="))) {
                                product = strtoul(cp + 7, NULL, 16);
                        } else
                                product = -1;
                  if (vendor != -1 && product != -1 && devnum >= 1 && devnum <= 127 &&
                      busnum >= 0 && busnum <= 999 &&
                      (vendorid == vendor || vendorid == -1) &&
                      (productid == product || productid == -1)) {
                        if (index)
                              index--;
                        else {
                              if (!ret) {
                                    if (bus)
                                          *bus = busnum;
                                    if (dev)
                                          *dev = devnum;
                              }
                              ret++;
                        }
                  }
                  break;
            }
                start = lineend + 1;
      }
      return ret;
}

struct usbdevice *usb_open_bynumber(unsigned int busnum, unsigned int devnum, int vendorid, int productid)
{
      struct usbdevice *dev;
        struct usb_device_descriptor desc;
      unsigned int vid, pid;
      char devsfile[256];
      int ret, fd;

      snprintf(devsfile, sizeof(devsfile), "%s/%03u/%03u", usb_devicefs_mountpoint, busnum, devnum);
      if ((fd =  open(devsfile, O_RDWR)) == -1)
            return NULL;
      if ((ret = read(fd, &desc, sizeof(desc))) != sizeof(desc)) {
            if (ret > 0)
                  errno = EIO;
            close(fd);
            return NULL;
      }
      vid = desc.idVendor[0] | (desc.idVendor[1] << 8);
      pid = desc.idProduct[0] | (desc.idProduct[1] << 8);
      if ((vid != vendorid && vendorid != -1) ||
          (pid != productid && productid != -1)) {
            errno = -ENOENT;
            close(fd);
            return NULL;
      }
      if (!(dev = malloc(sizeof(struct usbdevice)))) {
            close(fd);
            return NULL;
      }
      dev->fd = fd;
      dev->desc = desc;
      lprintf(20, "usb_open_bynumber: device %03u:%03u vid %04x pid %04x fd %d\n",
            busnum, devnum, vid, pid, fd);
      return dev;
}

struct usbdevice *usb_open(int vendorid, int productid, unsigned int timeout, unsigned int index)
{
      time_t starttime, curtime;
      unsigned int busnum, devnum;
      char devsfile[256];
      long timediff;
      int ret;
      struct pollfd pfd;

      snprintf(devsfile, sizeof(devsfile), "%s/devices", usb_devicefs_mountpoint);
      time(&starttime);
      if ((pfd.fd =  open(devsfile, O_RDONLY)) == -1)
            return NULL;
      for (;;) {
            ret = parsedev(pfd.fd, &busnum, &devnum, vendorid, productid, index);
            if (ret < 0) {
                  close(pfd.fd);
                  return NULL;
            }
            if (ret > 0)
                  break;
            time(&curtime);
            timediff = curtime - starttime;
            timediff = timeout - timediff;
            if (timediff <= 0) {
                  close(pfd.fd);
                  errno = ETIMEDOUT;
                  return NULL;
            }
            if (timediff > 10)
                  timediff = 10;
            pfd.events = POLLIN;
            ret = poll(&pfd, 1, timediff * 1000);
            if (ret < 0) {
                  close(pfd.fd);
                  return NULL;
            }
      }
      close(pfd.fd);
      return usb_open_bynumber(busnum, devnum, vendorid, productid);
}

int usb_control_msg(struct usbdevice *dev, unsigned char requesttype, unsigned char request,
                unsigned short value, unsigned short index, unsigned short length, void *data, unsigned int timeout)
{
        struct usbdevfs_ctrltransfer ctrl;
      int i;

      ctrl = (struct usbdevfs_ctrltransfer){ requesttype, request, value, index, length, timeout, data };
      for (;;) {
            i = ioctl(dev->fd, USBDEVFS_CONTROL, &ctrl);
            if (i != -1 || errno != ENXIO)
                  break;
            lprintf(20, "usb_control: URB already queued, retrying\n");
            usleep(20000);
      }
      lprintf(20, "usb_control: rqt 0x%02x rq 0x%02x val 0x%04x idx 0x%04x len %u ret %d\n",
            requesttype, request, value, index, length, i);
      if (i < 0) {
            lprintf(2, "ioctl: USBDEVFS_CONTROL (rqt=0x%x rq=0x%x val=%u idx=%u len=%u) error %s (%d)\n",
                  requesttype, request, value, index, length, strerror(errno), errno);
            return -1;
      }
      return i;
}

int usb_bulk_msg(struct usbdevice *dev, unsigned int ep, unsigned int dlen, void *data, unsigned int timeout)
{
      struct usbdevfs_bulktransfer bulk;
      int i;

      bulk = (struct usbdevfs_bulktransfer){ ep, dlen, timeout, data };
      for (;;) {
            i = ioctl(dev->fd, USBDEVFS_BULK, &bulk);
            if (i != -1 || errno != ENXIO)
                  break;
            lprintf(20, "usb_bulk: URB already queued, retrying\n");
            usleep(20000);
      }
      lprintf(20, "usb_bulk: ep 0x%02x len %u ret %d\n", ep, dlen, i);
      if (i < 0) {
            lprintf(2, "ioctl: USBDEVFS_BULK (ep=0x%x len=%u) error %s (%d)\n", ep, dlen, strerror(errno), errno);
            return -1;
      }
      return i;
}

int usb_resetep(struct usbdevice *dev, unsigned int ep)
{
      int i;

      i = ioctl(dev->fd, USBDEVFS_RESETEP, &ep);
      if (i < 0) {
            lprintf(2, "ioctl: USBDEVFS_RESETEP (ep=0x%x) error %s (%d)\n", ep, strerror(errno), errno);
            return -1;
      }
      return 0;
}

int usb_setconfiguration(struct usbdevice *dev, unsigned int configuration)
{
      int i;

      i = ioctl(dev->fd, USBDEVFS_SETCONFIGURATION, &configuration);
      if (i < 0) {
            lprintf(2, "ioctl: USBDEVFS_SETCONFIGURATION (cfg=%d) error %s (%d)\n",
                  configuration, strerror(errno), errno);
            return -1;
      }
      return 0;
}

int usb_setinterface(struct usbdevice *dev, unsigned int intf, unsigned int altsetting)
{
      struct usbdevfs_setinterface setif;
      int i;

      setif = (struct usbdevfs_setinterface) { intf, altsetting };
      i = ioctl(dev->fd, USBDEVFS_SETINTERFACE, &setif);
      if (i < 0) {
            lprintf(2, "ioctl: USBDEVFS_SETINTERFACE (intf=%d,altsetting=%d) error %s (%d)\n",
                  intf, altsetting, strerror(errno), errno);
            return -1;
      }
      return 0;
}

int usb_getdevicedescriptor(struct usbdevice *dev, struct usb_device_descriptor *desc)
{
      if (desc)
            memcpy(desc, &dev->desc, sizeof(*desc));
      return 0;
}

int usb_claiminterface(struct usbdevice *dev, unsigned int intf)
{
      return ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &intf);
}

int usb_releaseinterface(struct usbdevice *dev, unsigned int intf)
{
      return ioctl(dev->fd, USBDEVFS_RELEASEINTERFACE, &intf);
}

int usb_discsignal(struct usbdevice *dev, unsigned int signr, void *context)
{
      struct usbdevfs_disconnectsignal s = { signr, context };

      return ioctl(dev->fd, USBDEVFS_DISCSIGNAL, &s);
}

int usb_submiturb(struct usbdevice *dev, struct usbdevfs_urb *urb)
{
      return ioctl(dev->fd, USBDEVFS_SUBMITURB, urb);
}

int usb_discardurb(struct usbdevice *dev, struct usbdevfs_urb *urb)
{
      return ioctl(dev->fd, USBDEVFS_DISCARDURB, urb);
}

struct usbdevfs_urb *usb_reapurb(struct usbdevice *dev, int timeout)
{
      int ret;
      struct usbdevfs_urb *urb;
      struct timeval tv1, tv2;
      long wt;
      struct pollfd pfd;

      if (timeout <= 0) {
            ret = ioctl(dev->fd, timeout ? USBDEVFS_REAPURB : USBDEVFS_REAPURBNDELAY, &urb);
            if (ret < 0)
                  return NULL;
            return urb;
      }
      gettimeofday(&tv1, NULL);
      pfd.fd = dev->fd;
      pfd.events = POLLOUT;
      for (;;) {
            ret = ioctl(dev->fd, USBDEVFS_REAPURBNDELAY, &urb);
            if (ret >= 0)
                  return urb;
            gettimeofday(&tv2, NULL);
            wt = timeout + (tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000;
            if (wt <= 0)
                  return NULL;
            if (poll(&pfd, 1, wt) < 0)
                  return NULL;
      }
}

int usb_getfd(struct usbdevice *dev)
{
      return dev->fd;
}

/* ---------------------------------------------------------------------- */

Generated by  Doxygen 1.6.0   Back to index