upload android base code part6
This commit is contained in:
parent
421e214c7d
commit
4e516ec6ed
35396 changed files with 9188716 additions and 0 deletions
39
android/system/core/libusbhost/Android.bp
Normal file
39
android/system/core/libusbhost/Android.bp
Normal file
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// Copyright (C) 2010 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
cc_library {
|
||||
name: "libusbhost",
|
||||
vendor_available: true,
|
||||
vndk: {
|
||||
enabled: true,
|
||||
},
|
||||
host_supported: true,
|
||||
srcs: ["usbhost.c"],
|
||||
cflags: ["-Werror"],
|
||||
export_include_dirs: ["include"],
|
||||
target: {
|
||||
android: {
|
||||
cflags: [
|
||||
"-g",
|
||||
"-DUSE_LIBLOG",
|
||||
],
|
||||
shared_libs: ["liblog"],
|
||||
},
|
||||
darwin: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
247
android/system/core/libusbhost/include/usbhost/usbhost.h
Normal file
247
android/system/core/libusbhost/include/usbhost/usbhost.h
Normal file
|
@ -0,0 +1,247 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __USB_HOST_H
|
||||
#define __USB_HOST_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <linux/version.h>
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)
|
||||
#include <linux/usb/ch9.h>
|
||||
#else
|
||||
#include <linux/usb_ch9.h>
|
||||
#endif
|
||||
|
||||
struct usb_host_context;
|
||||
struct usb_endpoint_descriptor;
|
||||
|
||||
struct usb_descriptor_iter {
|
||||
unsigned char* config;
|
||||
unsigned char* config_end;
|
||||
unsigned char* curr_desc;
|
||||
};
|
||||
|
||||
struct usb_request
|
||||
{
|
||||
struct usb_device *dev;
|
||||
void* buffer;
|
||||
int buffer_length;
|
||||
int actual_length;
|
||||
int max_packet_size;
|
||||
void *private_data; /* struct usbdevfs_urb* */
|
||||
int endpoint;
|
||||
void *client_data; /* free for use by client */
|
||||
};
|
||||
|
||||
/* Callback for notification when new USB devices are attached.
|
||||
* Return true to exit from usb_host_run.
|
||||
*/
|
||||
typedef int (* usb_device_added_cb)(const char *dev_name, void *client_data);
|
||||
|
||||
/* Callback for notification when USB devices are removed.
|
||||
* Return true to exit from usb_host_run.
|
||||
*/
|
||||
typedef int (* usb_device_removed_cb)(const char *dev_name, void *client_data);
|
||||
|
||||
/* Callback indicating that initial device discovery is done.
|
||||
* Return true to exit from usb_host_run.
|
||||
*/
|
||||
typedef int (* usb_discovery_done_cb)(void *client_data);
|
||||
|
||||
/* Call this to initialize the USB host library. */
|
||||
struct usb_host_context *usb_host_init(void);
|
||||
|
||||
/* Call this to cleanup the USB host library. */
|
||||
void usb_host_cleanup(struct usb_host_context *context);
|
||||
|
||||
/* Call this to get the inotify file descriptor. */
|
||||
int usb_host_get_fd(struct usb_host_context *context);
|
||||
|
||||
/* Call this to initialize the usb host context. */
|
||||
int usb_host_load(struct usb_host_context *context,
|
||||
usb_device_added_cb added_cb,
|
||||
usb_device_removed_cb removed_cb,
|
||||
usb_discovery_done_cb discovery_done_cb,
|
||||
void *client_data);
|
||||
|
||||
/* Call this to read and handle occuring usb event. */
|
||||
int usb_host_read_event(struct usb_host_context *context);
|
||||
|
||||
/* Call this to monitor the USB bus for new and removed devices.
|
||||
* This is intended to be called from a dedicated thread,
|
||||
* as it will not return until one of the callbacks returns true.
|
||||
* added_cb will be called immediately for each existing USB device,
|
||||
* and subsequently each time a new device is added.
|
||||
* removed_cb is called when USB devices are removed from the bus.
|
||||
* discovery_done_cb is called after the initial discovery of already
|
||||
* connected devices is complete.
|
||||
*/
|
||||
void usb_host_run(struct usb_host_context *context,
|
||||
usb_device_added_cb added_cb,
|
||||
usb_device_removed_cb removed_cb,
|
||||
usb_discovery_done_cb discovery_done_cb,
|
||||
void *client_data);
|
||||
|
||||
/* Creates a usb_device object for a USB device */
|
||||
struct usb_device *usb_device_open(const char *dev_name);
|
||||
|
||||
/* Releases all resources associated with the USB device */
|
||||
void usb_device_close(struct usb_device *device);
|
||||
|
||||
/* Creates a usb_device object for already open USB device */
|
||||
struct usb_device *usb_device_new(const char *dev_name, int fd);
|
||||
|
||||
/* Returns the file descriptor for the usb_device */
|
||||
int usb_device_get_fd(struct usb_device *device);
|
||||
|
||||
/* Returns the name for the USB device, which is the same as
|
||||
* the dev_name passed to usb_device_open()
|
||||
*/
|
||||
const char* usb_device_get_name(struct usb_device *device);
|
||||
|
||||
/* Returns a unique ID for the device.
|
||||
*Currently this is generated from the dev_name path.
|
||||
*/
|
||||
int usb_device_get_unique_id(struct usb_device *device);
|
||||
|
||||
/* Returns a unique ID for the device name.
|
||||
* Currently this is generated from the device path.
|
||||
*/
|
||||
int usb_device_get_unique_id_from_name(const char* name);
|
||||
|
||||
/* Returns the device name for the unique ID.
|
||||
* Call free() to deallocate the returned string */
|
||||
char* usb_device_get_name_from_unique_id(int id);
|
||||
|
||||
/* Returns the USB vendor ID from the device descriptor for the USB device */
|
||||
uint16_t usb_device_get_vendor_id(struct usb_device *device);
|
||||
|
||||
/* Returns the USB product ID from the device descriptor for the USB device */
|
||||
uint16_t usb_device_get_product_id(struct usb_device *device);
|
||||
|
||||
const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device);
|
||||
|
||||
/* Returns a USB descriptor string for the given string ID.
|
||||
* Used to implement usb_device_get_manufacturer_name,
|
||||
* usb_device_get_product_name and usb_device_get_serial.
|
||||
* Call free() to free the result when you are done with it.
|
||||
*/
|
||||
char* usb_device_get_string(struct usb_device *device, int id, int timeout);
|
||||
|
||||
/* Returns the manufacturer name for the USB device.
|
||||
* Call free() to free the result when you are done with it.
|
||||
*/
|
||||
char* usb_device_get_manufacturer_name(struct usb_device *device, int timeout);
|
||||
|
||||
/* Returns the product name for the USB device.
|
||||
* Call free() to free the result when you are done with it.
|
||||
*/
|
||||
char* usb_device_get_product_name(struct usb_device *device, int timeout);
|
||||
|
||||
/* Returns the version number for the USB device.
|
||||
*/
|
||||
int usb_device_get_version(struct usb_device *device);
|
||||
|
||||
/* Returns the USB serial number for the USB device.
|
||||
* Call free() to free the result when you are done with it.
|
||||
*/
|
||||
char* usb_device_get_serial(struct usb_device *device, int timeout);
|
||||
|
||||
/* Returns true if we have write access to the USB device,
|
||||
* and false if we only have access to the USB device configuration.
|
||||
*/
|
||||
int usb_device_is_writeable(struct usb_device *device);
|
||||
|
||||
/* Initializes a usb_descriptor_iter, which can be used to iterate through all
|
||||
* the USB descriptors for a USB device.
|
||||
*/
|
||||
void usb_descriptor_iter_init(struct usb_device *device, struct usb_descriptor_iter *iter);
|
||||
|
||||
/* Returns the next USB descriptor for a device, or NULL if we have reached the
|
||||
* end of the list.
|
||||
*/
|
||||
struct usb_descriptor_header *usb_descriptor_iter_next(struct usb_descriptor_iter *iter);
|
||||
|
||||
/* Claims the specified interface of a USB device */
|
||||
int usb_device_claim_interface(struct usb_device *device, unsigned int interface);
|
||||
|
||||
/* Releases the specified interface of a USB device */
|
||||
int usb_device_release_interface(struct usb_device *device, unsigned int interface);
|
||||
|
||||
/* Requests the kernel to connect or disconnect its driver for the specified interface.
|
||||
* This can be used to ask the kernel to disconnect its driver for a device
|
||||
* so usb_device_claim_interface can claim it instead.
|
||||
*/
|
||||
int usb_device_connect_kernel_driver(struct usb_device *device,
|
||||
unsigned int interface, int connect);
|
||||
|
||||
/* Sets the current configuration for the device to the specified configuration */
|
||||
int usb_device_set_configuration(struct usb_device *device, int configuration);
|
||||
|
||||
/* Sets the specified interface of a USB device */
|
||||
int usb_device_set_interface(struct usb_device *device, unsigned int interface,
|
||||
unsigned int alt_setting);
|
||||
|
||||
/* Sends a control message to the specified device on endpoint zero */
|
||||
int usb_device_control_transfer(struct usb_device *device,
|
||||
int requestType,
|
||||
int request,
|
||||
int value,
|
||||
int index,
|
||||
void* buffer,
|
||||
int length,
|
||||
unsigned int timeout);
|
||||
|
||||
/* Reads or writes on a bulk endpoint.
|
||||
* Returns number of bytes transferred, or negative value for error.
|
||||
*/
|
||||
int usb_device_bulk_transfer(struct usb_device *device,
|
||||
int endpoint,
|
||||
void* buffer,
|
||||
unsigned int length,
|
||||
unsigned int timeout);
|
||||
|
||||
/** Reset USB bus for the device */
|
||||
int usb_device_reset(struct usb_device *device);
|
||||
|
||||
/* Creates a new usb_request. */
|
||||
struct usb_request *usb_request_new(struct usb_device *dev,
|
||||
const struct usb_endpoint_descriptor *ep_desc);
|
||||
|
||||
/* Releases all resources associated with the request */
|
||||
void usb_request_free(struct usb_request *req);
|
||||
|
||||
/* Submits a read or write request on the specified device */
|
||||
int usb_request_queue(struct usb_request *req);
|
||||
|
||||
/* Waits for the results of a previous usb_request_queue operation. timeoutMillis == -1 requests
|
||||
* to wait forever.
|
||||
* Returns a usb_request, or NULL for error.
|
||||
*/
|
||||
struct usb_request *usb_request_wait(struct usb_device *dev, int timeoutMillis);
|
||||
|
||||
/* Cancels a pending usb_request_queue() operation. */
|
||||
int usb_request_cancel(struct usb_request *req);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* __USB_HOST_H */
|
729
android/system/core/libusbhost/usbhost.c
Normal file
729
android/system/core/libusbhost/usbhost.c
Normal file
|
@ -0,0 +1,729 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
// #define DEBUG 1
|
||||
#if DEBUG
|
||||
|
||||
#ifdef USE_LIBLOG
|
||||
#define LOG_TAG "usbhost"
|
||||
#include "log/log.h"
|
||||
#define D ALOGD
|
||||
#else
|
||||
#define D printf
|
||||
#endif
|
||||
|
||||
#else
|
||||
#define D(...)
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#include "usbhost/usbhost.h"
|
||||
|
||||
#define DEV_DIR "/dev"
|
||||
#define DEV_BUS_DIR DEV_DIR "/bus"
|
||||
#define USB_FS_DIR DEV_BUS_DIR "/usb"
|
||||
#define USB_FS_ID_SCANNER USB_FS_DIR "/%d/%d"
|
||||
#define USB_FS_ID_FORMAT USB_FS_DIR "/%03d/%03d"
|
||||
|
||||
// Some devices fail to send string descriptors if we attempt reading > 255 bytes
|
||||
#define MAX_STRING_DESCRIPTOR_LENGTH 255
|
||||
|
||||
// From drivers/usb/core/devio.c
|
||||
// I don't know why this isn't in a kernel header
|
||||
#define MAX_USBFS_BUFFER_SIZE 16384
|
||||
|
||||
#define MAX_USBFS_WD_COUNT 10
|
||||
|
||||
struct usb_host_context {
|
||||
int fd;
|
||||
usb_device_added_cb cb_added;
|
||||
usb_device_removed_cb cb_removed;
|
||||
void *data;
|
||||
int wds[MAX_USBFS_WD_COUNT];
|
||||
int wdd;
|
||||
int wddbus;
|
||||
};
|
||||
|
||||
struct usb_device {
|
||||
char dev_name[64];
|
||||
unsigned char desc[4096];
|
||||
int desc_length;
|
||||
int fd;
|
||||
int writeable;
|
||||
};
|
||||
|
||||
static inline int badname(const char *name)
|
||||
{
|
||||
while(*name) {
|
||||
if(!isdigit(*name++)) return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_existing_devices_bus(char *busname,
|
||||
usb_device_added_cb added_cb,
|
||||
void *client_data)
|
||||
{
|
||||
char devname[32];
|
||||
DIR *devdir;
|
||||
struct dirent *de;
|
||||
int done = 0;
|
||||
|
||||
devdir = opendir(busname);
|
||||
if(devdir == 0) return 0;
|
||||
|
||||
while ((de = readdir(devdir)) && !done) {
|
||||
if(badname(de->d_name)) continue;
|
||||
|
||||
snprintf(devname, sizeof(devname), "%s/%s", busname, de->d_name);
|
||||
done = added_cb(devname, client_data);
|
||||
} // end of devdir while
|
||||
closedir(devdir);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
/* returns true if one of the callbacks indicates we are done */
|
||||
static int find_existing_devices(usb_device_added_cb added_cb,
|
||||
void *client_data)
|
||||
{
|
||||
char busname[32];
|
||||
DIR *busdir;
|
||||
struct dirent *de;
|
||||
int done = 0;
|
||||
|
||||
busdir = opendir(USB_FS_DIR);
|
||||
if(busdir == 0) return 0;
|
||||
|
||||
while ((de = readdir(busdir)) != 0 && !done) {
|
||||
if(badname(de->d_name)) continue;
|
||||
|
||||
snprintf(busname, sizeof(busname), USB_FS_DIR "/%s", de->d_name);
|
||||
done = find_existing_devices_bus(busname, added_cb,
|
||||
client_data);
|
||||
} //end of busdir while
|
||||
closedir(busdir);
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
static void watch_existing_subdirs(struct usb_host_context *context,
|
||||
int *wds, int wd_count)
|
||||
{
|
||||
char path[100];
|
||||
int i, ret;
|
||||
|
||||
wds[0] = inotify_add_watch(context->fd, USB_FS_DIR, IN_CREATE | IN_DELETE);
|
||||
if (wds[0] < 0)
|
||||
return;
|
||||
|
||||
/* watch existing subdirectories of USB_FS_DIR */
|
||||
for (i = 1; i < wd_count; i++) {
|
||||
snprintf(path, sizeof(path), USB_FS_DIR "/%03d", i);
|
||||
ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE);
|
||||
if (ret >= 0)
|
||||
wds[i] = ret;
|
||||
}
|
||||
}
|
||||
|
||||
struct usb_host_context *usb_host_init()
|
||||
{
|
||||
struct usb_host_context *context = calloc(1, sizeof(struct usb_host_context));
|
||||
if (!context) {
|
||||
fprintf(stderr, "out of memory in usb_host_context\n");
|
||||
return NULL;
|
||||
}
|
||||
context->fd = inotify_init();
|
||||
if (context->fd < 0) {
|
||||
fprintf(stderr, "inotify_init failed\n");
|
||||
free(context);
|
||||
return NULL;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
void usb_host_cleanup(struct usb_host_context *context)
|
||||
{
|
||||
close(context->fd);
|
||||
free(context);
|
||||
}
|
||||
|
||||
int usb_host_get_fd(struct usb_host_context *context)
|
||||
{
|
||||
return context->fd;
|
||||
} /* usb_host_get_fd() */
|
||||
|
||||
int usb_host_load(struct usb_host_context *context,
|
||||
usb_device_added_cb added_cb,
|
||||
usb_device_removed_cb removed_cb,
|
||||
usb_discovery_done_cb discovery_done_cb,
|
||||
void *client_data)
|
||||
{
|
||||
int done = 0;
|
||||
int i;
|
||||
|
||||
context->cb_added = added_cb;
|
||||
context->cb_removed = removed_cb;
|
||||
context->data = client_data;
|
||||
|
||||
D("Created device discovery thread\n");
|
||||
|
||||
/* watch for files added and deleted within USB_FS_DIR */
|
||||
context->wddbus = -1;
|
||||
for (i = 0; i < MAX_USBFS_WD_COUNT; i++)
|
||||
context->wds[i] = -1;
|
||||
|
||||
/* watch the root for new subdirectories */
|
||||
context->wdd = inotify_add_watch(context->fd, DEV_DIR, IN_CREATE | IN_DELETE);
|
||||
if (context->wdd < 0) {
|
||||
fprintf(stderr, "inotify_add_watch failed\n");
|
||||
if (discovery_done_cb)
|
||||
discovery_done_cb(client_data);
|
||||
return done;
|
||||
}
|
||||
|
||||
watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
|
||||
|
||||
/* check for existing devices first, after we have inotify set up */
|
||||
done = find_existing_devices(added_cb, client_data);
|
||||
if (discovery_done_cb)
|
||||
done |= discovery_done_cb(client_data);
|
||||
|
||||
return done;
|
||||
} /* usb_host_load() */
|
||||
|
||||
int usb_host_read_event(struct usb_host_context *context)
|
||||
{
|
||||
struct inotify_event* event;
|
||||
char event_buf[512];
|
||||
char path[100];
|
||||
int i, ret, done = 0;
|
||||
int offset = 0;
|
||||
int wd;
|
||||
|
||||
ret = read(context->fd, event_buf, sizeof(event_buf));
|
||||
if (ret >= (int)sizeof(struct inotify_event)) {
|
||||
while (offset < ret && !done) {
|
||||
event = (struct inotify_event*)&event_buf[offset];
|
||||
done = 0;
|
||||
wd = event->wd;
|
||||
if (wd == context->wdd) {
|
||||
if ((event->mask & IN_CREATE) && !strcmp(event->name, "bus")) {
|
||||
context->wddbus = inotify_add_watch(context->fd, DEV_BUS_DIR, IN_CREATE | IN_DELETE);
|
||||
if (context->wddbus < 0) {
|
||||
done = 1;
|
||||
} else {
|
||||
watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
|
||||
done = find_existing_devices(context->cb_added, context->data);
|
||||
}
|
||||
}
|
||||
} else if (wd == context->wddbus) {
|
||||
if ((event->mask & IN_CREATE) && !strcmp(event->name, "usb")) {
|
||||
watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);
|
||||
done = find_existing_devices(context->cb_added, context->data);
|
||||
} else if ((event->mask & IN_DELETE) && !strcmp(event->name, "usb")) {
|
||||
for (i = 0; i < MAX_USBFS_WD_COUNT; i++) {
|
||||
if (context->wds[i] >= 0) {
|
||||
inotify_rm_watch(context->fd, context->wds[i]);
|
||||
context->wds[i] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (wd == context->wds[0]) {
|
||||
i = atoi(event->name);
|
||||
snprintf(path, sizeof(path), USB_FS_DIR "/%s", event->name);
|
||||
D("%s subdirectory %s: index: %d\n", (event->mask & IN_CREATE) ?
|
||||
"new" : "gone", path, i);
|
||||
if (i > 0 && i < MAX_USBFS_WD_COUNT) {
|
||||
int local_ret = 0;
|
||||
if (event->mask & IN_CREATE) {
|
||||
local_ret = inotify_add_watch(context->fd, path,
|
||||
IN_CREATE | IN_DELETE);
|
||||
if (local_ret >= 0)
|
||||
context->wds[i] = local_ret;
|
||||
done = find_existing_devices_bus(path, context->cb_added,
|
||||
context->data);
|
||||
} else if (event->mask & IN_DELETE) {
|
||||
inotify_rm_watch(context->fd, context->wds[i]);
|
||||
context->wds[i] = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = 1; (i < MAX_USBFS_WD_COUNT) && !done; i++) {
|
||||
if (wd == context->wds[i]) {
|
||||
snprintf(path, sizeof(path), USB_FS_DIR "/%03d/%s", i, event->name);
|
||||
if (event->mask == IN_CREATE) {
|
||||
D("new device %s\n", path);
|
||||
done = context->cb_added(path, context->data);
|
||||
} else if (event->mask == IN_DELETE) {
|
||||
D("gone device %s\n", path);
|
||||
done = context->cb_removed(path, context->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
offset += sizeof(struct inotify_event) + event->len;
|
||||
}
|
||||
}
|
||||
|
||||
return done;
|
||||
} /* usb_host_read_event() */
|
||||
|
||||
void usb_host_run(struct usb_host_context *context,
|
||||
usb_device_added_cb added_cb,
|
||||
usb_device_removed_cb removed_cb,
|
||||
usb_discovery_done_cb discovery_done_cb,
|
||||
void *client_data)
|
||||
{
|
||||
int done;
|
||||
|
||||
done = usb_host_load(context, added_cb, removed_cb, discovery_done_cb, client_data);
|
||||
|
||||
while (!done) {
|
||||
|
||||
done = usb_host_read_event(context);
|
||||
}
|
||||
} /* usb_host_run() */
|
||||
|
||||
struct usb_device *usb_device_open(const char *dev_name)
|
||||
{
|
||||
int fd, did_retry = 0, writeable = 1;
|
||||
|
||||
D("usb_device_open %s\n", dev_name);
|
||||
|
||||
retry:
|
||||
fd = open(dev_name, O_RDWR);
|
||||
if (fd < 0) {
|
||||
/* if we fail, see if have read-only access */
|
||||
fd = open(dev_name, O_RDONLY);
|
||||
D("usb_device_open open returned %d errno %d\n", fd, errno);
|
||||
if (fd < 0 && (errno == EACCES || errno == ENOENT) && !did_retry) {
|
||||
/* work around race condition between inotify and permissions management */
|
||||
sleep(1);
|
||||
did_retry = 1;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
writeable = 0;
|
||||
D("[ usb open read-only %s fd = %d]\n", dev_name, fd);
|
||||
}
|
||||
|
||||
struct usb_device* result = usb_device_new(dev_name, fd);
|
||||
if (result)
|
||||
result->writeable = writeable;
|
||||
return result;
|
||||
}
|
||||
|
||||
void usb_device_close(struct usb_device *device)
|
||||
{
|
||||
close(device->fd);
|
||||
free(device);
|
||||
}
|
||||
|
||||
struct usb_device *usb_device_new(const char *dev_name, int fd)
|
||||
{
|
||||
struct usb_device *device = calloc(1, sizeof(struct usb_device));
|
||||
int length;
|
||||
|
||||
D("usb_device_new %s fd: %d\n", dev_name, fd);
|
||||
|
||||
if (lseek(fd, 0, SEEK_SET) != 0)
|
||||
goto failed;
|
||||
length = read(fd, device->desc, sizeof(device->desc));
|
||||
D("usb_device_new read returned %d errno %d\n", length, errno);
|
||||
if (length < 0)
|
||||
goto failed;
|
||||
|
||||
strncpy(device->dev_name, dev_name, sizeof(device->dev_name) - 1);
|
||||
device->fd = fd;
|
||||
device->desc_length = length;
|
||||
// assume we are writeable, since usb_device_get_fd will only return writeable fds
|
||||
device->writeable = 1;
|
||||
return device;
|
||||
|
||||
failed:
|
||||
close(fd);
|
||||
free(device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int usb_device_reopen_writeable(struct usb_device *device)
|
||||
{
|
||||
if (device->writeable)
|
||||
return 1;
|
||||
|
||||
int fd = open(device->dev_name, O_RDWR);
|
||||
if (fd >= 0) {
|
||||
close(device->fd);
|
||||
device->fd = fd;
|
||||
device->writeable = 1;
|
||||
return 1;
|
||||
}
|
||||
D("usb_device_reopen_writeable failed errno %d\n", errno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_device_get_fd(struct usb_device *device)
|
||||
{
|
||||
if (!usb_device_reopen_writeable(device))
|
||||
return -1;
|
||||
return device->fd;
|
||||
}
|
||||
|
||||
const char* usb_device_get_name(struct usb_device *device)
|
||||
{
|
||||
return device->dev_name;
|
||||
}
|
||||
|
||||
int usb_device_get_unique_id(struct usb_device *device)
|
||||
{
|
||||
int bus = 0, dev = 0;
|
||||
sscanf(device->dev_name, USB_FS_ID_SCANNER, &bus, &dev);
|
||||
return bus * 1000 + dev;
|
||||
}
|
||||
|
||||
int usb_device_get_unique_id_from_name(const char* name)
|
||||
{
|
||||
int bus = 0, dev = 0;
|
||||
sscanf(name, USB_FS_ID_SCANNER, &bus, &dev);
|
||||
return bus * 1000 + dev;
|
||||
}
|
||||
|
||||
char* usb_device_get_name_from_unique_id(int id)
|
||||
{
|
||||
int bus = id / 1000;
|
||||
int dev = id % 1000;
|
||||
char* result = (char *)calloc(1, strlen(USB_FS_ID_FORMAT));
|
||||
snprintf(result, strlen(USB_FS_ID_FORMAT) - 1, USB_FS_ID_FORMAT, bus, dev);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16_t usb_device_get_vendor_id(struct usb_device *device)
|
||||
{
|
||||
struct usb_device_descriptor* desc = (struct usb_device_descriptor*)device->desc;
|
||||
return __le16_to_cpu(desc->idVendor);
|
||||
}
|
||||
|
||||
uint16_t usb_device_get_product_id(struct usb_device *device)
|
||||
{
|
||||
struct usb_device_descriptor* desc = (struct usb_device_descriptor*)device->desc;
|
||||
return __le16_to_cpu(desc->idProduct);
|
||||
}
|
||||
|
||||
const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device)
|
||||
{
|
||||
return (struct usb_device_descriptor*)device->desc;
|
||||
}
|
||||
|
||||
char* usb_device_get_string(struct usb_device *device, int id, int timeout)
|
||||
{
|
||||
char string[256];
|
||||
__u16 buffer[MAX_STRING_DESCRIPTOR_LENGTH / sizeof(__u16)];
|
||||
__u16 languages[MAX_STRING_DESCRIPTOR_LENGTH / sizeof(__u16)];
|
||||
int i, result;
|
||||
int languageCount = 0;
|
||||
|
||||
if (id == 0) return NULL;
|
||||
|
||||
string[0] = 0;
|
||||
memset(languages, 0, sizeof(languages));
|
||||
|
||||
// read list of supported languages
|
||||
result = usb_device_control_transfer(device,
|
||||
USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR,
|
||||
(USB_DT_STRING << 8) | 0, 0, languages, sizeof(languages),
|
||||
timeout);
|
||||
if (result > 0)
|
||||
languageCount = (result - 2) / 2;
|
||||
|
||||
for (i = 1; i <= languageCount; i++) {
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
result = usb_device_control_transfer(device,
|
||||
USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR,
|
||||
(USB_DT_STRING << 8) | id, languages[i], buffer, sizeof(buffer),
|
||||
timeout);
|
||||
if (result > 0) {
|
||||
int i;
|
||||
// skip first word, and copy the rest to the string, changing shorts to bytes.
|
||||
result /= 2;
|
||||
for (i = 1; i < result; i++)
|
||||
string[i - 1] = buffer[i];
|
||||
string[i - 1] = 0;
|
||||
return strdup(string);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* usb_device_get_manufacturer_name(struct usb_device *device, int timeout)
|
||||
{
|
||||
struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
|
||||
return usb_device_get_string(device, desc->iManufacturer, timeout);
|
||||
}
|
||||
|
||||
char* usb_device_get_product_name(struct usb_device *device, int timeout)
|
||||
{
|
||||
struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
|
||||
return usb_device_get_string(device, desc->iProduct, timeout);
|
||||
}
|
||||
|
||||
int usb_device_get_version(struct usb_device *device)
|
||||
{
|
||||
struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
|
||||
return desc->bcdUSB;
|
||||
}
|
||||
|
||||
char* usb_device_get_serial(struct usb_device *device, int timeout)
|
||||
{
|
||||
struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;
|
||||
return usb_device_get_string(device, desc->iSerialNumber, timeout);
|
||||
}
|
||||
|
||||
int usb_device_is_writeable(struct usb_device *device)
|
||||
{
|
||||
return device->writeable;
|
||||
}
|
||||
|
||||
void usb_descriptor_iter_init(struct usb_device *device, struct usb_descriptor_iter *iter)
|
||||
{
|
||||
iter->config = device->desc;
|
||||
iter->config_end = device->desc + device->desc_length;
|
||||
iter->curr_desc = device->desc;
|
||||
}
|
||||
|
||||
struct usb_descriptor_header *usb_descriptor_iter_next(struct usb_descriptor_iter *iter)
|
||||
{
|
||||
struct usb_descriptor_header* next;
|
||||
if (iter->curr_desc >= iter->config_end)
|
||||
return NULL;
|
||||
next = (struct usb_descriptor_header*)iter->curr_desc;
|
||||
iter->curr_desc += next->bLength;
|
||||
return next;
|
||||
}
|
||||
|
||||
int usb_device_claim_interface(struct usb_device *device, unsigned int interface)
|
||||
{
|
||||
return ioctl(device->fd, USBDEVFS_CLAIMINTERFACE, &interface);
|
||||
}
|
||||
|
||||
int usb_device_release_interface(struct usb_device *device, unsigned int interface)
|
||||
{
|
||||
return ioctl(device->fd, USBDEVFS_RELEASEINTERFACE, &interface);
|
||||
}
|
||||
|
||||
int usb_device_connect_kernel_driver(struct usb_device *device,
|
||||
unsigned int interface, int connect)
|
||||
{
|
||||
struct usbdevfs_ioctl ctl;
|
||||
|
||||
ctl.ifno = interface;
|
||||
ctl.ioctl_code = (connect ? USBDEVFS_CONNECT : USBDEVFS_DISCONNECT);
|
||||
ctl.data = NULL;
|
||||
return ioctl(device->fd, USBDEVFS_IOCTL, &ctl);
|
||||
}
|
||||
|
||||
int usb_device_set_configuration(struct usb_device *device, int configuration)
|
||||
{
|
||||
return ioctl(device->fd, USBDEVFS_SETCONFIGURATION, &configuration);
|
||||
}
|
||||
|
||||
int usb_device_set_interface(struct usb_device *device, unsigned int interface,
|
||||
unsigned int alt_setting)
|
||||
{
|
||||
struct usbdevfs_setinterface ctl;
|
||||
|
||||
ctl.interface = interface;
|
||||
ctl.altsetting = alt_setting;
|
||||
return ioctl(device->fd, USBDEVFS_SETINTERFACE, &ctl);
|
||||
}
|
||||
|
||||
int usb_device_control_transfer(struct usb_device *device,
|
||||
int requestType,
|
||||
int request,
|
||||
int value,
|
||||
int index,
|
||||
void* buffer,
|
||||
int length,
|
||||
unsigned int timeout)
|
||||
{
|
||||
struct usbdevfs_ctrltransfer ctrl;
|
||||
|
||||
// this usually requires read/write permission
|
||||
if (!usb_device_reopen_writeable(device))
|
||||
return -1;
|
||||
|
||||
memset(&ctrl, 0, sizeof(ctrl));
|
||||
ctrl.bRequestType = requestType;
|
||||
ctrl.bRequest = request;
|
||||
ctrl.wValue = value;
|
||||
ctrl.wIndex = index;
|
||||
ctrl.wLength = length;
|
||||
ctrl.data = buffer;
|
||||
ctrl.timeout = timeout;
|
||||
return ioctl(device->fd, USBDEVFS_CONTROL, &ctrl);
|
||||
}
|
||||
|
||||
int usb_device_bulk_transfer(struct usb_device *device,
|
||||
int endpoint,
|
||||
void* buffer,
|
||||
unsigned int length,
|
||||
unsigned int timeout)
|
||||
{
|
||||
struct usbdevfs_bulktransfer ctrl;
|
||||
|
||||
// need to limit request size to avoid EINVAL
|
||||
if (length > MAX_USBFS_BUFFER_SIZE)
|
||||
length = MAX_USBFS_BUFFER_SIZE;
|
||||
|
||||
memset(&ctrl, 0, sizeof(ctrl));
|
||||
ctrl.ep = endpoint;
|
||||
ctrl.len = length;
|
||||
ctrl.data = buffer;
|
||||
ctrl.timeout = timeout;
|
||||
return ioctl(device->fd, USBDEVFS_BULK, &ctrl);
|
||||
}
|
||||
|
||||
int usb_device_reset(struct usb_device *device)
|
||||
{
|
||||
return ioctl(device->fd, USBDEVFS_RESET);
|
||||
}
|
||||
|
||||
struct usb_request *usb_request_new(struct usb_device *dev,
|
||||
const struct usb_endpoint_descriptor *ep_desc)
|
||||
{
|
||||
struct usbdevfs_urb *urb = calloc(1, sizeof(struct usbdevfs_urb));
|
||||
if (!urb)
|
||||
return NULL;
|
||||
|
||||
if ((ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)
|
||||
urb->type = USBDEVFS_URB_TYPE_BULK;
|
||||
else if ((ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)
|
||||
urb->type = USBDEVFS_URB_TYPE_INTERRUPT;
|
||||
else {
|
||||
D("Unsupported endpoint type %d", ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
|
||||
free(urb);
|
||||
return NULL;
|
||||
}
|
||||
urb->endpoint = ep_desc->bEndpointAddress;
|
||||
|
||||
struct usb_request *req = calloc(1, sizeof(struct usb_request));
|
||||
if (!req) {
|
||||
free(urb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
req->dev = dev;
|
||||
req->max_packet_size = __le16_to_cpu(ep_desc->wMaxPacketSize);
|
||||
req->private_data = urb;
|
||||
req->endpoint = urb->endpoint;
|
||||
urb->usercontext = req;
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
void usb_request_free(struct usb_request *req)
|
||||
{
|
||||
free(req->private_data);
|
||||
free(req);
|
||||
}
|
||||
|
||||
int usb_request_queue(struct usb_request *req)
|
||||
{
|
||||
struct usbdevfs_urb *urb = (struct usbdevfs_urb*)req->private_data;
|
||||
int res;
|
||||
|
||||
urb->status = -1;
|
||||
urb->buffer = req->buffer;
|
||||
// need to limit request size to avoid EINVAL
|
||||
if (req->buffer_length > MAX_USBFS_BUFFER_SIZE)
|
||||
urb->buffer_length = MAX_USBFS_BUFFER_SIZE;
|
||||
else
|
||||
urb->buffer_length = req->buffer_length;
|
||||
|
||||
do {
|
||||
res = ioctl(req->dev->fd, USBDEVFS_SUBMITURB, urb);
|
||||
} while((res < 0) && (errno == EINTR));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
struct usb_request *usb_request_wait(struct usb_device *dev, int timeoutMillis)
|
||||
{
|
||||
// Poll until a request becomes available if there is a timeout
|
||||
if (timeoutMillis > 0) {
|
||||
struct pollfd p = {.fd = dev->fd, .events = POLLOUT, .revents = 0};
|
||||
|
||||
int res = poll(&p, 1, timeoutMillis);
|
||||
|
||||
if (res != 1 || p.revents != POLLOUT) {
|
||||
D("[ poll - event %d, error %d]\n", p.revents, errno);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Read the request. This should usually succeed as we polled before, but it can fail e.g. when
|
||||
// two threads are reading usb requests at the same time and only a single request is available.
|
||||
struct usbdevfs_urb *urb = NULL;
|
||||
int res = TEMP_FAILURE_RETRY(ioctl(dev->fd, timeoutMillis == -1 ? USBDEVFS_REAPURB :
|
||||
USBDEVFS_REAPURBNDELAY, &urb));
|
||||
D("%s returned %d\n", timeoutMillis == -1 ? "USBDEVFS_REAPURB" : "USBDEVFS_REAPURBNDELAY", res);
|
||||
|
||||
if (res < 0) {
|
||||
D("[ reap urb - error %d]\n", errno);
|
||||
return NULL;
|
||||
} else {
|
||||
D("[ urb @%p status = %d, actual = %d ]\n", urb, urb->status, urb->actual_length);
|
||||
|
||||
struct usb_request *req = (struct usb_request*)urb->usercontext;
|
||||
req->actual_length = urb->actual_length;
|
||||
|
||||
return req;
|
||||
}
|
||||
}
|
||||
|
||||
int usb_request_cancel(struct usb_request *req)
|
||||
{
|
||||
struct usbdevfs_urb *urb = ((struct usbdevfs_urb*)req->private_data);
|
||||
return ioctl(req->dev->fd, USBDEVFS_DISCARDURB, urb);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue