summaryrefslogtreecommitdiff
path: root/libusb/core.c
diff options
context:
space:
mode:
authorDaniel Drake <dsd@gentoo.org>2008-03-13 12:36:56 +0000
committerDaniel Drake <dsd@gentoo.org>2008-03-13 12:46:08 +0000
commitc0c9432d38b22784070dce3a7874c62c31786a27 (patch)
tree825ca9817d3609a0645dbe3f1a1e46aa40dc1ff9 /libusb/core.c
parentde4c5341d168697baa4c0901c406deb47e78aae7 (diff)
downloadlibusb-c0c9432d38b22784070dce3a7874c62c31786a27.tar.gz
Beginnings of cross-platform abstraction
This also includes a libusb_get_pollfds API change
Diffstat (limited to 'libusb/core.c')
-rw-r--r--libusb/core.c397
1 files changed, 55 insertions, 342 deletions
diff --git a/libusb/core.c b/libusb/core.c
index 8d7abcf..68d32ce 100644
--- a/libusb/core.c
+++ b/libusb/core.c
@@ -20,66 +20,24 @@
#include <config.h>
-#include <dirent.h>
#include <errno.h>
-#include <fcntl.h>
-#include <features.h>
#include <poll.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
#include "libusb.h"
#include "libusbi.h"
-struct list_head usb_devs;
-struct list_head open_devs;
-static const char *usbfs_path = NULL;
+#ifdef OS_LINUX
+const struct usbi_os_backend * const usbi_backend = &linux_usbfs_backend;
+#else
+#error "Unsupported OS"
+#endif
-static int check_usb_vfs(const char *dirname)
-{
- DIR *dir;
- struct dirent *entry;
- int found = 0;
-
- dir = opendir(dirname);
- if (!dir)
- return 0;
-
- while ((entry = readdir(dir)) != NULL) {
- if (entry->d_name[0] == '.')
- continue;
-
- /* We assume if we find any files that it must be the right place */
- found = 1;
- break;
- }
-
- closedir(dir);
- return found;
-}
-
-static const char *find_usbfs_path(void)
-{
- const char *path = "/dev/bus/usb";
- const char *ret = NULL;
-
- if (check_usb_vfs(path)) {
- ret = path;
- } else {
- path = "/proc/bus/usb";
- if (check_usb_vfs(path))
- ret = path;
- }
-
- usbi_dbg("found usbfs at %s", ret);
- return ret;
-}
+static struct list_head usb_devs;
+struct list_head usbi_open_devs;
/* we traverse usbfs without knowing how many devices we are going to find.
* so we create this discovered_devs model which is similar to a linked-list
@@ -87,11 +45,6 @@ static const char *find_usbfs_path(void)
* eliminating the need for a list node in the libusb_device structure
* itself. */
#define DISCOVERED_DEVICES_SIZE_STEP 8
-struct discovered_devs {
- size_t len;
- size_t capacity;
- struct libusb_device *devices[0];
-};
static struct discovered_devs *discovered_devs_alloc(void)
{
@@ -107,7 +60,7 @@ static struct discovered_devs *discovered_devs_alloc(void)
/* append a device to the discovered devices collection. may realloc itself,
* returning new discdevs. returns NULL on realloc failure. */
-static struct discovered_devs *discovered_devs_append(
+struct discovered_devs *discovered_devs_append(
struct discovered_devs *discdevs, struct libusb_device *dev)
{
size_t len = discdevs->len;
@@ -144,115 +97,21 @@ static void discovered_devs_free(struct discovered_devs *discdevs)
free(discdevs);
}
-static struct libusb_device *device_new(uint8_t busnum, uint8_t devaddr)
+struct libusb_device *usbi_alloc_device(unsigned long session_id)
{
- char path[PATH_MAX + 1];
- unsigned char raw_desc[DEVICE_DESC_LENGTH];
- struct libusb_device *dev = malloc(sizeof(*dev));
- int fd = 0;
- int i;
- int r;
- int tmp;
-
+ size_t priv_size = usbi_backend->device_priv_size;
+ struct libusb_device *dev = malloc(sizeof(*dev) + priv_size);
if (!dev)
return NULL;
dev->refcnt = 1;
- dev->nodepath = NULL;
- dev->config = NULL;
-
- snprintf(path, PATH_MAX, "%s/%03d/%03d", usbfs_path, busnum, devaddr);
- usbi_dbg("%s", path);
- fd = open(path, O_RDWR);
- if (!fd) {
- usbi_dbg("open '%s' failed, ret=%d errno=%d", path, fd, errno);
- /* FIXME this might not be an error if the file has gone away due
- * to unplugging */
- goto err;
- }
-
- r = read(fd, raw_desc, DEVICE_DESC_LENGTH);
- if (r < 0) {
- usbi_err("read failed ret=%d errno=%d", r, errno);
- goto err;
- }
- /* FIXME: short read handling? */
-
- usbi_parse_descriptor(raw_desc, "bbWbbbbWWWbbbb", &dev->desc);
-
- /* Now try to fetch the rest of the descriptors */
- if (dev->desc.bNumConfigurations > USB_MAXCONFIG) {
- usbi_err("too many configurations");
- goto err;
- }
-
- if (dev->desc.bNumConfigurations < 1) {
- usbi_dbg("no configurations?");
- goto err;
- }
-
- tmp = dev->desc.bNumConfigurations * sizeof(struct libusb_config_descriptor);
- dev->config = malloc(tmp);
- if (!dev->config)
- goto err;
-
- memset(dev->config, 0, tmp);
- for (i = 0; i < dev->desc.bNumConfigurations; i++) {
- unsigned char buffer[8], *bigbuffer;
- struct libusb_config_descriptor config;
-
- /* Get the first 8 bytes to figure out what the total length is */
- r = read(fd, buffer, sizeof(buffer));
- if (r < sizeof(buffer)) {
- usbi_err("short descriptor read (%d/%d)", r, sizeof(buffer));
- goto err;
- }
-
- usbi_parse_descriptor(buffer, "bbw", &config);
-
- bigbuffer = malloc(config.wTotalLength);
- if (!bigbuffer)
- goto err;
-
- /* Read the rest of the config descriptor */
- memcpy(bigbuffer, buffer, sizeof(buffer));
-
- tmp = config.wTotalLength - 8;
- r = read(fd, bigbuffer + 8, tmp);
- if (r < tmp) {
- usbi_err("short descriptor read (%d/%d)", r, tmp);
- free(bigbuffer);
- goto err;
- }
-
- r = usbi_parse_configuration(&dev->config[i], bigbuffer);
- if (r > 0)
- usbi_warn("descriptor data still left\n");
- free(bigbuffer);
- }
-
- dev->nodepath = strdup(path);
- if (!dev->nodepath)
- goto err;
-
- dev->session_data = busnum << 8 | devaddr;
+ dev->session_data = session_id;
list_add(&dev->list, &usb_devs);
- close(fd);
+ memset(&dev->os_priv, 0, priv_size);
return dev;
-
-err:
- if (fd)
- close(fd);
- if (dev->config)
- free(dev->config);
- if (dev->nodepath)
- free(dev->nodepath);
- if (dev)
- free(dev);
- return NULL;
}
-static struct libusb_device *get_device_by_session_id(unsigned long session_id)
+struct libusb_device *usbi_get_device_by_session_id(unsigned long session_id)
{
struct libusb_device *dev;
@@ -263,103 +122,9 @@ static struct libusb_device *get_device_by_session_id(unsigned long session_id)
return NULL;
}
-/* open a device file, set up the libusb_device structure for it, and add it to
- * discdevs. on failure (non-zero return) the pre-existing discdevs should
- * be destroyed (and devices freed). on success, the new discdevs pointer
- * should be used it may have been moved. */
-static int scan_device(struct discovered_devs **_discdevs, uint8_t busnum,
- uint8_t devaddr)
-{
- struct discovered_devs *discdevs;
- unsigned long session_id;
- struct libusb_device *dev;
- int need_unref = 0;
- int r = 0;
-
- /* FIXME: session ID is not guaranteed unique as addresses can wrap and
- * will be reused. instead we should add a simple sysfs attribute with
- * a session ID. */
- session_id = busnum << 8 | devaddr;
- usbi_dbg("busnum %d devaddr %d session_id %ld", busnum, devaddr,
- session_id);
-
- dev = get_device_by_session_id(session_id);
- if (dev) {
- usbi_dbg("using existing device for %d/%d (session %ld)",
- busnum, devaddr, session_id);
- } else {
- usbi_dbg("allocating new device for %d/%d (session %ld)",
- busnum, devaddr, session_id);
- dev = device_new(busnum, devaddr);
- if (!dev) {
- r = -EIO;
- goto out;
- }
- need_unref = 1;
- }
-
- discdevs = discovered_devs_append(*_discdevs, dev);
- if (!discdevs)
- r = -ENOMEM;
- else
- *_discdevs = discdevs;
-
-out:
- if (need_unref)
- libusb_device_unref(dev);
- return r;
-}
-
-/* open a bus directory and adds all discovered devices to discdevs. on
- * failure (non-zero return) the pre-existing discdevs should be destroyed
- * (and devices freed). on success, the new discdevs pointer should be used
- * as it may have been moved. */
-static int scan_busdir(struct discovered_devs **_discdevs, uint8_t busnum)
-{
- DIR *dir;
- char dirpath[PATH_MAX + 1];
- struct dirent *entry;
- struct discovered_devs *discdevs = *_discdevs;
- int r = 0;
-
- snprintf(dirpath, PATH_MAX, "%s/%03d", usbfs_path, busnum);
- usbi_dbg("%s", dirpath);
- dir = opendir(dirpath);
- if (!dir) {
- usbi_err("opendir '%s' failed, errno=%d", dirpath, errno);
- /* FIXME: should handle valid race conditions like hub unplugged
- * during directory iteration - this is not an error */
- return -1;
- }
-
- while ((entry = readdir(dir))) {
- int devaddr;
-
- if (entry->d_name[0] == '.')
- continue;
-
- devaddr = atoi(entry->d_name);
- if (devaddr == 0) {
- usbi_dbg("unknown dir entry %s", entry->d_name);
- continue;
- }
-
- r = scan_device(&discdevs, busnum, (uint8_t) devaddr);
- if (r < 0)
- goto out;
- }
-
- *_discdevs = discdevs;
-out:
- closedir(dir);
- return r;
-}
-
API_EXPORTED int libusb_get_device_list(struct libusb_device ***list)
{
- DIR *buses;
struct discovered_devs *discdevs = discovered_devs_alloc();
- struct dirent *entry;
struct libusb_device **ret;
int r = 0;
size_t i;
@@ -369,30 +134,9 @@ API_EXPORTED int libusb_get_device_list(struct libusb_device ***list)
if (!discdevs)
return -ENOMEM;
- buses = opendir(usbfs_path);
- if (!buses) {
- usbi_err("opendir buses failed errno=%d", errno);
- return -1;
- }
-
- while ((entry = readdir(buses))) {
- struct discovered_devs *discdevs_new = discdevs;
- int busnum;
-
- if (entry->d_name[0] == '.')
- continue;
-
- busnum = atoi(entry->d_name);
- if (busnum == 0) {
- usbi_dbg("unknown dir entry %s", entry->d_name);
- continue;
- }
-
- r = scan_busdir(&discdevs_new, busnum);
- if (r < 0)
- goto out;
- discdevs = discdevs_new;
- }
+ r = usbi_backend->get_device_list(discdevs);
+ if (r < 0)
+ goto out;
/* convert discovered_devs into a list */
len = discdevs->len;
@@ -411,7 +155,6 @@ API_EXPORTED int libusb_get_device_list(struct libusb_device ***list)
out:
discovered_devs_free(discdevs);
- closedir(buses);
return r;
}
@@ -445,9 +188,13 @@ API_EXPORTED void libusb_device_unref(struct libusb_device *dev)
if (--dev->refcnt == 0) {
usbi_dbg("destroy device %04x:%04x", dev->desc.idVendor,
dev->desc.idProduct);
+
+ if (usbi_backend->destroy_device)
+ usbi_backend->destroy_device(dev);
+
list_del(&dev->list);
- free(dev->config);
- free(dev->nodepath);
+ if (dev->config)
+ free(dev->config);
free(dev);
}
}
@@ -466,27 +213,26 @@ API_EXPORTED struct libusb_config_descriptor *libusb_get_config_descriptor(
API_EXPORTED struct libusb_device_handle *libusb_open(struct libusb_device *dev)
{
- struct libusb_device_handle *devh;
- int fd;
+ struct libusb_device_handle *handle;
+ size_t priv_size = usbi_backend->device_handle_priv_size;
+ int r;
usbi_dbg("open %04x:%04x", dev->desc.idVendor, dev->desc.idProduct);
- fd = open(dev->nodepath, O_RDWR);
- if (!fd) {
- usbi_err("open failed, code %d errno %d", fd, errno);
+ handle = malloc(sizeof(*handle) + priv_size);
+ if (!handle)
return NULL;
- }
- devh = malloc(sizeof(*devh));
- if (!devh) {
- close(fd);
+ handle->dev = libusb_device_ref(dev);
+ memset(&handle->os_priv, 0, priv_size);
+ r = usbi_backend->open(handle);
+ if (r < 0) {
+ libusb_device_unref(dev);
+ free(handle);
return NULL;
}
- devh->fd = fd;
- devh->dev = libusb_device_ref(dev);
- list_add(&devh->list, &open_devs);
- usbi_add_pollfd(fd, POLLOUT);
- return devh;
+ list_add(&handle->list, &usbi_open_devs);
+ return handle;
}
/* convenience function for finding a device with a particular vendor/product
@@ -499,7 +245,7 @@ API_EXPORTED struct libusb_device_handle *libusb_open_device_with_vid_pid(
struct libusb_device **devs;
struct libusb_device *found = NULL;
struct libusb_device *dev;
- struct libusb_device_handle *devh;
+ struct libusb_device_handle *handle = NULL;
size_t i = 0;
if (libusb_get_device_list(&devs) < 0)
@@ -515,17 +261,16 @@ API_EXPORTED struct libusb_device_handle *libusb_open_device_with_vid_pid(
}
if (found)
- devh = libusb_open(found);
+ handle = libusb_open(found);
libusb_free_device_list(devs, 1);
- return devh;
+ return handle;
}
-static void do_close(struct libusb_device_handle *devh)
+static void do_close(struct libusb_device_handle *dev_handle)
{
- usbi_remove_pollfd(devh->fd);
- close(devh->fd);
- libusb_device_unref(devh->dev);
+ usbi_backend->close(dev_handle);
+ libusb_device_unref(dev_handle->dev);
}
API_EXPORTED void libusb_close(struct libusb_device_handle *dev_handle)
@@ -548,38 +293,29 @@ API_EXPORTED struct libusb_device *libusb_get_device(
API_EXPORTED int libusb_claim_interface(struct libusb_device_handle *dev,
int iface)
{
- int r;
usbi_dbg("interface %d", iface);
-
- r = ioctl(dev->fd, IOCTL_USB_CLAIMINTF, &iface);
- if (r < 0)
- usbi_err("claim interface failed, error %d", r);
- return r;
+ return usbi_backend->claim_interface(dev, iface);
}
API_EXPORTED int libusb_release_interface(struct libusb_device_handle *dev,
int iface)
{
- int r;
usbi_dbg("interface %d", iface);
-
- r = ioctl(dev->fd, IOCTL_USB_RELEASEINTF, &iface);
- if (r < 0)
- usbi_err("release interface failed, error %d", r);
- return r;
+ return usbi_backend->release_interface(dev, iface);
}
API_EXPORTED int libusb_init(void)
{
usbi_dbg("");
- usbfs_path = find_usbfs_path();
- if (!usbfs_path) {
- usbi_err("could not find usbfs");
- return -ENODEV;
+
+ if (usbi_backend->init) {
+ int r = usbi_backend->init();
+ if (r < 0)
+ return r;
}
list_init(&usb_devs);
- list_init(&open_devs);
+ list_init(&usbi_open_devs);
usbi_io_init();
return 0;
}
@@ -588,37 +324,14 @@ API_EXPORTED void libusb_exit(void)
{
struct libusb_device_handle *devh;
usbi_dbg("");
- if (!list_empty(&open_devs)) {
+ if (!list_empty(&usbi_open_devs)) {
usbi_dbg("naughty app left some devices open!\n");
- list_for_each_entry(devh, &open_devs, list)
+ list_for_each_entry(devh, &usbi_open_devs, list)
do_close(devh);
+ /* FIXME where do the open handles get freed? */
}
-}
-
-API_EXPORTED size_t libusb_get_pollfds(struct libusb_pollfd **pollfds)
-{
- struct libusb_device_handle *devh;
- struct libusb_pollfd *ret;
- size_t cnt = 0;
- size_t i = 0;
-
- /* count number of open devices */
- list_for_each_entry(devh, &open_devs, list)
- cnt++;
-
- /* create array */
- ret = calloc(cnt, sizeof(struct libusb_pollfd));
- if (!ret)
- return -ENOMEM;
-
- /* add fds */
- list_for_each_entry(devh, &open_devs, list) {
- ret[i++].fd = devh->fd;
- ret[i].events = POLLOUT;
- }
-
- *pollfds = ret;
- return cnt;
+ if (usbi_backend->exit)
+ usbi_backend->exit();
}
void usbi_log(enum usbi_log_level level, const char *function,