libusb
是一个C库,应用程序开发人员可以使用它来编写用于访问USB设备的用户空间驱动程序。 应用程序开发人员无需依赖要为新的USB设备开发的操作系统驱动程序。 可以使用libusb
框架公开的API在用户级别开发驱动程序,而libusb
框架又调用特定于OS的API。 您可以在http://libusb.info/中找到有关libusb
更多信息。
当前支持的Linux®,OS X,Microsoft®Windows®,Android,OpenBSD等都是低端系统。 libusb
已被libusb
移植并libusb
支持,IBM®AIX®是Big-endian操作系统。 应用程序开发人员必须在其现有的libusb
应用程序中更改其大尾数敏感代码,以在AIX上运行。 本文清楚地说明了如何编写libusb
应用程序以及如何在现有应用程序中更改代码的实例,以使其能够在大型字节序系统(例如AIX)上运行。
安装与配置
可以从以下位置下载用于AIX的libusb
rpm: https : //public.dhe.ibm.com/aix/freeSoftware/aixtoolbox/RPMS/ppc/libusb/
可以使用以下命令将rpm安装在AIX系统上:
#rpm -ivh libusb-1.0.19-1.aix7.1.ppc.rpm
支持libusb
的最低AIX版本是7.2TL1和7.1TL4SP3。
默认情况下禁用AIX中的libusb
支持。 必须通过运行以下命令来设置ODM变量来启用它:
#chdev –a usblibconfig="available" –l usb0
usb0
应该处于定义状态,以上命令才能成功执行。 上面的命令以及cfgmgr
命令可以发现libusb
设备。
了解libusb设备
libusb
设备在/ dev文件系统中创建。 这些设备的父级是usb0
。
当libusb
是通过设置启用usblibconfig
属性available
, libusb
设备曝光率,为孩子usb0
。
您可以通过运行以下命令列出所有USB设备:
# lsdev -C | grep usb
对于具有标准操作系统客户端驱动程序的设备(例如闪存驱动器,键盘和鼠标),每个设备都表示为两个设备。 一个是操作系统设备,另一个是libusb
设备。 例如,如果将闪存驱动器连接到AIX系统,则操作系统客户端驱动程序将设备枚举为usblibdev0
, libusb
枚举设备为usbms0
。 如果没有操作系统客户端驱动程序要求一个设备,则该设备仅代表一个设备。 例如,如果将USB摄像机连接到AIX系统,则它将仅表示为usblibdev1
。
对于每个libusb
设备(也具有AIX操作系统内置客户端驱动程序的设备),在ODM中会创建一个名为usbdevice
的新属性,以标识libusb
设备的相应客户端驱动程序的设备。 以下示例显示如何将libusb
设备与操作系统客户端驱动程序的设备关联:
# lsattr -El usblibdev0
speed highspeed USB Protocol Speed of
Device False
usbdevice usbms0 Actual USB Device with Client
Driver False
在此示例中,USB设备是闪存驱动器,其具有内置于Mass Storage Class客户端驱动程序(/usr/lib/drivers/usb/usbcd)
的AIX操作系统。 usbdevice
属性指示与usblibdev0
设备关联的客户端驱动程序的设备为usbms0
。
您可以在以下位置找到有关libusb
设备和相关驱动程序的更多信息: https : //www.ibm.com/support/knowledgecenter/ssw_aix_72/com.ibm.aix.ktechrf2/usblibdd_pass.htm
在AIX上编写libusb应用程序
在本节中,说明了使用libusb
API的三个示例。 这些示例的说明如下:
listdevs:
此示例打印所有连接到AIX系统的设备,并打印其供应商ID(VID),产品ID(PID),总线号,设备地址和端口号。
xusb
:此示例从USB设备读取设备描述符,二进制设备对象存储(BOS)描述符和字符串描述符。
每个libusb
应用程序必须以libusb_init
开头,并以libusb_exit
。
典型的libusb
应用程序如下所示:
int main() {
libusb_init(); /* Initializes the AIX LIUBUSB library */
……………
libusb_get_device_list(…) /* Get the USB devices list */
………………
libusb_open(…);/*opens your device using VID, PID combination*/
……………
libusb_claim_interface(…) /* claim your device interface */
……………
……………
/* the application code */
……………
……………
libusb_release_interface(…) /* release your device interface*/
……………
libusb_close(…); /* close your device */
……………
libusb_exit(); /* undo the initialization and do cleanup */
}
注意:如果libusb_open
已经处于活动状态,它将自动分离内核驱动程序。 libusb_close
将附加内核驱动程序.
listdevs:
int main(void)
{
libusb_device **devs;
int r;
ssize_t cnt;
r = libusb_init(NULL);
if (r < 0)
return r;
cnt = libusb_get_device_list(NULL, &devs);
if (cnt < 0)
return (int) cnt;
print_devs(devs);
libusb_free_device_list(devs, 1);
libusb_exit(NULL);
return 0;
}
static void print_devs(
libusb_device **devs)
{
libusb_device *dev;
int i = 0, j = 0;
uint8_t path[8];
while ((dev = devs[i++]) != NULL)
{
struct libusb_device_descriptor desc;
int r = libusb_get_device_descriptor(dev, &desc);
if (r < 0) {
fprintf(stderr, "failed to getdescriptor");
return;
}
printf("%04x:%04x (bus %d, device %d)", desc.idVendor, desc.idProduct,
libusb_get_bus_number(dev), libusb_get_device_address(dev));
r = libusb_get_port_numbers(dev, path, sizeof(path));
if (r > 0) {
printf("path: %d", path[0]);
for (j = 1; j < r; j++)
printf(".%d",path[j]);
}
printf("\n");
}
}
要获取所有设备的列表,必须调用libusb_get_device_list
。 在内部,此libusb
函数调用特定于AIX的API以获取连接到AIX系统的设备的列表。 除设备列表外,还检索设备信息,例如总线号,端口号,设备地址和设备描述符。 所有这些设备信息都存储在每个设备的libusb
本地数据结构( struct libusb_device)
中。 后续调用(例如libusb_get_device_descriptor
, libusb_get_bus_number
和libusb_get_device_address
将从本地数据结构获取与这些调用关联的数据,并且不会生成对AIX USB子系统的API调用。
xusb:
int main(int argc, char **argv)
{
int i;
unsigned tmp_vid, tmp_pid;
static uint16_t VID, PID;
libusb_device **devs;
int r;
ssize_t cnt;
libusb_device_handle *handle;
for (i=0; i<arglen; i++) {
if (argv[j][i] == ':')
break;
if (i != arglen) {
if (sscanf(argv[j], "%x:%x" ,&tmp_vid, &tmp_pid) != 2)
{
printf(" Please specify VID & PID as \"vid:pid\" in hexadecimal format\n");
return 1;
}
VID = (uint16_t)tmp_vid;
PID = (uint16_t)tmp_pid;
r = libusb_init(NULL);
if (r < 0)
return r;
handle = libusb_open_device_with_vid_pid(NULL, vid, pid);
if (handle == NULL) {
perr(" Failed.\n");
return -1;
}
get_descriptors(handle, VID, PID);
printf("Closing device...\n");
libusb_close(handle);
libusb_exit(NULL);
return 0;
}
int get_descriptors( libusb_device_handle *handle, int vid, int pid)
{
libusb_device *dev;
uint8_t bus, port_path[8];
struct libusb_bos_descriptor *bos_desc;
struct libusb_config_descriptor *conf_desc;
const struct libusb_endpoint_descriptor *endpoint;
int i, j, k, r;
int iface, nb_ifaces, first_iface = -1;
struct libusb_device_descriptor dev_desc;
char string[128];
uint8_t string_index[3];
uint8_t endpoint_in = 0, endpoint_out = 0
printf("Opening device %04X:%04X...\n", vid, pid);
dev = libusb_get_device(handle);
printf("\nReading device descriptor:\n");
libusb_get_device_descriptor(dev, &dev_desc));
printf("length: %d\n", dev_desc.bLength);
printf("device class: %d\n", dev_desc.bDeviceClass);
printf("S/N: %d\n", dev_desc.iSerialNumber);
printf("VID:PID: %04X:%04X\n", dev_desc.idVendor, dev_desc.idProduct);
printf("nb confs:%d\n", dev_desc.bNumConfigurations);
// Copy the string descriptors for easier parsing
string_index[0] = dev_desc.iManufacturer;
string_index[1] = dev_desc.iProduct;
string_index[2] = dev_desc.iSerialNumber;
printf("\nReading string descriptors:\n");
for (i=0; i<3; i++)
{
if (string_index[i] == 0){
continue;
}
if (libusb_get_string_descriptor_ascii(handle, string_index[i],
(unsigned char*)string, 128) >= 0)
{
printf(" String (0x%02X): \"%s\"\n", string_index[i], string);
}
}
}
在对设备执行任何读或写操作之前,应先将其打开。 在设备打开事件之后返回一个句柄,该事件需要在后续的libusb
API调用中使用以引用设备。 libusb
有一个API, libusb_open_device_with_vid_pid,
它将VID和PID作为输入,打开设备,并返回设备句柄。 此功能的替代方法是调用libusb_get_device_list
并获取与VID和PID匹配的设备,然后调用libusb_open
以获得该设备的句柄。 在应用程序的末尾,必须调用libusb_close
破坏由libusb_open
创建的libusb_open
。
编写执行I / O操作的libusb应用程序
编写libusb
应用程序以执行对USB闪存设备的读取或写入操作时,涉及以下步骤。
- 填充“命令块包装器”(CBW)结构中的所有必填字段,并在扩展端点上发送CBW命令。
- 根据读取或写入操作,将缓冲区及其大小发送到大容量或大容量端点。 如果是读操作,则必须将缓冲区发送到大容量端点,如果是写操作,则必须将其发送到大容量端点。
- 读取批量导入端点上的状态。
下面给出了可以通过USB设备执行I / O操作的典型应用程序代码:
read_io(){
…………
………….
/* Send CBW */
libusb_bulk_transfer (handle, endpoint, (unsigned char*) &cbw, 31, &size, 1000);
…………..
/* Send Buffer */
libusb_bulk_transfer (handle, endpoint_in, data, block_size, &size, 5000);
…………..
…………..
/* Get Status */
libusb_bulk_transfer (handle, endpoint, (unsigned char*)&csw, 13, &size, 1000);
………….
if (csw.bCSWStatus)
{
if (csw.bCSWStatus == 1)
return -2; // request Get Sense
else
return -1;
}
}
发送CBW
如果USB设备遵循SCSI标准,则CBW的典型大小为31个字节。 如下所示填充字段:
cbw.dCBWSignature[0] = 'U';
cbw.dCBWSignature[1] = 'S';
cbw.dCBWSignature[2] = 'B';
cbw.dCBWSignature[3] = 'C';
cbw.dCBWTag = tag++;
cbw.dCBWDataTransferLength = data_length;
cbw.dCBWDataTransferLength = REV32(data_length);
cbw.bmCBWFlags = direction;
cbw.bCBWLUN = lun;
cbw.bCBWCBLength = cdb_len;
memcpy(cbw.CBWCB, cdb, cdb_len);
由于大字节序,字段dCBWDataTransferLength
应当在AIX上以不同的方式填充。 字节交换后必须填充它。 REV32是一个宏,它将32位大字节序转换为小字节序。 宏如下所示:
#define REV32 ((num>>24)&0xff) | // move byte 3 to byte 0
((num<<8)&0xff0000)| // move byte 1 to byte 2
((num>>8)&0xff00) | // move byte 2 to byte 1
((num<<24)&0xff000000); // byte 0 to byte 3
填写完字段后,必须以以下方式将命令发送到USB设备。 libusb_bulk_transfer
是一个libusb
API,用于提交批量I / O事务。
r = libusb_bulk_transfer (handle, endpoint, (unsigned char*)&cbw, 31, &size, 1000);
if (r == LIBUSB_ERROR_PIPE) {
libusb_clear_halt(handle, endpoint);
}
哪里:
-
handle
:libusb设备的设备句柄。 -
Endpoint
:是基于I / O操作的设备的端点号。 由于CBW始终在out端点上发送,因此应在此处给出out端点号。 -
cbw
:是如上所述填充的CBW结构。 -
31
:CBW结构的大小。 -
size
:是在I / O传输中成功传输的字节数。 -
1000
:命令的超时时间(以毫秒为单位)。
某些设备使管道停滞并导致管道错误。 因此,无论何时libusb_bulk_transfer
并必须调用libusb_clear_halt
函数以清除管道上的停顿条件,都必须检查此错误。
向设备发送读/写操作
发送CBW之后,需要发送数据缓冲区。 在以下示例中,将对512字节的读取操作发送到设备。
r = libusb_bulk_transfer(handle, endpoint_in, data, 512, &size, 5000);
if (r == LIBUSB_ERROR_PIPE) {
libusb_clear_halt(handle, endpoint_in);
}
读取操作的超时时间为5秒。
阅读状态
发送数据缓冲区后,通过发送CSW从设备检索I / O操作的状态。 如果USB设备遵循SCSI标准,则CSW的典型大小为31个字节。
r = libusb_bulk_transfer (handle, endpoint, (unsigned char*)&csw, 13, &size, 1000);
if (r == LIBUSB_ERROR_PIPE) {
libusb_clear_halt (handle, endpoint);
}
命令的状态存储在csw.bCSWStatus
。
错误条件不在本文中解释。 如果状态为-2,则必须将请求检测命令发送到设备。
AIX中支持的libusb API
每个操作系统必须实现usbi_os_backend
提供的usbi_os_backend
结构提供的libusbi.h
。 AIX当前实现以下接口:
Init,
exit,
get_device_list,
open,
close,
get_device_descriptor,
get_active_config_descriptor,
get_config_descriptor,
get_config_descriptor_by_value,
get_configuration,
set_configuration,
set_interface_altsetting,
kernel_driver_active
clear_halt,
reset_device,
destroy_device,
submit_transfer,
cancel_transfer,
clear_transfer_priv,
handle_events,
clock_gettime
注意:AIX中的submit_transfer
API当前实现批量 , 中断和同步传输,但仅支持批量传输。 而且,它不实现USB 3.0批量流libusb
接口。
编译您的libusb应用程序
要在AIX上编译libusb
应用程序,必须安装libusb-1.0.19-1.aix7.1.ppc.rpm
。 有关rpm,设备配置和受支持的AIX版本的安装 ,请参阅本文的“ 安装和配置”部分。
以下命令确认libusb
rpm的安装成功。
# rpm -qa libusb
libusb-1.0.19-1.ppc
头文件libusb.h
安装在/usr/include/libusb-1.0中,应用程序应链接到的libusb静态库( libusb-1.0
)安装在/usr/lib/libusb-1.0中。
可以使用以下命令来编译libusb
应用程序(例如listdevs.c
):
# xlc -I /usr/include/libusb-1.0 listdevs.c -lusb-1.0 -lpthread -lcfg -lodm -o listdevs
lisdevs
可执行文件提供以下输出:
# ./listdevs
found /dev/usbhc0
found 1 device
0951:1656 (bus 0, device 2) path: 1
在AIX上调试应用程序
本节说明了在AIX上运行libusb
应用程序时如何收集应用程序和内核日志。
应用日志
可以通过设置环境变量将应用程序日志收集到文件中。
在运行应用程序的shell上运行以下命令
#export LIBUSB_DEBUG=4
运行应用程序后,应用程序日志将捕获在/var/adm/ras/libusb.log中
内核日志
libusb应用程序运行时,也可以调试内核。 可以使用以下方法将内核日志收集到文件中:
创建一个目录,您需要在其中保留日志。
#mkdir -p /var/adm/ras/kernellogs
在启动应用程序之前,请运行以下命令:
#trace -a -L 16777216 -T 1048576 -j 1075,7380 -o
/var/adm/ras/kernellogs/usbsystrace
#ctctrl -c usblibdd -r memtracedetail
您的应用程序退出后,转储收集的跟踪。
#trcstop
#ctctrl -D -c usblibdd -r -d /var/adm/ras/kernellogs
日志文件将位于/ var / adm / ras / kernellogs目录中。
翻译自: https://www.ibm.com/developerworks/aix/library/au-aix-libusb/index.html