在 QT 中使用 libusb 检测 MAC 上的 USB 设备

最近在用 QT 做一个 MAC 上的 Kindle 批注管理软件,遇到的第一个问题就是检测 MAC 上连接的 USB 设备的状态。如果是在 Cocoa 进行开发,会有对应的系统 API 可供使用,但是由于我是在 QT 平台进行的开发,所以无形中加大了一点难度。就在这时,我发现了一个库:libusb

libusb 介绍

libusb 设计了一系列的外部API 为应用程序所调用,通过这些API应用程序可以操作硬件,从libusb的源代码可以看出,这些API 调用了内核的底层接口,和kernel driver中所用到的函数所实现的功能差不多,只是libusb更加接近USB 规范。使得libusb的使用也比开发内核驱动相对容易的多。(From: 百度百科

0x00 下载 libusb

在 libusb 项目主页(http://libusb.info)我们可以找到最新的源码,下载下来,并且解压。这里我下载的是 libusb-1.0.20.tar.bz2,把它解压出来。

0x01 安装 libusb

cd libusb-1.0.20/
./configure
make
make install

这时就已经在机器上编译安装完成了 libusb

0x02 运行示例程序

cd examples/
make

然后我们看到在 examples/ 目录下多了几个可执行程序:

  • listdevs:列出当前所有的 USB 设备
  • hotplugtest:USB 热插拔测试
  • dpfp_threaded:操作 U.are.U 4000b 指纹采集仪的 Demo
  • dpfp:初始化 U.are.U 4000b 指纹采集仪
  • sam3u_benchmark:测试 Atmel SAM3U USB 主控的同步传输的性能的 Demo
  • fxload:USB 固件操作
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
  • xusb:USB 测试程序
usage: /Users/jason/Downloads/libusb-1.0.20/examples/.libs/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

我们以 listdevs 为例,执行测试程序:

./listdevs

执行结果:

05ac:8406 (bus 20, device 3) path: 7
05ac:828f (bus 20, device 20) path: 3.3
0a5c:4500 (bus 20, device 27) path: 3
05ac:8005 (bus 20, device 0)

链接库

在Windows平台、MAC 平台和Linux平台下都大量存在着库。本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。

由于Windows、MAC和Linux的平台不同(主要是编译器、汇编器和连接器的不同),因此二者库的二进制是不兼容的。

Linux下的库有两种:静态库共享库(动态库)。二者的不同点在于代码被载入的时刻不同。

静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。

共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。

创建静态链接库

0x00 写一个静态链接库

  • hello.h
#ifndef HELLO_H
#define HELLO_H 

void hello(const char *name); 

#endif
  • hello.c
#include <stdio.h>
void hello(const char *name) {
        printf("Hello %s!\n", name);
}
  • main.c
#include "hello.h"
int main()
{
    hello("world");
    return 0;
}

0x01 创建目标代码

gcc -c hello.c

gcc 中 -c 的编译选项的意思是使用GNU汇编器将源文件转化为目标代码之后就结束,在这种情况下,只调用了C编译器(ccl)和汇编器(as),而连接器(ld)并没有被执行,所以输出的目标文件不会包含作为程序在被装载和执行时所必须的包含信息,但它可以在以后被连接到一个程序。

我们执行 ls 命令会看到目录下多了一个 hello.o 文件,它就是 hello.c 编译的目标代码

0x02 创建静态链接库

ar rcs libhello.a hello.o

创建静态库用ar命令。

静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.a。

例如:我们将创建的静态库名为hello,则静态库文件名就是libhello.a。在创建和使用静态库时,需要注意这点。

我们执行 ls 命令,可以看到目录下多了一个静态链接库 libhello.a

0x03 链接静态链接库

静态库制作完了,如何使用它内部的函数呢?

只需要在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明静态库名,gcc将会从静态库中将公用函数连接到目标文件中。

注意,gcc会在静态库名前加上前缀lib,然后追加扩展名.a得到的静态库文件名来查找静态库文件,因此,我们在写需要连接的库时,只写名字就可以,如libhello.a的库,只写: -lhello

gcc -o main main.c -L. -lhello

执行程序

./main
Hello world!

创建动态链接库

0x00 创建动态链接库

gcc -dynamiclib -o hello.dylib hello.o

我们使用 ls 命令可以看到目录下多了 hello.dylib,它就是创建的动态链接库(.dylib是 MAC 系统下的,Windows 下是.dll, Linux 下是.so)

0x00 链接动态链接库

gcc -o main1 main.c -L. -lhello

执行程序

./main1
Hello world!

在 QT 中使用 libusb

0x00 将 libusb 动态链接库加入 QT 项目

我们首先在 QT5 中新建一个项目 Testlibusb,然后在项目目录下新建一个目录 lib 用来存放 libusb 的库文件。

将 libusb 对应的库文件复制到该目录下,因为我所使用的平台是 MAC OS X,所对应的库文件应当是以 .dylib 为扩展名的,我们在 libusb 源码文件夹下的 /libusb/.libs/ 目录下找到 libusb-1.0.0.dylib 然后复制到刚刚创建的目录下

并且将 libusb 的头文件 libusb.h 加入到项目中

0x01 修改 QT 项目编译选项

修改 QT 项目中的 .pro 文件,加入下面几行:

macx: LIBS += -L$$PWD/lib/ -lusb-1.0.0

INCLUDEPATH += $$PWD/.

DEPENDPATH += $$PWD/.

0x02 编写 libusb 测试程序

  • getusbinfo.h
#ifndef GETUSBINFO
#define GETUSBINFO

#include <QString>
#include <QObject>
#include <QList>
#include <QThread>

#include <libusb.h>

struct STUUSBDevices{
    QString idProduct;
    QString idVendor;
    QString iManufacturer;
    QString iSerialNumber;
};

class GetUsbInfo :  QThread{
public:
    GetUsbInfo(QObject *parent);
    ~GetUsbInfo();
    int initUsbDevices();
    QString getVidPid(libusb_device **devs);
    void showAllUsbDevices(QList<STUUSBDevices> lst);
    void setRunStatus();
    void run();

    bool isStop;

};

#endif // GETUSBINFO
  • getusbinfo.cpp
#include "getusbinfo.h"
#include <QThread>
#include <QDebug>
#include <QString>

GetUsbInfo::GetUsbInfo(QObject *parent) :
    QThread(parent),isStop(false)
{
}

GetUsbInfo::~GetUsbInfo()
{
    qDebug()<<"GetUsbInfo::~GetUsbInfo "<<endl;
}

int GetUsbInfo::initUsbDevices()
{
    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;

    getVidPid(devs);
    libusb_free_device_list(devs, 1);

    libusb_exit(NULL);
    return 0;
}

QString GetUsbInfo::getVidPid(libusb_device **devs)
{
    libusb_device *dev;
    int i = 0;
    QList<STUUSBDevices> lstUsb;
    while ((dev = devs[i++]) != NULL) {
        struct libusb_device_descriptor desc;
        int r = libusb_get_device_descriptor(dev, &desc);
        if (r < 0) {
            qDebug()<<"failed to get device descriptor"<<stderr;
            return "";
        }
        printf("%04x:%04x (bus %d, device %d)\n",
            desc.idVendor, desc.idProduct,
            libusb_get_bus_number(dev), libusb_get_device_address(dev));
        STUUSBDevices stu;
        stu.idProduct = QString::number(desc.idProduct);
        stu.idVendor = QString::number(desc.idVendor);
        stu.iManufacturer = QString::number(desc.iManufacturer);
        stu.iSerialNumber = QString::number(desc.iSerialNumber);

        lstUsb.append(stu);
    }
    showAllUsbDevices(lstUsb);
    return QString(lstUsb[0].idProduct);
}

void GetUsbInfo::showAllUsbDevices(QList<STUUSBDevices> lst)
{
    for(int i=0;i<lst.count();i++)
    {
        qDebug()<<"vid: "<<lst.at(i).idVendor<<"\n"
               <<"pid:"<<lst.at(i).idProduct<<"\n"
                <<"serNumber:"<<lst.at(i).iSerialNumber<<"\n"
                  <<"Manufacturer:"<<lst.at(i).iManufacturer<<"\n";
    }
}

void GetUsbInfo::setRunStatus()
{
    isStop = true;
}

void GetUsbInfo::run()
{
    qDebug()<<"GetUsbInfo::run() "<<endl;
    while (!isStop)
    {
       initUsbDevices();
       sleep(10);
    }
}
  • main.cpp
#include "mainwindow.h"
#include <QApplication>
#include "getusbinfo.h"

#include <QLibrary>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    QThread t;

    GetUsbInfo info(&t);

    info.initUsbDevices();

    return a.exec();
}

0x03 执行 libusb 测试程序

Starting /Users/jason/Project/QTDemos/build-Testlibusb-Desktop_Qt_5_5_1_clang_64bit-Debug/Testlibusb.app/Contents/MacOS/Testlibusb...
vid:  "1452"
 pid: "33798"
 serNumber: "5"
 Manufacturer: "3" 

vid:  "1452"
 pid: "33423"
 serNumber: "0"
 Manufacturer: "1" 

vid:  "2652"
 pid: "17664"
 serNumber: "0"
 Manufacturer: "1" 

vid:  "1452"
 pid: "32773"
 serNumber: "0"
 Manufacturer: "0"

最终我们获得了当前 MAC 上的 USB 设备列表


本文的版权归作者 罗远航 所有,采用 Attribution-NonCommercial 3.0 License。任何人可以进行转载、分享,但不可在未经允许的情况下用于商业用途;转载请注明出处。感谢配合!

时间: 2024-10-27 09:28:20

在 QT 中使用 libusb 检测 MAC 上的 USB 设备的相关文章

如何在Mac上为VR设备转换视频

随着VR技术和VR头戴式耳机的日益普及,对VR支持的视频的需求也在增加.Mac版Wondershare UniConverter是出色的VR转换器,可以将视频转换为VR视频并在特定的VR设备上播放.今天分享的就是如何在Mac上为VR设备转换视频. https://www.macdown.com 步骤1启动Wondershare UniConverter并选择VR Converter 下载并安装后,在Mac上打开软件.从主界面的“ 工具箱”部分中选择“ VR Converter”选项. 步骤2加载

Ubuntu 16.04中VirtualBox 5.1使用U盘/USB设备的方法

环境: Ubuntu 16.04 LTS VirtualBox 5.1.26 r118224 虚拟机系统:Windows 7 SP1 64 安装扩展: 在这里下载: http://download.virtualbox.org/virtualbox/5.1.26/Oracle_VM_VirtualBox_Extension_Pack-5.1.26-117224.vbox-extpack http://www.oracle.com/technetwork/server-storage/virtua

在 Mac 上打包 PyQT 程序

有许多人使用 Python 来写图形化界面时选择了 PyQT,但是有许多人不知道如何将开发好的程序打包成为安装包,这篇文章我就来介绍一种非常简单的也是非常基础的在 MAC 下打包 PyQT 程序的方法. 安装 PyQT 安装 QT 我们首先要安装 QT,我这里安装的是, QT 5.5,对于 MAC 上 QT 的安装直接到官方网站上去找到对应的安装包下载安装即可. http://www.qt.io/ 安装 SIP 对于 SIP,我们也需要到官方网站去下载对应的 MAC 的源码包,安装过程如下: p

QT 使用 QTcpSocket来检测 ip 设备的网络状态

使用QT中 QTcpSocket来检测设备的网络状态: 函数返回true,设备网络状态正常,返回false,设备网络异常. bool TcpIpSocket::sendATcpSocketToIp(QString ip){     m_ipAdress = ip;     abort();     connectToHost(m_ipAdress, port);     //等待timeout时间,如果仍然不通,则异常     return waitForConnected(timeout);

在mac上配置push notification的问题

在代码的deletegater中写: - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after

如何在Mac上播放和查看SWF文件

有时,某些Mac用户可能会遇到必须打开或访问的SWF文件.如果您有需要在Mac上查看,播放或打开的SWF文件,则可以使用各种免费工具来实现. SWF是Adobe Flash文件的文件格式,在使用Web动画,某些类型的Web视频,图形作品,交互以及其他类似Web内容时,您可能会在各种不同情况下遇到SWF文件,通常是与Web相关或设计工作. 本教程将向您展示如何在Mac上轻松查看和播放SWF文件,我们将介绍几种不同的方法,以便您使用最适合自己的方法. 如何使用VLC在Mac上查看和播放SWF文件 V

第三十二课、Qt中的文件操作

一.Qt中的IO操作 1.Qt中IO操作的处理方式 (1).Qt通过统一的接口简化了文件与外部设备的操作方式 (2).Qt中的文件被看做是一种特殊的外部设备 (3).Qt中的文件操作与外部设备操作相同 2.IO操作中的关键函数接口 注意:IO操作的本质:连续存储空间的数据读写 3.Qt中IO设备类型 (1).顺序存储设备:只能从头开始顺序读写数据,不能指定数据的读写位置(串口) (2).随机存储设备:可以定位到任意位置进行数据读写(seek function函数)(文件) 4.Qt中IO设备 的

Mac上Android手机无法连接解决方案

用mac电脑开发Android程序的时候,有时候会发现adb连接不上手机,这时候首先你要查找你的开发者选项里面usb调试功能是不是打开了,不可以的话你可以重启一下开发工具,如果打开了还是不能连接的时候,你可以进行如下操作. 1. 打开终端,输入:system_profiler SPUSBDataType,查看Mac系统所有USB设备信息,找到相应的厂商Vender ID. 记住是看的你Android手机的设备Id,前面是Android 2.  echo 0x2a45 >> ~/.android

如何在RHEV虚拟机中使用USB设备的设置方法

使用 SPICE 连接协议的虚拟机可以被配置为直接使用客户端系统上的 USB 设备. 只有在虚拟机处于活跃的状态,并且正通过客户端使用时,客户端上的 USB 设备才可以被重定向到虚拟机上.每次当一个 USB 设备被插入到客户端时,USB 重定义功能可以被手工启动,或在 SPICE 客户端菜单中设置为自动重定向到活跃的虚拟机. 请注意:"客户端系统(client machine)"和"虚拟客户端系统(guest machine)"之间的区别.客户端系统(在本文档中有时