如何使用libusb开发_在IBM AIX中使用libusb开发应用程序

本文详细介绍了如何在IBM AIX系统上使用libusb开发应用程序,包括安装配置、理解libusb设备、编写libusb应用程序、执行I/O操作及调试。libusb是一个C库,允许开发者在用户空间编写USB设备驱动,支持多种操作系统,包括AIX。文中提供了具体的示例代码,如发送CBW、读写操作和检查设备状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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属性availablelibusb设备曝光率,为孩子usb0

您可以通过运行以下命令列出所有USB设备:

# lsdev -C | grep usb

对于具有标准操作系统客户端驱动程序的设备(例如闪存驱动器,键盘和鼠标),每个设备都表示为两个设备。 一个是操作系统设备,另一个是libusb设备。 例如,如果将闪存驱动器连接到AIX系统,则操作系统客户端驱动程序将设备枚举为usblibdev0libusb枚举设备为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_descriptorlibusb_get_bus_numberlibusb_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闪存设备的读取或写入操作时,涉及以下步骤。

  1. 填充“命令块包装器”(CBW)结构中的所有必填字段,并在扩展端点上发送CBW命令。
  2. 根据读取或写入操作,将缓冲区及其大小发送到大容量或大容量端点。 如果是读操作,则必须将缓冲区发送到大容量端点,如果是写操作,则必须将其发送到大容量端点。
  3. 读取批量导入端点上的状态。

下面给出了可以通过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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值