文章目录
libusb 简介
libusb 是一个 C 库,提供对 USB 设备的通用访问。它旨在帮助开发人员开发与 USB 硬件通信的应用程序。
它有以下三个特点:
- 它是可移植的:使用单个跨平台 API,它可以访问 Linux、macOS、Windows 等上的 USB 设备。
- 它是用户模式:应用程序与设备通信不需要特殊权限或提升。
- 它与版本无关:支持所有版本的 USB 协议,从 1.0 到 3.1(最新)。
libusb的优点:
- 支持主流平台:Linux, OS X, Windows, OpenBSD/NetBSD, Solaris and Haiku
- 支持所有USB版本, from 1.0 to 3.1
- Unified modern API, that provides both synchronous and asynchronous access
- User-mode: no need for kernel access
libusb 编译
直接从libusb的官网下载或者从git下载:https://github.com/libusb/libusb.git
$ git clone git://github.com/libusb/libusb.git
$ cd libusb
#安装依赖
$ sudo apt install autoconf automake libtool libudev-dev m4
#配置编译源码
$ ./bootstrap.sh
$ ./autogen.sh
$ make
编译之后的产物在目录libusb/.libs
下面
libusb 源码
目录介绍
- Xcode:Apple 平台相关文件。
- android:顾名思义,用于生成 Android 版本的 libusb 库及测试用例。
- doc:用于生成 API 文档。在doc/目录下执行:doxygen doxygen.cfg 或者 make docs。
- examples:libusb 的使用示例。后续代码分析即以各个示例作为实例。
- libusb:libusb 核心代码
- libusb/os:平台相关的代码支持
- msvc:微软编译环境相关文件。
- tests:libusb 的测试。
核心代码文件
core.c
:libusb核心功能实现
descriptor.c
:libusb中处理usb描述符功能实现
hotplugin.c
:libusb中处理hotplug相关功能
io.c
:libusb中I/O相关功能
libusb.h
:该文件是libusb对外提供的头文件,里面声明了libusb对外的接口,定义了libusb接口使用的数据结构。
libusbi.h
:libusb库内部头文件
strerror.c
:libusb内部错误定义
sync.c
:libusb中处理同步I/O相关功能
version_nano.h
、version.h
:libusb版本文件
平台支持
这部分文件较多,根据编译时的配置,可以编译不同的支持平台。
以linux为例,在默认配置下编译,会生成中间文件:
这说明linux平台后端支持为:
events_posix.h
/events_posix.c
threads_posix.h
/threads_posix.c
linux_udev.c
linux_usbfs.h
/linux_usbfs.cpp
这里因为系统中有libudev库,所以使用了libudev,如果也可以不使用libudev,此时就是使用netlink即linux_netlink.c
例子
在examples
目录下面,libusb提供了一些官方案例:
- dpfp.c
一款指纹识别器的应用程序,将采集到的指纹图像保存为文件。用到了control、interrupt、bulk三种传输方式。 - ezusb.h/exusb.c
“EZ-USB的固件下载程序,可实现下载固件(image)到Cypress EZ-USB microcontrollers,ezusb系列芯片使用端点0和厂商特定命令将数据写到片上SRAM,并且也支持写数据到CPUCS register或者eeprom。程序使用控制传输方式进行指令和数据的传输,libusb_control_transfer()的形参bmRequestType使用LIBUSB_REQUEST_TYPE_VENDOR(厂商自定义请求)。程序支持五种下载类型(Target type): an21, fx, fx2, fx2lp, fx3,支持四种固件(image)类型:“Intel HEX”, “Cypress 8051 IIC”, “Cypress 8051 BIX”, “Cypress IMG format”。” - fxload.c
./fxload
no firmware specified!
Usage: fxload [-v] [-V] [-t type] [-d vid:pid] [-p bus,addr] [-s loader] -i firmware
-i <path> -- Firmware to upload
-s <path> -- Second stage loader
-t <type> -- Target type: an21, fx, fx2, fx2lp, fx3
-d <vid:pid> -- Target device, as an USB VID:PID
-p <bus,addr> -- Target device, as a libusb bus number and device address path
-v -- Increase verbosity
-q -- Decrease verbosity (silent mode)
-V -- Print program version
- hotplugtest.c
hotplugtest 用于监听系统中 USB 设备的 attached(插入)和 detached(拔出)。
./hotplugtest
Device attached: 058f:6387
No access to device: Access denied (insufficient permissions)
Device detached: 058f:6387
- listdevs.c
获取并显示系统当前的 USB 设备信息,包含:VID、PID、bus 编号、设备地址、端口号。
./listdevs
2109:0817 (bus 2, device 2) path: 6
1d6b:0003 (bus 2, device 1)
2f4c:2000 (bus 1, device 7) path: 8
3434:03a1 (bus 1, device 6) path: 4.3
046d:c52b (bus 1, device 5) path: 4.2
2109:2817 (bus 1, device 3) path: 4
046d:0825 (bus 1, device 2) path: 2
8087:0026 (bus 1, device 4) path: 14
058f:6387 (bus 1, device 11) path: 12.1
1a40:0101 (bus 1, device 8) path: 12
1d6b:0002 (bus 1, device 1)
- sam3u_benchmark.c
Atmel SAM3U isochronous(等时传输)性能测试。程序不断接收来自SAM3U iso端点的数据,当按下CTRL-C时,计算花费时间和传输的总数据量。 - testlibusb.c
打印usb设备列表的详细信息:包括设备描述符、配置、接口、端点描述符
/testlibusb
Dev (bus 2, device 2): 2109 - 0817 speed: 5G
Dev (bus 2, device 1): 1D6B - 0003 speed: 10G
Dev (bus 1, device 7): 2F4C - 2000 speed: 12M
Dev (bus 1, device 6): 3434 - 03A1 speed: 12M
Dev (bus 1, device 5): 046D - C52B speed: 12M
Dev (bus 1, device 3): 2109 - 2817 speed: 480M
Dev (bus 1, device 2): 046D - 0825 speed: 480M
Dev (bus 1, device 4): 8087 - 0026 speed: 12M
Dev (bus 1, device 11): 058F - 6387 speed: 480M
Dev (bus 1, device 8): 1A40 - 0101 speed: 480M
Dev (bus 1, device 1): 1D6B - 0002 speed: 480M
- xusb.c
一个综合的USB测试程序,包括:HID设备(xbox、PS3和Joystick)、Mass Storage,涉及control、interrupt、bulk传输。
其中Mass Storage可以使用普通的U盘进行测试,只需修改VID和PID即可,可以实现的功能有:读取描述符、查询U盘信息、读取U盘容量、读取U盘数据(因为没有使用文件系统,读取出来的数据是原始二进制数据)。关于Mass Storage中涉及的SCSI命令,参考: USB Mass Stroage - SCSI指令格式详解。
./xusb
usage: ./xusb [-h] [-d] [-i] [-k] [-b file] [-l lang] [-j] [-x] [-s] [-p] [-w] [vid:pid]
-h : display usage
-d : enable debug output
-i : print topology and speed info
-j : test composite FTDI based JTAG device
-k : test Mass Storage device
-b file : dump Mass Storage data to file 'file'
-p : test Sony PS3 SixAxis controller
-s : test Microsoft Sidewinder Precision Pro (HID)
-x : test Microsoft XBox Controller Type S
-l lang : language to report errors in (ISO 639-1)
-w : force the use of device requests when querying WCID descriptors
If only the vid:pid is provided, xusb attempts to run the most appropriate test
API使用
API文档直接参考官方在线文档:https://libusb.sourceforge.io/api-1.0/,也可以直接看提供的头文件libusb.h
。
libusb初始化和去初始化
- 初始化
int LIBUSB_CALL libusb_init(libusb_context **ctx);
// libusb_init属于旧接口,官方推荐下面的libusb_init_context接口
int LIBUSB_CALL libusb_init_context(libusb_context **ctx, const struct libusb_init_option options[], int num_options);
初始化 libusb 内部资源,所以在使用 libusb 其他函数之前,必需优先调用该函数。
- 去初始化
void LIBUSB_CALL libusb_exit(libusb_context *ctx);
去初始化libusb,应该在程序退出前关闭所有已经打开的设备后调用该接口来去初始化libusb。
- 设置日志
void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level);
/* may be deprecated in the future in favor of lubusb_init_context()+libusb_set_option() */
void LIBUSB_CALL libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, int mode);
int LIBUSB_CALLV libusb_set_option(libusb_context *ctx, enum libusb_option option, ...);
libusb设备处理和枚举
ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx,
libusb_device ***list);
void LIBUSB_CALL libusb_free_device_list(libusb_device **list,
int unref_devices);
libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev);
void LIBUSB_CALL libusb_unref_device(libusb_device *dev);
int LIBUSB_CALL libusb_get_configuration(libusb_device_handle *dev,
int *config);
uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev);
uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev);
int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_t *port_numbers, int port_numbers_len);
LIBUSB_DEPRECATED_FOR(libusb_get_port_numbers)
int LIBUSB_CALL libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t *path, uint8_t path_length);
libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev);
uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device *dev);
int LIBUSB_CALL libusb_get_device_speed(libusb_device *dev);
int LIBUSB_CALL libusb_get_max_packet_size(libusb_device *dev,
unsigned char endpoint);
int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev,
unsigned char endpoint);
int LIBUSB_CALL libusb_get_max_alt_packet_size(libusb_device *dev,
int interface_number, int alternate_setting, unsigned char endpoint);
int LIBUSB_CALL libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, libusb_device_handle **dev_handle);
int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **dev_handle);
void LIBUSB_CALL libusb_close(libusb_device_handle *dev_handle);
libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle);
int LIBUSB_CALL libusb_set_configuration(libusb_device_handle *dev_handle,
int configuration);
int LIBUSB_CALL libusb_claim_interface(libusb_device_handle *dev_handle,
int interface_number);
int LIBUSB_CALL libusb_release_interface(libusb_device_handle *dev_handle,
int interface_number);
libusb_device_handle * LIBUSB_CALL libusb_open_device_with_vid_pid(
libusb_context *ctx, uint16_t vendor_id, uint16_t product_id);
int LIBUSB_CALL libusb_set_interface_alt_setting(libusb_device_handle *dev_handle,
int interface_number, int alternate_setting);
int LIBUSB_CALL libusb_clear_halt(libusb_device_handle *dev_handle,
unsigned char endpoint);
int LIBUSB_CALL libusb_reset_device(libusb_device_handle *dev_handle);
int LIBUSB_CALL libusb_kernel_driver_active(libusb_device_handle *dev_handle,
int interface_number);
int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle *dev_handle,
int interface_number);
int LIBUSB_CALL libusb_attach_kernel_driver(libusb_device_handle *dev_handle,
int interface_number);
int LIBUSB_CALL libusb_set_auto_detach_kernel_driver(
libusb_device_handle *dev_handle, int enable);
libusb 杂项
const struct libusb_version * LIBUSB_CALL libusb_get_version(void);
int LIBUSB_CALL libusb_has_capability(uint32_t capability);
const char * LIBUSB_CALL libusb_error_name(int error_code);
int LIBUSB_CALL libusb_setlocale(const char *locale);
const char * LIBUSB_CALL libusb_strerror(int errcode);
libusb USB描述符
int LIBUSB_CALL libusb_get_device_descriptor(libusb_device *dev,
struct libusb_device_descriptor *desc);
int LIBUSB_CALL libusb_get_active_config_descriptor(libusb_device *dev,
struct libusb_config_descriptor **config);
int LIBUSB_CALL libusb_get_config_descriptor(libusb_device *dev,
uint8_t config_index, struct libusb_config_descriptor **config);
int LIBUSB_CALL libusb_get_config_descriptor_by_value(libusb_device *dev,
uint8_t bConfigurationValue, struct libusb_config_descriptor **config);
void LIBUSB_CALL libusb_free_config_descriptor(
struct libusb_config_descriptor *config);
int LIBUSB_CALL libusb_get_ss_endpoint_companion_descriptor(
libusb_context *ctx,
const struct libusb_endpoint_descriptor *endpoint,
struct libusb_ss_endpoint_companion_descriptor **ep_comp);
void LIBUSB_CALL libusb_free_ss_endpoint_companion_descriptor(
struct libusb_ss_endpoint_companion_descriptor *ep_comp);
int LIBUSB_CALL libusb_get_bos_descriptor(libusb_device_handle *dev_handle,
struct libusb_bos_descriptor **bos);
void LIBUSB_CALL libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos);
int LIBUSB_CALL libusb_get_usb_2_0_extension_descriptor(
libusb_context *ctx,
struct libusb_bos_dev_capability_descriptor *dev_cap,
struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension);
void LIBUSB_CALL libusb_free_usb_2_0_extension_descriptor(
struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension);
int LIBUSB_CALL libusb_get_ss_usb_device_capability_descriptor(
libusb_context *ctx,
struct libusb_bos_dev_capability_descriptor *dev_cap,
struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap);
void LIBUSB_CALL libusb_free_ss_usb_device_capability_descriptor(
struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap);
int LIBUSB_CALL libusb_get_ssplus_usb_device_capability_descriptor(
libusb_context *ctx,
struct libusb_bos_dev_capability_descriptor *dev_cap,
struct libusb_ssplus_usb_device_capability_descriptor **ssplus_usb_device_cap);
void LIBUSB_CALL libusb_free_ssplus_usb_device_capability_descriptor(
struct libusb_ssplus_usb_device_capability_descriptor *ssplus_usb_device_cap);
int LIBUSB_CALL libusb_get_container_id_descriptor(libusb_context *ctx,
struct libusb_bos_dev_capability_descriptor *dev_cap,
struct libusb_container_id_descriptor **container_id);
void LIBUSB_CALL libusb_free_container_id_descriptor(
struct libusb_container_id_descriptor *container_id);
int LIBUSB_CALL libusb_get_platform_descriptor(libusb_context *ctx,
struct libusb_bos_dev_capability_descriptor *dev_cap,
struct libusb_platform_descriptor **platform_descriptor);
void LIBUSB_CALL libusb_free_platform_descriptor(
struct libusb_platform_descriptor *platform_descriptor);
int LIBUSB_CALL libusb_get_interface_association_descriptors(libusb_device *dev,
uint8_t config_index, struct libusb_interface_association_descriptor_array **iad_array);
int LIBUSB_CALL libusb_get_active_interface_association_descriptors(libusb_device *dev,
struct libusb_interface_association_descriptor_array **iad_array);
void LIBUSB_CALL libusb_free_interface_association_descriptors(
struct libusb_interface_association_descriptor_array *iad_array);
static inline int libusb_get_descriptor(libusb_device_handle *dev_handle,
uint8_t desc_type, uint8_t desc_index, unsigned char *data, int length);
static inline int libusb_get_string_descriptor(libusb_device_handle *dev_handle,
uint8_t desc_index, uint16_t langid, unsigned char *data, int length);
int LIBUSB_CALL libusb_get_string_descriptor_ascii(libusb_device_handle *dev_handle,
uint8_t desc_index, unsigned char *data, int length);
libusb 设备热插拔事件通知
int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx,
int events, int flags,
int vendor_id, int product_id, int dev_class,
libusb_hotplug_callback_fn cb_fn, void *user_data,
libusb_hotplug_callback_handle *callback_handle);
void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context *ctx,
libusb_hotplug_callback_handle callback_handle);
void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx,
libusb_hotplug_callback_handle callback_handle);
libusb 异步设备I/O
int LIBUSB_CALL libusb_alloc_streams(libusb_device_handle *dev_handle,
uint32_t num_streams, unsigned char *endpoints, int num_endpoints);
int LIBUSB_CALL libusb_free_streams(libusb_device_handle *dev_handle,
unsigned char *endpoints, int num_endpoints);
unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handle,
size_t length);
int LIBUSB_CALL libusb_dev_mem_free(libusb_device_handle *dev_handle,
unsigned char *buffer, size_t length);
static inline unsigned char *libusb_control_transfer_get_data(
struct libusb_transfer *transfer);
static inline struct libusb_control_setup *libusb_control_transfer_get_setup(
struct libusb_transfer *transfer);
static inline void libusb_fill_control_setup(unsigned char *buffer,
uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
uint16_t wLength);
struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(int iso_packets);
int LIBUSB_CALL libusb_submit_transfer(struct libusb_transfer *transfer);
int LIBUSB_CALL libusb_cancel_transfer(struct libusb_transfer *transfer);
void LIBUSB_CALL libusb_free_transfer(struct libusb_transfer *transfer);
void LIBUSB_CALL libusb_transfer_set_stream_id(
struct libusb_transfer *transfer, uint32_t stream_id);
uint32_t LIBUSB_CALL libusb_transfer_get_stream_id(
struct libusb_transfer *transfer);
static inline void libusb_fill_control_transfer(
struct libusb_transfer *transfer, libusb_device_handle *dev_handle,
unsigned char *buffer, libusb_transfer_cb_fn callback, void *user_data,
unsigned int timeout);
static inline void libusb_fill_bulk_transfer(struct libusb_transfer *transfer,
libusb_device_handle *dev_handle, unsigned char endpoint,
unsigned char *buffer, int length, libusb_transfer_cb_fn callback,
void *user_data, unsigned int timeout);
static inline void libusb_fill_bulk_stream_transfer(
struct libusb_transfer *transfer, libusb_device_handle *dev_handle,
unsigned char endpoint, uint32_t stream_id,
unsigned char *buffer, int length, libusb_transfer_cb_fn callback,
void *user_data, unsigned int timeout);
static inline void libusb_fill_interrupt_transfer(
struct libusb_transfer *transfer, libusb_device_handle *dev_handle,
unsigned char endpoint, unsigned char *buffer, int length,
libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout);
static inline void libusb_fill_iso_transfer(struct libusb_transfer *transfer,
libusb_device_handle *dev_handle, unsigned char endpoint,
unsigned char *buffer, int length, int num_iso_packets,
libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout);
static inline void libusb_set_iso_packet_lengths(
struct libusb_transfer *transfer, unsigned int length);
static inline unsigned char *libusb_get_iso_packet_buffer(
struct libusb_transfer *transfer, unsigned int packet);
static inline unsigned char *libusb_get_iso_packet_buffer_simple(
struct libusb_transfer *transfer, unsigned int packet);
libusb 同步设备I/O
int LIBUSB_CALL libusb_control_transfer(libusb_device_handle *dev_handle,
uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex,
unsigned char *data, uint16_t wLength, unsigned int timeout);
int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle *dev_handle,
unsigned char endpoint, unsigned char *data, int length,
int *transferred, unsigned int timeout);
int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle,
unsigned char endpoint, unsigned char *data, int length,
int *transferred, unsigned int timeout);
libusb 轮询与定时
int LIBUSB_CALL libusb_try_lock_events(libusb_context *ctx);
void LIBUSB_CALL libusb_lock_events(libusb_context *ctx);
void LIBUSB_CALL libusb_unlock_events(libusb_context *ctx);
int LIBUSB_CALL libusb_event_handling_ok(libusb_context *ctx);
int LIBUSB_CALL libusb_event_handler_active(libusb_context *ctx);
void LIBUSB_CALL libusb_interrupt_event_handler(libusb_context *ctx);
void LIBUSB_CALL libusb_lock_event_waiters(libusb_context *ctx);
void LIBUSB_CALL libusb_unlock_event_waiters(libusb_context *ctx);
int LIBUSB_CALL libusb_wait_for_event(libusb_context *ctx, struct timeval *tv);
int LIBUSB_CALL libusb_handle_events_timeout(libusb_context *ctx,
struct timeval *tv);
int LIBUSB_CALL libusb_handle_events_timeout_completed(libusb_context *ctx,
struct timeval *tv, int *completed);
int LIBUSB_CALL libusb_handle_events(libusb_context *ctx);
int LIBUSB_CALL libusb_handle_events_completed(libusb_context *ctx, int *completed);
int LIBUSB_CALL libusb_handle_events_locked(libusb_context *ctx,
struct timeval *tv);
int LIBUSB_CALL libusb_pollfds_handle_timeouts(libusb_context *ctx);
int LIBUSB_CALL libusb_get_next_timeout(libusb_context *ctx,
struct timeval *tv);
const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds(
libusb_context *ctx);
void LIBUSB_CALL libusb_free_pollfds(const struct libusb_pollfd **pollfds);
void LIBUSB_CALL libusb_set_pollfd_notifiers(libusb_context *ctx,
libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb,
void *user_data);
libusb 涉及技术
针对 Linux 平台,涉及的技术有:
- sysfs:内核把一些 USB 设备的信息通过文件系统的方式呈现给了用户空间,应用通过读取相应文件即可获取所需信息。如遍历 /sys/bus/usb/devices/ 目录下的信息就可以获取各个设备的基本信息
- libudev:用于 USB 设备的热插拔
- netlink:用于 USB 设备的热插拔。netlink 是 Linux 提供的用于内核和用户空间之间的通信方式。一旦内核监测到系统设备有变化(如ADD/DEL/REMOVE),则通知用户进程。libudev 和 Netlink 二选一即可,可以认为 libudev 是对 Netlink 的封装
- 多线程
- 进程间通信:pipe/pipe2/eventfd。
- USB 规范