libusb学习笔记

ubuntu版本:ubuntu-gnome-16.04-desktop-amd64,gnome版
libusb版本 :2016-10-01: v1.0.21
作者:wang baoli
E-mail: baoliw@foxmail.com

libusb学习网站:

website:http://libusb.info/
API:http://libusb.sourceforge.net/api-1.0/
download:https://github.com/libusb/libusb
mailing list:http://mailing-list.libusb.info
libusb test demo:https://github.com/crazybaoli/libusb-test

1. 编译及安装

下载libusb源码,进入目录,shell下依次执行下列命令。

1.1 执行:./configure

提示:configure: error: “udev support requested but libudev not installed”
解决:

sudo apt-get install libudev-dev

1.2 执行:make

提示:‘aclocal-1.14’ is missing on your system.
解决:

  1. sudo apt-get install automake
  2. sudo apt-get install libtool
  3. sudo autoreconf -ivf
  4. make

1.3 安装:make install

执行:sudo make install
libusb-1.0.a 和 libusb-1.0.so 被安装到 /usr/local/lib/ 目录
libusb.h 被安装到 /usr/local/include/libusb-1.0/ 目录

注:在这以后可以直接执行:./configure && make && make install

2. 源码学习

libusb采用的linux技术:sysfs、libudev、netlink、pipe、thread、hotplug

2.1 目录分析

tests/

关于libusb的四个压力测试,不涉USB打开操作及具体的数据传输。

android/

用于生成Android版本的libusb库、test和examples。进入android/jni/,执行ndk_build即可。在android/README中有以下描述:

  1. Download the latest NDK from: http://developer.android.com/tools/sdk/ndk/index.html
  2. Extract the NDK.
  3. Open a shell and make sure there exist an NDK global variable, set to the directory where you extracted the NDK.
  4. Change directory to libusb’s “android/jni”
  5. Run “ndk-build”.
  6. The libusb library, examples and tests can then be found in:“android/libs/$ARCH”

doc/

用于生成软件接口文档。编译完工程后,打开doc/doxygen.cfg,将PROJECT_LOGO = libusb.png修改为PROJECT_LOGO = ,否则产生文档时会提示 libusb.png不存在,修改完成后在doc/目录下执行:doxygen doxygen.cfg即可生成html格式文档,或者执行make docs。
注:Ubuntu需要提前安装doxygen。

libusb/

libusb的核心代码。
1)os/目录是是平台相关的代码,支持:darwin、haiku、linux、windows、sunos、netbsd、openbsd等七种平台,即Linux, OS X, Windows, Windows CE, Android, OpenBSD/NetBSD, Haiku。
2)libusb-1.0.def DLL中导出函数的声明的一种方式:采用模块定义(.def) 文件声明,.def文件为链接器提供了有关被链接程序的导出、属性及其他方面的信息。
3)libusb-1.0.rc 用于windows,产生 .res文件。

msvc/

微软VC编译环境,目录下均是windows平台环境相关文件。

m4/

linux编译相关。m4 是一种宏处理器,它扫描用户输入的文本并将其输出,期间如果遇到宏就将其展开后输出。

Xcode/

apple平台相关文件。Xcode是苹果的集成开发环境(IDE),开发者可用其构建适用于苹果iPad、iPhone以及Mac设备的应用程序。在应用程序的创建、测试、优化以及提交至App Store的过程中,Xcode为开发者提供了用以管理整个开发工作流的工具。

examples/

libusb的测试demo,进入目录后执行make即可生成可执行文件进行测试。

1)getopt/

getopt现在已经是C函数库的一部分,没有编译使用,删除此目录不会有影响。

2)hotplugtest.c

热插拔测试demo

3)listdevs.c

获取系统当前的usb设备列表,并打印出VID、PID、bus和device编号

4)testlibusb.c

打印usb设备列表的详细信息:包括设备描述符、配置、接口、端点描述符

5)dpfp.c

一款指纹识别器的应用程序:URU4000B fingerprint scanner 应用程序,将采集到的指纹图像保存为文件。系统采用异步传输的方式,使用了control、interrupt、bulk三种传输方式。不仅使用了libusb_control_transfer等同步接口传输,也使用了libusb_submit_transfer的异步传输方式。

6)dpfp_threaded.c

与dpfp.c功能一致,代码也大部分相同,唯一不同在于dpfp.c将libusb_handle_events 放在 main loop中,而dpfp_threaded.c 将libusb_handle_events 放在一个线程当中。

7)sam3u_benchmark.c

Atmel SAM3U isochronous(等时传输)性能测试。程序不断接收来自SAM3U iso端点的数据,当按下CTRL-C时,计算花费时间和传输的总数据量。

8)xusb.c

一个综合的USB测试程序,包括:HID设备(xbox、PS3和Joystick)、Mass Storage,涉及中断、批量和控制传输。其中Mass Storage可以使用普通的U盘进行测试,只需修改VID和PID即可,可以实现的功能有:读取描述符、查询U盘信息、读取U盘容量、读取U盘数据(因为没有使用文件系统,读取出来的数据是原始二进制数据)。
关于Mass Storage中涉及的SCSI命令,参考: USB Mass Stroage - SCSI指令格式详解。

9)fxload.c和ezusb.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”。

10)other

ChangeLog:代码修改日志。2008-05-25: v0.9.0 release,目前最新版2016-10-01: v1.0.21
INSTALL:编译、安装方法。编译器选项,如: ./configure CC=c99 CFLAGS=-g LIBS=-lposix
PORTING:移植libusb到其他未支持平台的方法。

注:

  1. Atmel SAM3U:基于ARM Cortex M3内核的MCU,支持usb high speed。
  2. 关于ezusb的介绍:
    http://www.linux-usb.org/ezusb/
    http://www.cypress.com/
    EZ-USB FX是CYPRESS公司出品的一种带有USB功能的8051兼容系列,封装采用PQFP。这一系列芯片的最大不同之处在于使用不同的方式存储固件,EZ-USB FX可以在一个串行EEPROM中存储固件,也可以在主机上存储固件。当设备连接主机后,这些固件通过USB总线传输到芯片中。这样做最大的好处就是固件容易升级,不需要替换芯片或使用特殊的程序,只要在主机上更新固件即可。
    CY7C61083A是一款FX2LP芯片,支持full/high speed,应用:MP3、读卡器、照相机等等

2.2 权限问题

当open USB时需要提供root权限,这同打开串口一样,对底层硬件操作都需要root权限。

2.3 函数调用图

如果能绘制出函数调用关系图会更有利于分析代码。可以采用callfraph,但有些具有特殊返回值的函数不能被识别,并且不能跨文件寻找调用关系(可能是我没有正确的使用)。可以直接采用dot语言手动绘图。

2.4 log

2.4.1 修改log输出

libusb 日志默认输出到stderr,如果我们想输出libusb 的log至syslog,有以下两种方法:
方法1:./configure --enable-system-log
修改结果会反馈在./config.h中,增加了USE_SYSTEM_LOGGING_FACILITY宏
查看syslog:cat /var/log/syslog

Nov 22 09:51:07 ubuntu libusb-test: libusb: error [_get_usbfs_fd] libusb couldn’t open USB device /dev/bus/usb/002/018: Permission denied.

方法2:./configure CFLAGS=-DUSE_SYSTEM_LOGGING_FACILITY=1
或者:CFLAGS=-DUSE_SYSTEM_LOGGING_FACILITY=1 ./configure
修改结果反馈在./Makefile文件中,使CFLAGS = -DUSE_SYSTEM_LOGGING_FACILITY=1,这会覆盖原来的cflags 。
当然,也可在执行./configure后,直接修改Makefile中的cflags选项。
推荐采用方法1.
注:可以使用 ./configur --help 来获取–enable-system-log类似的选项

2.4.2 设置debug level

using libusb_set_debug() or the LIBUSB_DEBUG environment variable

除了可以使用libusb_set_debug()函数,也可以通过环境变量LIBUSB_DEBUG 来设置debug level。

2.5 open

USB设备文件对应路径为:“/dev/bus/usb/xxx/xxx”,使用了udev文件系统。
libusb通过打开设备文件:/dev/bus/usb/bus编号/device地址 来打开USB,stm32 USB device 对应/dev/bus/usb/002/020。可以使用lsusb来查看bus和device地址。
libusb优先使用udev文件系统打开usb设备,其次选择usbfs文件系统:/proc/bus/usb/来打开usb设备。Linux2.6采用了usbfs文件系统:/proc/bus/usb,在Ubuntu16.4上没有usbfs。
分析代码可知:

static const char *find_usbfs_path(void)
{const char *path = "/dev/bus/usb";const char *ret = NULL;if (check_usb_vfs(path)) {ret = path;} else {path = "/proc/bus/usb";if (check_usb_vfs(path))ret = path;}/* look for /dev/usbdev*.* if the normal places fail */if (ret == NULL) {struct dirent *entry;DIR *dir;path = "/dev";dir = opendir(path);if (dir != NULL) {while ((entry = readdir(dir)) != NULL) {if (_is_usbdev_entry(entry, NULL, NULL)) {/* found one; that's enough */ret = path;usbdev_names = 1;break;}}closedir(dir);}}#if defined(USE_UDEV)if (ret == NULL)ret = "/dev/bus/usb";
#endifif (ret != NULL)usbi_dbg("found usbfs at %s", ret);return ret;
}

stm32设备在linux sysfs文件系统的路径: /sys/bus/usb/devices/2-2.1,由内核向用户空间导出设备的数据结构及属性,可以修改和访问。libusb中有大量通过sysfs来获得usb设备属性的用法,如获取usb 速度:
speed = (DEVICE_CTX(dev), sysfs_dir, "speed");
我们也可以使用 cat /sys/bus/usb/devices/2-2.1/speed 来获取usb速度。
由于目录/sys/bus/usb/devices/经常被使用,在libusb源码中有以下宏定义:
#define SYSFS_DEVICE_PATH "/sys/bus/usb/devices"

2.6 结构体乱序初始化

linux结构体可以采用乱序初始化,即用成员变量前加(.)符号,如定义linux_usbfs_backend 结构体变量时就采用了这种方法:

const struct usbi_os_backend linux_usbfs_backend = {.name = "Linux usbfs",.caps = .init = op_init,.exit = op_exit,.get_device_list = NULL,.....
}

乱序初始化是C99标准新加的,比较直观的一种初始化方式。相比顺序初始化而言,乱序初始化就如其名,成员可以不按照顺序初始化,而且可以只初始化部分成员,扩展性较好。linux内核中采用这种方式初始化struct。

2.7 数据传输

libusb的数据传输通过向内核提交URB来实现:
ioctl(dpriv->fd, IOCTL_USBFS_SUBMITURB, urb);
而非使用read、write等读写函数。

2.8 hotplug

libusb的热插拔事件有两种:arrived 、left。
hotplug事件的底层支持机制:udev或者netlink,udev的热插拔机制是基于netlink实现。
libusb对于产生的热插拔消息在handle_events()中进行处理,然后调用用户注册的回调函数。
libusb内部专门开辟了一个线程来监听是否有USB设备插拔,并通过netlink或是udev两种方式来实现监听hotplug。优先采取udev方式,当系统不支持udev时,便采用netlink方式。其实udev的热插拔机制也是基于netlink实现。
实现原理:
当libusb在热插拔监听线程(linux_udev_event_thread_main or linux_netlink_event_thread_main)中接收到内核的hotplug消息,libusb首先将消息添加到ctx->hotplug_msgs中,然后在通过管道(ctx->event_pipe)将hotplug事件发送出去。在用户创建的monitor线程里,调用libusb_handle_events()进行事件处理,具体做法是调用poll监听管道,一旦ctx->event_pipe[0]可读,便读取hotplug_msgs,经过usbi_hotplug_match_cb()函数判断VID、PID以及device class符合后再调用用户的回调函数。
注:同一个进程中也可以使用管道进行通信。

2.9 LIST

libusb实现了循环双向链表,并且只有前向和后向指针,无数据成员,实现方法上也很独特。应用时作为其它数据结构的成员,可通过list_entry宏来获得这个数据结构指针。

struct list_head {struct list_head *prev, *next;
};

void list_init(struct list_head *entry)
初始化一个链表

void list_add_tail(struct list_head *entry, struct list_head *head)
list_add_tail和list_add都是形成双向循环链表,只是实现上有一点不同而已。
将节点entry添加到链表head的尾部,使head->prev指向entry,(head->next固定指向了链表中的第二个节点)

void list_add(struct list_head *entry, struct list_head *head)
将节点entry添加到链表head的尾部,使head->next指向entry,(head->prev固定指向了链表中的第二个节点)

void list_del(struct list_head *entry)
删除一个链表

list_empty(entry)
判断链表是否为空

list_entry(ptr, type, member)
取得包含ptr所指结构体的对象的指针:返回type类型指针,这个type类型指针指向的对象包含这个节点。

ptr:list_head 结构体指针
type:包含member成员的数据类型
member:type数据类型里的成员,member为list_head类型

list_for_each_entry(pos, head, member, type)
遍历head链表中的每个节点(entry),pos指向每次遍历的结果。pos为type类型结构体指针,这个结构体包含list_head 结构体成员。

pos:一个包含member成员的结构体指针
head:list head
member:pos指针指向的结构体里的list_head 结构体成员
type:pos的数据类型

2.10 获取USB设备列表

用户使用ssize_t libusb_get_device_list(libusb_context *ctx, libusb_device ***list)函数即可获得系统当前所有的USB设备。
libusb将接入的usb设备都保存在ctx->usb_devs链表中,libusb_get_device_list函数便是通过它来取得设备列表。
libusb通过三种途径来维护ctx->usb_devs链表(这里主要指添加设备)。

  1. 调用libusb_init初始化时,libusb调用linux_scan_devices获取系统当前所有usb设备,并将其添加到ctx->usb_devs链表。
  2. 在hotplug监听线程中,如果有设备插入,便将其添加到ctx->usb_devs链表。
  3. 用户调用libusb_get_device_list时,再一次查看是否有新设备插入,如果有便将其添加到ctx->usb_devs链表。

2.11 控制传输

用户可以使用libusb_control_transfer() 进行控制传输。
控制传输既可以在系统枚举阶段进行,也可以在打开USB设备后进行传输,如在xusb.c中便有很多地方用到了控制传输:获取HID设备的报告描述符等。

2.12 头文件说明

(1)宏定义_MSC_VER

_MSC_VER是微软的预编译控制,由于vc++不支持stdbool.h,所以有某些头文件有以下代码以便支持bool变量。

#if !defined(_MSC_VER)
#include <stdbool.h>
#else
#define __attribute__(x)
#if !defined(bool)
#define bool int
#endif
#if !defined(true)
#define true (1 == 1)
#endif
#if !defined(false)
#define false (!true)
#endif
#if defined(_PREFAST_)
#pragma warning(disable:28193)
#endif
#endif

(2) C++支持

为了在C++代码支持libusb库,在头文件中可见以下代码:

#ifdef  __cplusplus
extern "C" {#endif// 代码#ifdef  __cplusplus
}
#endif

(3)条件编译的使用

#if, #elif, #else, #endif
#if defined()和#if !defined()

2.13 其它

宏定义_WIN32

VC 有 3 个预处理常量,分别是 _WIN32、_WIN64、WIN32,WIN32和_WIN32 可以用来判断是否 Windows 系统(对于跨平台程序),而 _WIN64 用来判断编译环境是 x86 还是 x64。

3. libusb测试demo

github:https://github.com/crazybaoli/libusb-test

  • 支持bulk/interrupt endpoint 数据读写
  • 支持hotplug
  • 支持命令行参数
  • 支持快捷发送数据
  • 支持将收到的数据保存为文件
  • 支持lsusb功能,可列出系统所有usb设备
  • 支持打印显示特定usb设备(VID:PID)的描述符

libusb开发指南相关推荐

  1. 最全面的Unity游戏开发指南视频教程 第2卷

    最全面的Unity游戏开发指南视频教程 第2卷 流派:电子学习| MP4 |视频:h264,1280×720 |音频:AAC,44.1 KHz 语言:英语+中英文字幕(根据原英文字幕机译更准确)|大小 ...

  2. Linux 汇编语言开发指南

    Linux 汇编语言开发指南 肖文鹏 (xiaowp@263.net), 北京理工大学计算机系硕士研究生 本文作者 肖文鹏是北京理工大学计算机系的一名硕士研究生,主要从事操作系统和分布式计算环境的研究 ...

  3. 《Python和Pygame游戏开发指南》——2.16 pygame.display.update()函数

    本节书摘来自异步社区<Python和Pygame游戏开发指南>一书中的第2章,第2.16节,作者[美]Al Sweigart(斯维加特), 李强 译,更多章节内容可以访问云栖社区" ...

  4. 400 多行代码!超详细 Rasa 中文聊天机器人开发指南 | 原力计划

    作者 | 无名之辈FTER 责编 | 夕颜 出品 | 程序人生(ID:coder_life) 本文翻译自Rasa官方文档,并融合了自己的理解和项目实战,同时对文档中涉及到的技术点进行了一定程度的扩展, ...

  5. Knockout应用开发指南 第六章:加载或保存JSON数据

    原文:Knockout应用开发指南 第六章:加载或保存JSON数据 加载或保存JSON数据 Knockout可以实现很复杂的客户端交互,但是几乎所有的web应用程序都要和服务器端交换数据(至少为了本地 ...

  6. iOS开发指南 从Hello World到App Store上架 第5版

    iOS开发指南 从Hello World到App Store上架 第5版 关东升 著 ISBN:9787115450630 包装:平装 开本:16开 正文语种:中文 出版社: 人民邮电出版社 出版时间 ...

  7. 《jQuery EasyUI开发指南》——10.4 迭代开发

    本节书摘来自异步社区<jQuery EasyUI开发指南>一书中的第10章,第10.4节,作者:王波著,更多章节内容可以访问云栖社区"异步社区"公众号查看 10.4 迭 ...

  8. 《Node.js开发指南》书评汇总

    刚查了下库存,发现订阅<Node.js开发指南>的读者大增,这是为什么呢?看了下近期本书在豆瓣的评论,口碑很好,现将豆瓣的书评汇总如下: ------------------------- ...

  9. JNI/NDK开发指南(八)——调用构造方法和父类实例方法

    转载请注明出处:http://blog.csdn.net/xyang81/article/details/44002089 在第6章我们学习到了在Native层如何调用Java静态方法和实例方法,其中 ...

最新文章

  1. 忘记mysq rootl密码
  2. 去掉softmax后Transformer会更好吗?复旦华为诺亚提出SOFT
  3. QT的QWGLNativeContext类的使用
  4. IOS开发基础之UI的喜马拉雅的项目-10
  5. 解决Java当中 用point 画图时背景颜色变成黑色问题
  6. uva 10118 ——Free Candies
  7. 计算机动画制作 教学设计,《设置动画效果》教学设计
  8. ajax配套字符串,在ajax请求中填充数据字符串
  9. 增益和偏移的概念_高速ADC的关键指标:量化误差、offset/gain error、DNL、INL、ENOB、分辨率、RMS、SFDR、THD、SINAD、dBFS、TWO...
  10. SDN/NFV若干问题
  11. Pandas(鸢尾花案例:groupby, agg, apply)
  12. 15 岁黑进系统,发挑衅邮件意外获 Offer,不惑之年捐出全部财产,Twitter CEO 太牛了!...
  13. 数论基本定理及应用(三)
  14. 愚人节的礼物Java
  15. 送给前端的你,推荐几篇前端汇总文章。(来自知乎专栏)
  16. 南京大学计算机考研复试线2021,南京大学2021年考研复试基本分数线已发布
  17. 百度地图html多点标注,百度地图api 同时在地图上标注多个点 有问题 紧急求救...
  18. 人工智能技术对我们的生活,有多少影响?
  19. 【C++从入门到入土】第五篇:继承(爆肝画图详解)
  20. 页面布局整理汇总,让你彻底搞明白多种布局的关系

热门文章

  1. Effective C++ ------- virtual
  2. Java Date Time 教程
  3. 【百度地图API】如何调整结果面板的样式?如何获取指定页码的结果?
  4. 一起谈.NET技术,C#序列化与反序列化(Serializable and Deserialize)
  5. 解决xp登陆域很慢的方法
  6. Android view变形,android仿变形金刚效果实现MatchView
  7. JSTL-EL表达式
  8. Ext Ajax:如何调用Ext.Ajax.request方法和使用Java Servlet进行处理
  9. python 读plt文件_用python读Excel文件
  10. mysql中的运算符的执行顺序_【MySQL】执行顺序