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 usb0usb0应该处于定义状态,以上命令才能成功执行。 上面的命令以及cfgmgr命令可以发现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
在本节中,说明了使用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 。
编写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; } }如果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,则必须将请求检测命令发送到设备。
每个操作系统必须实现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接口。
要在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 listdevslisdevs可执行文件提供以下输出:
# ./listdevs found /dev/usbhc0 found 1 device 0951:1656 (bus 0, device 2) path: 1本节说明了在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