318 lines
7.4 KiB
C
318 lines
7.4 KiB
C
/*
|
|
* Main API entry point
|
|
*
|
|
* Copyright (c) 2000-2003 Johannes Erdfelt <johannes@erdfelt.com>
|
|
*
|
|
* This library is covered by the LGPL, read LICENSE for details.
|
|
*/
|
|
|
|
#include <stdlib.h> /* getenv */
|
|
#include <stdio.h> /* stderr */
|
|
#include <string.h> /* strcmp */
|
|
#include <errno.h>
|
|
|
|
#include "usbi.h"
|
|
|
|
int usb_debug = 0;
|
|
struct usb_bus *_usb_busses = NULL;
|
|
|
|
int usb_find_busses(void)
|
|
{
|
|
struct usb_bus *busses, *bus;
|
|
int ret, changes = 0;
|
|
|
|
ret = usb_os_find_busses(&busses);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/*
|
|
* Now walk through all of the busses we know about and compare against
|
|
* this new list. Any duplicates will be removed from the new list.
|
|
* If we don't find it in the new list, the bus was removed. Any
|
|
* busses still in the new list, are new to us.
|
|
*/
|
|
bus = _usb_busses;
|
|
while (bus)
|
|
{
|
|
int found = 0;
|
|
struct usb_bus *nbus, *tbus = bus->next;
|
|
|
|
nbus = busses;
|
|
while (nbus)
|
|
{
|
|
struct usb_bus *tnbus = nbus->next;
|
|
|
|
if (!strcmp(bus->dirname, nbus->dirname))
|
|
{
|
|
/* Remove it from the new busses list */
|
|
LIST_DEL(busses, nbus);
|
|
|
|
usb_free_bus(nbus);
|
|
found = 1;
|
|
break;
|
|
}
|
|
|
|
nbus = tnbus;
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
/* The bus was removed from the system */
|
|
LIST_DEL(_usb_busses, bus);
|
|
usb_free_bus(bus);
|
|
changes++;
|
|
}
|
|
|
|
bus = tbus;
|
|
}
|
|
|
|
/*
|
|
* Anything on the *busses list is new. So add them to usb_busses and
|
|
* process them like the new bus it is.
|
|
*/
|
|
bus = busses;
|
|
while (bus)
|
|
{
|
|
struct usb_bus *tbus = bus->next;
|
|
|
|
/*
|
|
* Remove it from the temporary list first and add it to the real
|
|
* usb_busses list.
|
|
*/
|
|
LIST_DEL(busses, bus);
|
|
|
|
LIST_ADD(_usb_busses, bus);
|
|
|
|
changes++;
|
|
|
|
bus = tbus;
|
|
}
|
|
|
|
return changes;
|
|
}
|
|
|
|
int usb_find_devices(void)
|
|
{
|
|
struct usb_bus *bus;
|
|
int ret, changes = 0;
|
|
|
|
for (bus = usb_busses; bus; bus = bus->next)
|
|
{
|
|
struct usb_device *devices, *dev;
|
|
|
|
/* Find all of the devices and put them into a temporary list */
|
|
ret = usb_os_find_devices(bus, &devices);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/*
|
|
* Now walk through all of the devices we know about and compare
|
|
* against this new list. Any duplicates will be removed from the new
|
|
* list. If we don't find it in the new list, the device was removed.
|
|
* Any devices still in the new list, are new to us.
|
|
*/
|
|
dev = bus->devices;
|
|
while (dev)
|
|
{
|
|
int found = 0;
|
|
struct usb_device *ndev, *tdev = dev->next;
|
|
|
|
ndev = devices;
|
|
while (ndev)
|
|
{
|
|
struct usb_device *tndev = ndev->next;
|
|
|
|
if (!strcmp(dev->filename, ndev->filename))
|
|
{
|
|
/* Remove it from the new devices list */
|
|
LIST_DEL(devices, ndev);
|
|
|
|
usb_free_dev(ndev);
|
|
found = 1;
|
|
break;
|
|
}
|
|
|
|
ndev = tndev;
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
/* The device was removed from the system */
|
|
LIST_DEL(bus->devices, dev);
|
|
usb_free_dev(dev);
|
|
changes++;
|
|
}
|
|
|
|
dev = tdev;
|
|
}
|
|
|
|
/*
|
|
* Anything on the *devices list is new. So add them to bus->devices and
|
|
* process them like the new device it is.
|
|
*/
|
|
dev = devices;
|
|
while (dev)
|
|
{
|
|
struct usb_device *tdev = dev->next;
|
|
|
|
/*
|
|
* Remove it from the temporary list first and add it to the real
|
|
* bus->devices list.
|
|
*/
|
|
LIST_DEL(devices, dev);
|
|
|
|
/*
|
|
* Some ports fetch the descriptors on scanning (like Linux) so we don't
|
|
* need to fetch them again.
|
|
*/
|
|
if (!dev->config)
|
|
{
|
|
usb_dev_handle *udev;
|
|
|
|
udev = usb_open(dev);
|
|
if (udev)
|
|
{
|
|
usb_fetch_and_parse_descriptors(udev);
|
|
|
|
usb_close(udev);
|
|
}
|
|
}
|
|
|
|
// [ID:2928293 Tim Green]
|
|
//
|
|
if (dev->config)
|
|
{
|
|
LIST_ADD(bus->devices, dev);
|
|
changes++;
|
|
}
|
|
|
|
dev = tdev;
|
|
}
|
|
|
|
usb_os_determine_children(bus);
|
|
}
|
|
|
|
return changes;
|
|
}
|
|
|
|
void usb_init(void)
|
|
{
|
|
if (getenv("USB_DEBUG"))
|
|
usb_set_debug(atoi(getenv("USB_DEBUG")));
|
|
|
|
usb_os_init();
|
|
}
|
|
|
|
usb_dev_handle *usb_open(struct usb_device *dev)
|
|
{
|
|
usb_dev_handle *udev;
|
|
|
|
udev = malloc(sizeof(*udev));
|
|
if (!udev)
|
|
return NULL;
|
|
|
|
udev->fd = -1;
|
|
udev->device = dev;
|
|
udev->bus = dev->bus;
|
|
udev->config = udev->interface = udev->altsetting = -1;
|
|
|
|
if (usb_os_open(udev) < 0)
|
|
{
|
|
free(udev);
|
|
return NULL;
|
|
}
|
|
|
|
return udev;
|
|
}
|
|
|
|
int usb_get_string(usb_dev_handle *dev, int index, int langid, char *buf,
|
|
size_t buflen)
|
|
{
|
|
/*
|
|
* We can't use usb_get_descriptor() because it's lacking the index
|
|
* parameter. This will be fixed in libusb 1.0
|
|
*/
|
|
return usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR,
|
|
(USB_DT_STRING << 8) + index, langid, buf, (int)buflen, 1000);
|
|
}
|
|
|
|
int usb_get_string_simple(usb_dev_handle *dev, int index, char *buf, size_t buflen)
|
|
{
|
|
char tbuf[255]; /* Some devices choke on size > 255 */
|
|
int ret, langid, si, di;
|
|
|
|
/*
|
|
* Asking for the zero'th index is special - it returns a string
|
|
* descriptor that contains all the language IDs supported by the
|
|
* device. Typically there aren't many - often only one. The
|
|
* language IDs are 16 bit numbers, and they start at the third byte
|
|
* in the descriptor. See USB 2.0 specification, section 9.6.7, for
|
|
* more information on this. */
|
|
ret = usb_get_string(dev, 0, 0, tbuf, sizeof(tbuf));
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (ret < 4)
|
|
return -EIO;
|
|
|
|
langid = tbuf[2] | (tbuf[3] << 8);
|
|
|
|
ret = usb_get_string(dev, index, langid, tbuf, sizeof(tbuf));
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (tbuf[1] != USB_DT_STRING)
|
|
return -EIO;
|
|
|
|
if (tbuf[0] > ret)
|
|
return -EFBIG;
|
|
|
|
for (di = 0, si = 2; si < tbuf[0]; si += 2)
|
|
{
|
|
if (di >= ((int)buflen - 1))
|
|
break;
|
|
|
|
if (tbuf[si + 1]) /* high byte */
|
|
buf[di++] = '?';
|
|
else
|
|
buf[di++] = tbuf[si];
|
|
}
|
|
|
|
buf[di] = 0;
|
|
|
|
return di;
|
|
}
|
|
|
|
int usb_close(usb_dev_handle *dev)
|
|
{
|
|
int ret;
|
|
|
|
ret = usb_os_close(dev);
|
|
free(dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct usb_device *usb_device(usb_dev_handle *dev)
|
|
{
|
|
return dev->device;
|
|
}
|
|
|
|
void usb_free_dev(struct usb_device *dev)
|
|
{
|
|
usb_destroy_configuration(dev);
|
|
free(dev->children);
|
|
free(dev);
|
|
}
|
|
|
|
struct usb_bus *usb_get_busses(void)
|
|
{
|
|
return _usb_busses;
|
|
}
|
|
|
|
void usb_free_bus(struct usb_bus *bus)
|
|
{
|
|
free(bus);
|
|
}
|
|
|