130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood/* 230ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood * Copyright (C) 2010 The Android Open Source Project 330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood * 430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood * Licensed under the Apache License, Version 2.0 (the "License"); 530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood * you may not use this file except in compliance with the License. 630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood * You may obtain a copy of the License at 730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood * 830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood * http://www.apache.org/licenses/LICENSE-2.0 930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood * 1030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood * Unless required by applicable law or agreed to in writing, software 1130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood * distributed under the License is distributed on an "AS IS" BASIS, 1230ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood * See the License for the specific language governing permissions and 1430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood * limitations under the License. 1530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood */ 1630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 173695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann#ifndef _GNU_SOURCE 183695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann#define _GNU_SOURCE 193695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann#endif 203695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann 21e15af09876c5e0301084f46b8036aed147cf9c24Mike Lockwood// #define DEBUG 1 22e15af09876c5e0301084f46b8036aed147cf9c24Mike Lockwood#if DEBUG 23e15af09876c5e0301084f46b8036aed147cf9c24Mike Lockwood 24e15af09876c5e0301084f46b8036aed147cf9c24Mike Lockwood#ifdef USE_LIBLOG 25e15af09876c5e0301084f46b8036aed147cf9c24Mike Lockwood#define LOG_TAG "usbhost" 26a5c6017d8ff9af9c2a65d3a78ddc0d2813964b43Dan Willemsen#include "log/log.h" 278d66c49258ac4f59bd67c23c9c914cca81f85b01Steve Block#define D ALOGD 28e15af09876c5e0301084f46b8036aed147cf9c24Mike Lockwood#else 29e15af09876c5e0301084f46b8036aed147cf9c24Mike Lockwood#define D printf 30e15af09876c5e0301084f46b8036aed147cf9c24Mike Lockwood#endif 31e15af09876c5e0301084f46b8036aed147cf9c24Mike Lockwood 32e15af09876c5e0301084f46b8036aed147cf9c24Mike Lockwood#else 33e15af09876c5e0301084f46b8036aed147cf9c24Mike Lockwood#define D(...) 34e15af09876c5e0301084f46b8036aed147cf9c24Mike Lockwood#endif 35e15af09876c5e0301084f46b8036aed147cf9c24Mike Lockwood 3630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood#include <stdio.h> 3730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood#include <stdlib.h> 3830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood#include <unistd.h> 3930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood#include <string.h> 40dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet#include <stddef.h> 4130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 4230ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood#include <sys/ioctl.h> 4330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood#include <sys/types.h> 4430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood#include <sys/time.h> 4530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood#include <sys/inotify.h> 4630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood#include <dirent.h> 4730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood#include <fcntl.h> 4830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood#include <errno.h> 4930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood#include <ctype.h> 503695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann#include <poll.h> 5130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood#include <pthread.h> 5230ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 5330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood#include <linux/usbdevice_fs.h> 5430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood#include <asm/byteorder.h> 5530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 5630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood#include "usbhost/usbhost.h" 5730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 58f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby#define DEV_DIR "/dev" 59a306ced1ac383557dabfb38c70e5f5e2a2ec0510Ziv Hendel#define DEV_BUS_DIR DEV_DIR "/bus" 60a306ced1ac383557dabfb38c70e5f5e2a2ec0510Ziv Hendel#define USB_FS_DIR DEV_BUS_DIR "/usb" 61dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet#define USB_FS_ID_SCANNER USB_FS_DIR "/%d/%d" 62dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet#define USB_FS_ID_FORMAT USB_FS_DIR "/%03d/%03d" 6330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 640dd1aab7b5184f41ec838def8493d16cfe70b739Mike Lockwood// Some devices fail to send string descriptors if we attempt reading > 255 bytes 650dd1aab7b5184f41ec838def8493d16cfe70b739Mike Lockwood#define MAX_STRING_DESCRIPTOR_LENGTH 255 660dd1aab7b5184f41ec838def8493d16cfe70b739Mike Lockwood 67c4c00d8e6a215020be87f6702844267e105e1f0eMike Lockwood// From drivers/usb/core/devio.c 68c4c00d8e6a215020be87f6702844267e105e1f0eMike Lockwood// I don't know why this isn't in a kernel header 69c4c00d8e6a215020be87f6702844267e105e1f0eMike Lockwood#define MAX_USBFS_BUFFER_SIZE 16384 7030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 71dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet#define MAX_USBFS_WD_COUNT 10 72dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet 736ac3aa157493ef24bc837b679dd8292fad8961e0Mike Lockwoodstruct usb_host_context { 74dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet int fd; 75dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet usb_device_added_cb cb_added; 76dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet usb_device_removed_cb cb_removed; 77dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet void *data; 78dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet int wds[MAX_USBFS_WD_COUNT]; 79dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet int wdd; 80a306ced1ac383557dabfb38c70e5f5e2a2ec0510Ziv Hendel int wddbus; 816ac3aa157493ef24bc837b679dd8292fad8961e0Mike Lockwood}; 826ac3aa157493ef24bc837b679dd8292fad8961e0Mike Lockwood 8330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwoodstruct usb_device { 8430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood char dev_name[64]; 85ec9e7b1c1844e66c39a4f63ded8208c1717380f8Mike Lockwood unsigned char desc[4096]; 8630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood int desc_length; 8730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood int fd; 8830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood int writeable; 8930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood}; 9030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 9130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwoodstatic inline int badname(const char *name) 9230ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 9330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood while(*name) { 9430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood if(!isdigit(*name++)) return 1; 9530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood } 9630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood return 0; 9730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 9830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 996cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Gobystatic int find_existing_devices_bus(char *busname, 1006cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby usb_device_added_cb added_cb, 1016cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby void *client_data) 1026cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby{ 1036cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby char devname[32]; 1046cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby DIR *devdir; 1056cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby struct dirent *de; 1066cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby int done = 0; 1076cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby 1086cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby devdir = opendir(busname); 1096cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby if(devdir == 0) return 0; 1106cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby 1116cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby while ((de = readdir(devdir)) && !done) { 1126cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby if(badname(de->d_name)) continue; 1136cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby 1146cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby snprintf(devname, sizeof(devname), "%s/%s", busname, de->d_name); 1156cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby done = added_cb(devname, client_data); 1166cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby } // end of devdir while 1176cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby closedir(devdir); 1186cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby 1196cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby return done; 1206cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby} 1216cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby 1227a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood/* returns true if one of the callbacks indicates we are done */ 1237a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwoodstatic int find_existing_devices(usb_device_added_cb added_cb, 1247a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood void *client_data) 12530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 1266cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby char busname[32]; 1276cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby DIR *busdir; 12830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood struct dirent *de; 1297a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood int done = 0; 13030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 13130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood busdir = opendir(USB_FS_DIR); 132f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby if(busdir == 0) return 0; 13330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 1347a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood while ((de = readdir(busdir)) != 0 && !done) { 13530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood if(badname(de->d_name)) continue; 13630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 137dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet snprintf(busname, sizeof(busname), USB_FS_DIR "/%s", de->d_name); 1386cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby done = find_existing_devices_bus(busname, added_cb, 1396cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby client_data); 140dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet } //end of busdir while 14130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood closedir(busdir); 1427a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood 1437a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood return done; 1447a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood} 1457a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood 146f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Gobystatic void watch_existing_subdirs(struct usb_host_context *context, 147f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby int *wds, int wd_count) 148f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby{ 149f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby char path[100]; 150f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby int i, ret; 151f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby 152f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby wds[0] = inotify_add_watch(context->fd, USB_FS_DIR, IN_CREATE | IN_DELETE); 153f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby if (wds[0] < 0) 154f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby return; 155f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby 156f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby /* watch existing subdirectories of USB_FS_DIR */ 157f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby for (i = 1; i < wd_count; i++) { 158dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet snprintf(path, sizeof(path), USB_FS_DIR "/%03d", i); 159f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE); 160f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby if (ret >= 0) 161f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby wds[i] = ret; 162f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby } 163f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby} 164f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby 1657a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwoodstruct usb_host_context *usb_host_init() 1667a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood{ 1677a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood struct usb_host_context *context = calloc(1, sizeof(struct usb_host_context)); 1687a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood if (!context) { 1697a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood fprintf(stderr, "out of memory in usb_host_context\n"); 1707a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood return NULL; 1717a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood } 1727a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood context->fd = inotify_init(); 1737a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood if (context->fd < 0) { 1747a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood fprintf(stderr, "inotify_init failed\n"); 1757a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood free(context); 1767a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood return NULL; 1777a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood } 1787a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood return context; 1797a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood} 1807a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood 1817a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwoodvoid usb_host_cleanup(struct usb_host_context *context) 1827a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood{ 1837a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood close(context->fd); 1847a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood free(context); 18530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 18630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 187dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquetint usb_host_get_fd(struct usb_host_context *context) 188dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet{ 189dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet return context->fd; 190dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet} /* usb_host_get_fd() */ 191dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet 192dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquetint usb_host_load(struct usb_host_context *context, 1937a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood usb_device_added_cb added_cb, 1947a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood usb_device_removed_cb removed_cb, 195a805519ceedc53afa5453a6d8a7d80038d885d9fMike Lockwood usb_discovery_done_cb discovery_done_cb, 1967a96ba436c9a2bacc64e712bdb53bd7accc5c3a9Mike Lockwood void *client_data) 19730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 198dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet int done = 0; 199dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet int i; 200dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet 201dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet context->cb_added = added_cb; 202dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet context->cb_removed = removed_cb; 203dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet context->data = client_data; 20430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 20530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood D("Created device discovery thread\n"); 20630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 20730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood /* watch for files added and deleted within USB_FS_DIR */ 208a306ced1ac383557dabfb38c70e5f5e2a2ec0510Ziv Hendel context->wddbus = -1; 209dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet for (i = 0; i < MAX_USBFS_WD_COUNT; i++) 210dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet context->wds[i] = -1; 211f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby 21230ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood /* watch the root for new subdirectories */ 213dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet context->wdd = inotify_add_watch(context->fd, DEV_DIR, IN_CREATE | IN_DELETE); 214dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet if (context->wdd < 0) { 21530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood fprintf(stderr, "inotify_add_watch failed\n"); 216e8849d190a3097274dcb64416e564538a1017054Mike Lockwood if (discovery_done_cb) 217e8849d190a3097274dcb64416e564538a1017054Mike Lockwood discovery_done_cb(client_data); 218dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet return done; 21930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood } 22030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 221dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT); 22230ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 22330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood /* check for existing devices first, after we have inotify set up */ 2246cc883ca098f34bd126478a6aa19d5cce48d71a9Benoit Goby done = find_existing_devices(added_cb, client_data); 225a805519ceedc53afa5453a6d8a7d80038d885d9fMike Lockwood if (discovery_done_cb) 226a805519ceedc53afa5453a6d8a7d80038d885d9fMike Lockwood done |= discovery_done_cb(client_data); 22730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 228dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet return done; 229dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet} /* usb_host_load() */ 230dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet 231dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquetint usb_host_read_event(struct usb_host_context *context) 232dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet{ 233dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet struct inotify_event* event; 234dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet char event_buf[512]; 235dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet char path[100]; 236dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet int i, ret, done = 0; 237f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel int offset = 0; 238dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet int wd; 239dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet 240dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet ret = read(context->fd, event_buf, sizeof(event_buf)); 241dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet if (ret >= (int)sizeof(struct inotify_event)) { 242a306ced1ac383557dabfb38c70e5f5e2a2ec0510Ziv Hendel while (offset < ret && !done) { 243f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel event = (struct inotify_event*)&event_buf[offset]; 244f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel done = 0; 245f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel wd = event->wd; 246f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel if (wd == context->wdd) { 247f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel if ((event->mask & IN_CREATE) && !strcmp(event->name, "bus")) { 248a306ced1ac383557dabfb38c70e5f5e2a2ec0510Ziv Hendel context->wddbus = inotify_add_watch(context->fd, DEV_BUS_DIR, IN_CREATE | IN_DELETE); 249a306ced1ac383557dabfb38c70e5f5e2a2ec0510Ziv Hendel if (context->wddbus < 0) { 250a306ced1ac383557dabfb38c70e5f5e2a2ec0510Ziv Hendel done = 1; 251a306ced1ac383557dabfb38c70e5f5e2a2ec0510Ziv Hendel } else { 252a306ced1ac383557dabfb38c70e5f5e2a2ec0510Ziv Hendel watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT); 253a306ced1ac383557dabfb38c70e5f5e2a2ec0510Ziv Hendel done = find_existing_devices(context->cb_added, context->data); 254a306ced1ac383557dabfb38c70e5f5e2a2ec0510Ziv Hendel } 255a306ced1ac383557dabfb38c70e5f5e2a2ec0510Ziv Hendel } 256a306ced1ac383557dabfb38c70e5f5e2a2ec0510Ziv Hendel } else if (wd == context->wddbus) { 257a306ced1ac383557dabfb38c70e5f5e2a2ec0510Ziv Hendel if ((event->mask & IN_CREATE) && !strcmp(event->name, "usb")) { 258f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT); 259f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel done = find_existing_devices(context->cb_added, context->data); 260a306ced1ac383557dabfb38c70e5f5e2a2ec0510Ziv Hendel } else if ((event->mask & IN_DELETE) && !strcmp(event->name, "usb")) { 261f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel for (i = 0; i < MAX_USBFS_WD_COUNT; i++) { 262f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel if (context->wds[i] >= 0) { 263f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel inotify_rm_watch(context->fd, context->wds[i]); 264f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel context->wds[i] = -1; 265f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel } 266f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel } 267f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel } 268f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel } else if (wd == context->wds[0]) { 269f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel i = atoi(event->name); 270f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel snprintf(path, sizeof(path), USB_FS_DIR "/%s", event->name); 271f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel D("%s subdirectory %s: index: %d\n", (event->mask & IN_CREATE) ? 272f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel "new" : "gone", path, i); 273f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel if (i > 0 && i < MAX_USBFS_WD_COUNT) { 2743c1d7b34c151ec1669e0d190a0839718242682c9Bo Huang int local_ret = 0; 275f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel if (event->mask & IN_CREATE) { 2763c1d7b34c151ec1669e0d190a0839718242682c9Bo Huang local_ret = inotify_add_watch(context->fd, path, 277f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel IN_CREATE | IN_DELETE); 2783c1d7b34c151ec1669e0d190a0839718242682c9Bo Huang if (local_ret >= 0) 2793c1d7b34c151ec1669e0d190a0839718242682c9Bo Huang context->wds[i] = local_ret; 280f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel done = find_existing_devices_bus(path, context->cb_added, 281f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel context->data); 282f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel } else if (event->mask & IN_DELETE) { 283dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet inotify_rm_watch(context->fd, context->wds[i]); 284dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet context->wds[i] = -1; 285f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby } 286f4de078388242fff9b68c04b122e8a095cab9b0eBenoit Goby } 287f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel } else { 288f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel for (i = 1; (i < MAX_USBFS_WD_COUNT) && !done; i++) { 289f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel if (wd == context->wds[i]) { 290f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel snprintf(path, sizeof(path), USB_FS_DIR "/%03d/%s", i, event->name); 291f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel if (event->mask == IN_CREATE) { 292f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel D("new device %s\n", path); 293f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel done = context->cb_added(path, context->data); 294f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel } else if (event->mask == IN_DELETE) { 295f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel D("gone device %s\n", path); 296f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel done = context->cb_removed(path, context->data); 297f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel } 29830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood } 29930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood } 30030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood } 301f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel 302f75ea8d082ff39e316bd4d73979b610cc4b5e4bcZiv Hendel offset += sizeof(struct inotify_event) + event->len; 30330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood } 30430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood } 305dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet 306dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet return done; 307dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet} /* usb_host_read_event() */ 308dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet 309dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquetvoid usb_host_run(struct usb_host_context *context, 310dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet usb_device_added_cb added_cb, 311dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet usb_device_removed_cb removed_cb, 312dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet usb_discovery_done_cb discovery_done_cb, 313dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet void *client_data) 314dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet{ 315dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet int done; 316dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet 317dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet done = usb_host_load(context, added_cb, removed_cb, discovery_done_cb, client_data); 318dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet 319dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet while (!done) { 320dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet 321dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet done = usb_host_read_event(context); 322dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet } 323dea46b6657845a366d13f57c720eab28c9062ab7Guillaume Ranquet} /* usb_host_run() */ 32430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 32530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwoodstruct usb_device *usb_device_open(const char *dev_name) 32630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 327cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood int fd, did_retry = 0, writeable = 1; 32830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 329ec9e7b1c1844e66c39a4f63ded8208c1717380f8Mike Lockwood D("usb_device_open %s\n", dev_name); 330ec9e7b1c1844e66c39a4f63ded8208c1717380f8Mike Lockwood 33130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwoodretry: 33230ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood fd = open(dev_name, O_RDWR); 33330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood if (fd < 0) { 33430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood /* if we fail, see if have read-only access */ 33530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood fd = open(dev_name, O_RDONLY); 336e15af09876c5e0301084f46b8036aed147cf9c24Mike Lockwood D("usb_device_open open returned %d errno %d\n", fd, errno); 337e15af09876c5e0301084f46b8036aed147cf9c24Mike Lockwood if (fd < 0 && (errno == EACCES || errno == ENOENT) && !did_retry) { 33830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood /* work around race condition between inotify and permissions management */ 33930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood sleep(1); 34030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood did_retry = 1; 34130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood goto retry; 34230ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood } 34330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 344cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood if (fd < 0) 345cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood return NULL; 346cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood writeable = 0; 34730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood D("[ usb open read-only %s fd = %d]\n", dev_name, fd); 34830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood } 34930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 350cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood struct usb_device* result = usb_device_new(dev_name, fd); 351cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood if (result) 352cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood result->writeable = writeable; 353cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood return result; 354cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood} 355cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood 356cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwoodvoid usb_device_close(struct usb_device *device) 357cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood{ 358cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood close(device->fd); 359cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood free(device); 360cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood} 361cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood 362cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwoodstruct usb_device *usb_device_new(const char *dev_name, int fd) 363cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood{ 364cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood struct usb_device *device = calloc(1, sizeof(struct usb_device)); 365cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood int length; 366cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood 367ec9e7b1c1844e66c39a4f63ded8208c1717380f8Mike Lockwood D("usb_device_new %s fd: %d\n", dev_name, fd); 368ec9e7b1c1844e66c39a4f63ded8208c1717380f8Mike Lockwood 369cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood if (lseek(fd, 0, SEEK_SET) != 0) 370cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood goto failed; 37130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood length = read(fd, device->desc, sizeof(device->desc)); 372ec9e7b1c1844e66c39a4f63ded8208c1717380f8Mike Lockwood D("usb_device_new read returned %d errno %d\n", length, errno); 37330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood if (length < 0) 374cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood goto failed; 37530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 37693aff72d9b469f4b77468bab654cd65243b662e6Mike Lockwood strncpy(device->dev_name, dev_name, sizeof(device->dev_name) - 1); 37730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood device->fd = fd; 37830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood device->desc_length = length; 379cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood // assume we are writeable, since usb_device_get_fd will only return writeable fds 380cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood device->writeable = 1; 38130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood return device; 382cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood 383cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwoodfailed: 38430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood close(fd); 38530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood free(device); 38630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood return NULL; 38730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 38830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 389cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwoodstatic int usb_device_reopen_writeable(struct usb_device *device) 39030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 391cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood if (device->writeable) 392cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood return 1; 393cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood 394cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood int fd = open(device->dev_name, O_RDWR); 395cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood if (fd >= 0) { 396cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood close(device->fd); 397cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood device->fd = fd; 398cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood device->writeable = 1; 399cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood return 1; 400cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood } 401cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood D("usb_device_reopen_writeable failed errno %d\n", errno); 402cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood return 0; 403cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood} 404cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood 405cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwoodint usb_device_get_fd(struct usb_device *device) 406cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood{ 407cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood if (!usb_device_reopen_writeable(device)) 408cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood return -1; 409cd185f23cc6f062b252d1d19bca55e721290ee62Mike Lockwood return device->fd; 41030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 41130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 41230ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwoodconst char* usb_device_get_name(struct usb_device *device) 41330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 41430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood return device->dev_name; 41530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 41630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 417203f102028c4df33c191d8cd610775eba8d5366dMike Lockwoodint usb_device_get_unique_id(struct usb_device *device) 418203f102028c4df33c191d8cd610775eba8d5366dMike Lockwood{ 419203f102028c4df33c191d8cd610775eba8d5366dMike Lockwood int bus = 0, dev = 0; 420203f102028c4df33c191d8cd610775eba8d5366dMike Lockwood sscanf(device->dev_name, USB_FS_ID_SCANNER, &bus, &dev); 421203f102028c4df33c191d8cd610775eba8d5366dMike Lockwood return bus * 1000 + dev; 422203f102028c4df33c191d8cd610775eba8d5366dMike Lockwood} 423203f102028c4df33c191d8cd610775eba8d5366dMike Lockwood 42407eb4af174f31c449a86dd459e2c81702f74acf1Mike Lockwoodint usb_device_get_unique_id_from_name(const char* name) 42507eb4af174f31c449a86dd459e2c81702f74acf1Mike Lockwood{ 42607eb4af174f31c449a86dd459e2c81702f74acf1Mike Lockwood int bus = 0, dev = 0; 42707eb4af174f31c449a86dd459e2c81702f74acf1Mike Lockwood sscanf(name, USB_FS_ID_SCANNER, &bus, &dev); 42807eb4af174f31c449a86dd459e2c81702f74acf1Mike Lockwood return bus * 1000 + dev; 42907eb4af174f31c449a86dd459e2c81702f74acf1Mike Lockwood} 43007eb4af174f31c449a86dd459e2c81702f74acf1Mike Lockwood 4317d700f8bdce747a26e3ee4737683194d77286ba3Mike Lockwoodchar* usb_device_get_name_from_unique_id(int id) 4327d700f8bdce747a26e3ee4737683194d77286ba3Mike Lockwood{ 4337d700f8bdce747a26e3ee4737683194d77286ba3Mike Lockwood int bus = id / 1000; 4347d700f8bdce747a26e3ee4737683194d77286ba3Mike Lockwood int dev = id % 1000; 4357d700f8bdce747a26e3ee4737683194d77286ba3Mike Lockwood char* result = (char *)calloc(1, strlen(USB_FS_ID_FORMAT)); 4367d700f8bdce747a26e3ee4737683194d77286ba3Mike Lockwood snprintf(result, strlen(USB_FS_ID_FORMAT) - 1, USB_FS_ID_FORMAT, bus, dev); 4377d700f8bdce747a26e3ee4737683194d77286ba3Mike Lockwood return result; 4387d700f8bdce747a26e3ee4737683194d77286ba3Mike Lockwood} 4397d700f8bdce747a26e3ee4737683194d77286ba3Mike Lockwood 44030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwooduint16_t usb_device_get_vendor_id(struct usb_device *device) 44130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 44230ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood struct usb_device_descriptor* desc = (struct usb_device_descriptor*)device->desc; 44330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood return __le16_to_cpu(desc->idVendor); 44430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 44530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 44630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwooduint16_t usb_device_get_product_id(struct usb_device *device) 44730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 44830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood struct usb_device_descriptor* desc = (struct usb_device_descriptor*)device->desc; 44930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood return __le16_to_cpu(desc->idProduct); 45030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 45130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 45250372073d2919480fb32d56cb3b91663b9fca0f1Mike Lockwoodconst struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device) 45350372073d2919480fb32d56cb3b91663b9fca0f1Mike Lockwood{ 45450372073d2919480fb32d56cb3b91663b9fca0f1Mike Lockwood return (struct usb_device_descriptor*)device->desc; 45550372073d2919480fb32d56cb3b91663b9fca0f1Mike Lockwood} 45650372073d2919480fb32d56cb3b91663b9fca0f1Mike Lockwood 457dfd21b89f28bd8a6d4acdc05b74fbd03bb844f48Vitalii Tomkivchar* usb_device_get_string(struct usb_device *device, int id, int timeout) 4581b7d991b433cf6d6fae4f40cb37f9b6c6043cfbcMike Lockwood{ 4591b7d991b433cf6d6fae4f40cb37f9b6c6043cfbcMike Lockwood char string[256]; 4600dd1aab7b5184f41ec838def8493d16cfe70b739Mike Lockwood __u16 buffer[MAX_STRING_DESCRIPTOR_LENGTH / sizeof(__u16)]; 4610dd1aab7b5184f41ec838def8493d16cfe70b739Mike Lockwood __u16 languages[MAX_STRING_DESCRIPTOR_LENGTH / sizeof(__u16)]; 4621b7d991b433cf6d6fae4f40cb37f9b6c6043cfbcMike Lockwood int i, result; 4631b7d991b433cf6d6fae4f40cb37f9b6c6043cfbcMike Lockwood int languageCount = 0; 4641b7d991b433cf6d6fae4f40cb37f9b6c6043cfbcMike Lockwood 465d2e798b53039f35967705acd5f6c399742f97e72Mike Lockwood if (id == 0) return NULL; 466d2e798b53039f35967705acd5f6c399742f97e72Mike Lockwood 4671b7d991b433cf6d6fae4f40cb37f9b6c6043cfbcMike Lockwood string[0] = 0; 4681b7d991b433cf6d6fae4f40cb37f9b6c6043cfbcMike Lockwood memset(languages, 0, sizeof(languages)); 46930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 47030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood // read list of supported languages 471120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood result = usb_device_control_transfer(device, 4721b7d991b433cf6d6fae4f40cb37f9b6c6043cfbcMike Lockwood USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR, 473dfd21b89f28bd8a6d4acdc05b74fbd03bb844f48Vitalii Tomkiv (USB_DT_STRING << 8) | 0, 0, languages, sizeof(languages), 474dfd21b89f28bd8a6d4acdc05b74fbd03bb844f48Vitalii Tomkiv timeout); 47530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood if (result > 0) 47630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood languageCount = (result - 2) / 2; 47730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 47830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood for (i = 1; i <= languageCount; i++) { 47930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood memset(buffer, 0, sizeof(buffer)); 48030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 481120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood result = usb_device_control_transfer(device, 4821b7d991b433cf6d6fae4f40cb37f9b6c6043cfbcMike Lockwood USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR, 483dfd21b89f28bd8a6d4acdc05b74fbd03bb844f48Vitalii Tomkiv (USB_DT_STRING << 8) | id, languages[i], buffer, sizeof(buffer), 484dfd21b89f28bd8a6d4acdc05b74fbd03bb844f48Vitalii Tomkiv timeout); 48530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood if (result > 0) { 48630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood int i; 48730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood // skip first word, and copy the rest to the string, changing shorts to bytes. 48830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood result /= 2; 48930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood for (i = 1; i < result; i++) 49030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood string[i - 1] = buffer[i]; 49130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood string[i - 1] = 0; 49230ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood return strdup(string); 49330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood } 49430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood } 49530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 49630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood return NULL; 49730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 49830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 499dfd21b89f28bd8a6d4acdc05b74fbd03bb844f48Vitalii Tomkivchar* usb_device_get_manufacturer_name(struct usb_device *device, int timeout) 50030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 50130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc; 502dfd21b89f28bd8a6d4acdc05b74fbd03bb844f48Vitalii Tomkiv return usb_device_get_string(device, desc->iManufacturer, timeout); 50330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 50430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 505dfd21b89f28bd8a6d4acdc05b74fbd03bb844f48Vitalii Tomkivchar* usb_device_get_product_name(struct usb_device *device, int timeout) 50630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 50730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc; 508dfd21b89f28bd8a6d4acdc05b74fbd03bb844f48Vitalii Tomkiv return usb_device_get_string(device, desc->iProduct, timeout); 50930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 51030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 511f68600abfc7f1e2d64a4737630feea43bdaa85dcMike Lockwoodint usb_device_get_version(struct usb_device *device) 512f68600abfc7f1e2d64a4737630feea43bdaa85dcMike Lockwood{ 513f68600abfc7f1e2d64a4737630feea43bdaa85dcMike Lockwood struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc; 514f68600abfc7f1e2d64a4737630feea43bdaa85dcMike Lockwood return desc->bcdUSB; 515f68600abfc7f1e2d64a4737630feea43bdaa85dcMike Lockwood} 516f68600abfc7f1e2d64a4737630feea43bdaa85dcMike Lockwood 517dfd21b89f28bd8a6d4acdc05b74fbd03bb844f48Vitalii Tomkivchar* usb_device_get_serial(struct usb_device *device, int timeout) 51830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 51930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc; 520dfd21b89f28bd8a6d4acdc05b74fbd03bb844f48Vitalii Tomkiv return usb_device_get_string(device, desc->iSerialNumber, timeout); 52130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 52230ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 52330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwoodint usb_device_is_writeable(struct usb_device *device) 52430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 52530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood return device->writeable; 52630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 52730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 52830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwoodvoid usb_descriptor_iter_init(struct usb_device *device, struct usb_descriptor_iter *iter) 52930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 53030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood iter->config = device->desc; 53130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood iter->config_end = device->desc + device->desc_length; 53230ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood iter->curr_desc = device->desc; 53330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 53430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 53530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwoodstruct usb_descriptor_header *usb_descriptor_iter_next(struct usb_descriptor_iter *iter) 53630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 53730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood struct usb_descriptor_header* next; 53830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood if (iter->curr_desc >= iter->config_end) 53930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood return NULL; 54030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood next = (struct usb_descriptor_header*)iter->curr_desc; 54130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood iter->curr_desc += next->bLength; 54230ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood return next; 54330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 54430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 54530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwoodint usb_device_claim_interface(struct usb_device *device, unsigned int interface) 54630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 54730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood return ioctl(device->fd, USBDEVFS_CLAIMINTERFACE, &interface); 54830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 54930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 55030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwoodint usb_device_release_interface(struct usb_device *device, unsigned int interface) 55130ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 55230ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood return ioctl(device->fd, USBDEVFS_RELEASEINTERFACE, &interface); 55330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 55430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 555ec9e7b1c1844e66c39a4f63ded8208c1717380f8Mike Lockwoodint usb_device_connect_kernel_driver(struct usb_device *device, 556ec9e7b1c1844e66c39a4f63ded8208c1717380f8Mike Lockwood unsigned int interface, int connect) 557ec9e7b1c1844e66c39a4f63ded8208c1717380f8Mike Lockwood{ 558ec9e7b1c1844e66c39a4f63ded8208c1717380f8Mike Lockwood struct usbdevfs_ioctl ctl; 559ec9e7b1c1844e66c39a4f63ded8208c1717380f8Mike Lockwood 560ec9e7b1c1844e66c39a4f63ded8208c1717380f8Mike Lockwood ctl.ifno = interface; 561ec9e7b1c1844e66c39a4f63ded8208c1717380f8Mike Lockwood ctl.ioctl_code = (connect ? USBDEVFS_CONNECT : USBDEVFS_DISCONNECT); 562ec9e7b1c1844e66c39a4f63ded8208c1717380f8Mike Lockwood ctl.data = NULL; 563ec9e7b1c1844e66c39a4f63ded8208c1717380f8Mike Lockwood return ioctl(device->fd, USBDEVFS_IOCTL, &ctl); 564ec9e7b1c1844e66c39a4f63ded8208c1717380f8Mike Lockwood} 565ec9e7b1c1844e66c39a4f63ded8208c1717380f8Mike Lockwood 566d2e798b53039f35967705acd5f6c399742f97e72Mike Lockwoodint usb_device_set_configuration(struct usb_device *device, int configuration) 567d2e798b53039f35967705acd5f6c399742f97e72Mike Lockwood{ 568d2e798b53039f35967705acd5f6c399742f97e72Mike Lockwood return ioctl(device->fd, USBDEVFS_SETCONFIGURATION, &configuration); 569d2e798b53039f35967705acd5f6c399742f97e72Mike Lockwood} 570d2e798b53039f35967705acd5f6c399742f97e72Mike Lockwood 571d2e798b53039f35967705acd5f6c399742f97e72Mike Lockwoodint usb_device_set_interface(struct usb_device *device, unsigned int interface, 572d2e798b53039f35967705acd5f6c399742f97e72Mike Lockwood unsigned int alt_setting) 573d2e798b53039f35967705acd5f6c399742f97e72Mike Lockwood{ 574d2e798b53039f35967705acd5f6c399742f97e72Mike Lockwood struct usbdevfs_setinterface ctl; 575d2e798b53039f35967705acd5f6c399742f97e72Mike Lockwood 576d2e798b53039f35967705acd5f6c399742f97e72Mike Lockwood ctl.interface = interface; 577d2e798b53039f35967705acd5f6c399742f97e72Mike Lockwood ctl.altsetting = alt_setting; 578d2e798b53039f35967705acd5f6c399742f97e72Mike Lockwood return ioctl(device->fd, USBDEVFS_SETINTERFACE, &ctl); 579d2e798b53039f35967705acd5f6c399742f97e72Mike Lockwood} 580d2e798b53039f35967705acd5f6c399742f97e72Mike Lockwood 581120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwoodint usb_device_control_transfer(struct usb_device *device, 582120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood int requestType, 583120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood int request, 584120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood int value, 585120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood int index, 586120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood void* buffer, 587120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood int length, 588120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood unsigned int timeout) 589120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood{ 590120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood struct usbdevfs_ctrltransfer ctrl; 591120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood 592120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood // this usually requires read/write permission 593120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood if (!usb_device_reopen_writeable(device)) 594120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood return -1; 595120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood 596120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood memset(&ctrl, 0, sizeof(ctrl)); 597120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood ctrl.bRequestType = requestType; 598120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood ctrl.bRequest = request; 599120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood ctrl.wValue = value; 600120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood ctrl.wIndex = index; 601120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood ctrl.wLength = length; 602120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood ctrl.data = buffer; 603120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood ctrl.timeout = timeout; 604120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood return ioctl(device->fd, USBDEVFS_CONTROL, &ctrl); 605120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood} 606120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood 607120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwoodint usb_device_bulk_transfer(struct usb_device *device, 608120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood int endpoint, 609120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood void* buffer, 6109879bb2ba28891cb2a4231c39ea2d5d2da730be7Philip P. Moltmann unsigned int length, 611120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood unsigned int timeout) 612120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood{ 613120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood struct usbdevfs_bulktransfer ctrl; 614120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood 615c4c00d8e6a215020be87f6702844267e105e1f0eMike Lockwood // need to limit request size to avoid EINVAL 616c4c00d8e6a215020be87f6702844267e105e1f0eMike Lockwood if (length > MAX_USBFS_BUFFER_SIZE) 617c4c00d8e6a215020be87f6702844267e105e1f0eMike Lockwood length = MAX_USBFS_BUFFER_SIZE; 618c4c00d8e6a215020be87f6702844267e105e1f0eMike Lockwood 619120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood memset(&ctrl, 0, sizeof(ctrl)); 620120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood ctrl.ep = endpoint; 621120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood ctrl.len = length; 622120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood ctrl.data = buffer; 623120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood ctrl.timeout = timeout; 624120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood return ioctl(device->fd, USBDEVFS_BULK, &ctrl); 625120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood} 626120b57a3d9703bccba534af335aa94dd3a41be2fMike Lockwood 627f6411fa1ecf3a87ce78800660752f57ba9b67836Keun-young Parkint usb_device_reset(struct usb_device *device) 628f6411fa1ecf3a87ce78800660752f57ba9b67836Keun-young Park{ 629f6411fa1ecf3a87ce78800660752f57ba9b67836Keun-young Park return ioctl(device->fd, USBDEVFS_RESET); 630f6411fa1ecf3a87ce78800660752f57ba9b67836Keun-young Park} 631f6411fa1ecf3a87ce78800660752f57ba9b67836Keun-young Park 632e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwoodstruct usb_request *usb_request_new(struct usb_device *dev, 633e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood const struct usb_endpoint_descriptor *ep_desc) 63430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 635e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood struct usbdevfs_urb *urb = calloc(1, sizeof(struct usbdevfs_urb)); 636e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood if (!urb) 637e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood return NULL; 638e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood 639e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood if ((ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) 640e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood urb->type = USBDEVFS_URB_TYPE_BULK; 641e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood else if ((ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) 642e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood urb->type = USBDEVFS_URB_TYPE_INTERRUPT; 643e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood else { 644e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood D("Unsupported endpoint type %d", ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); 645e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood free(urb); 646e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood return NULL; 647e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood } 648e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood urb->endpoint = ep_desc->bEndpointAddress; 649e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood 650e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood struct usb_request *req = calloc(1, sizeof(struct usb_request)); 651e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood if (!req) { 652e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood free(urb); 653e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood return NULL; 654e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood } 655e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood 656e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood req->dev = dev; 657e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood req->max_packet_size = __le16_to_cpu(ep_desc->wMaxPacketSize); 658e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood req->private_data = urb; 659b5d68a3c28d0b7a947373344cbda2a2369d0b67dMike Lockwood req->endpoint = urb->endpoint; 660e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood urb->usercontext = req; 661e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood 662e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood return req; 66330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 66430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 665e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwoodvoid usb_request_free(struct usb_request *req) 66630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 667e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood free(req->private_data); 668e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood free(req); 66930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 67030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 671e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwoodint usb_request_queue(struct usb_request *req) 67230ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 673e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood struct usbdevfs_urb *urb = (struct usbdevfs_urb*)req->private_data; 67430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood int res; 67530ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 67630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood urb->status = -1; 677e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood urb->buffer = req->buffer; 678c4c00d8e6a215020be87f6702844267e105e1f0eMike Lockwood // need to limit request size to avoid EINVAL 679c4c00d8e6a215020be87f6702844267e105e1f0eMike Lockwood if (req->buffer_length > MAX_USBFS_BUFFER_SIZE) 680c4c00d8e6a215020be87f6702844267e105e1f0eMike Lockwood urb->buffer_length = MAX_USBFS_BUFFER_SIZE; 681c4c00d8e6a215020be87f6702844267e105e1f0eMike Lockwood else 682c4c00d8e6a215020be87f6702844267e105e1f0eMike Lockwood urb->buffer_length = req->buffer_length; 68330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 68430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood do { 685e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood res = ioctl(req->dev->fd, USBDEVFS_SUBMITURB, urb); 68630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood } while((res < 0) && (errno == EINTR)); 68730ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 68830ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood return res; 68930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 69030ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 6913695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmannstruct usb_request *usb_request_wait(struct usb_device *dev, int timeoutMillis) 69230ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 6933695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann // Poll until a request becomes available if there is a timeout 6943695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann if (timeoutMillis > 0) { 6953695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann struct pollfd p = {.fd = dev->fd, .events = POLLOUT, .revents = 0}; 6963695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann 6973695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann int res = poll(&p, 1, timeoutMillis); 6983695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann 6993695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann if (res != 1 || p.revents != POLLOUT) { 7003695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann D("[ poll - event %d, error %d]\n", p.revents, errno); 701e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood return NULL; 70230ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood } 70330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood } 7043695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann 7053695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann // Read the request. This should usually succeed as we polled before, but it can fail e.g. when 7063695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann // two threads are reading usb requests at the same time and only a single request is available. 7073695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann struct usbdevfs_urb *urb = NULL; 7083695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann int res = TEMP_FAILURE_RETRY(ioctl(dev->fd, timeoutMillis == -1 ? USBDEVFS_REAPURB : 7093695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann USBDEVFS_REAPURBNDELAY, &urb)); 7103695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann D("%s returned %d\n", timeoutMillis == -1 ? "USBDEVFS_REAPURB" : "USBDEVFS_REAPURBNDELAY", res); 7113695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann 7123695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann if (res < 0) { 7133695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann D("[ reap urb - error %d]\n", errno); 7143695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann return NULL; 7153695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann } else { 7163695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann D("[ urb @%p status = %d, actual = %d ]\n", urb, urb->status, urb->actual_length); 7173695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann 7183695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann struct usb_request *req = (struct usb_request*)urb->usercontext; 7193695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann req->actual_length = urb->actual_length; 7203695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann 7213695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann return req; 7223695285d5d743190ed88f982b0c531066e68b686Philip P. Moltmann } 72330ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 72430ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood 725e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwoodint usb_request_cancel(struct usb_request *req) 72630ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood{ 727e533c5f1005e8913f577d2aeac5a26f88f55a249Mike Lockwood struct usbdevfs_urb *urb = ((struct usbdevfs_urb*)req->private_data); 728ef4087bf2b20dfa44c7e33c1544d29cfdcd656e7Badhri Jagan Sridharan return ioctl(req->dev->fd, USBDEVFS_DISCARDURB, urb); 72930ff2c70ce05d761e8cb0ab7ee02b39a681fe0cbMike Lockwood} 730