让我们花点时间来欣赏一下Linux的USB栈。它就像一台运转良好的机器,多层结构和谐地协同工作:

  • USB核心:USB操作的核心部分
  • 主机控制器驱动:可以将它们视为USB与计算机硬件之间的翻译者
  • USB设备驱动:应用程序与USB设备之间的中间人
  • 用户空间库:我们今天将主要在这里花时间

现在,让我们撸起袖子,进入精彩的部分吧!

libusb:通往USB奇境的门户

如果你想与USB设备通信而不想编写内核驱动(说实话,谁不想呢?),libusb就是你的新好朋友。这个用户空间库是USB通信的多功能工具。

首先,让我们安装libusb:

sudo apt-get install libusb-1.0-0-dev

现在,让我们编写一个简单的程序来列出系统中连接的所有USB设备:


#include <stdio.h>
#include <libusb-1.0/libusb.h>

int main() {
    libusb_device **devs;
    libusb_context *ctx = NULL;
    int r;
    ssize_t cnt;

    r = libusb_init(&ctx);
    if(r < 0) {
        fprintf(stderr, "Init Error %d\n", r);
        return 1;
    }

    cnt = libusb_get_device_list(ctx, &devs);
    if(cnt < 0) {
        fprintf(stderr, "Get Device Error\n");
        return 1;
    }

    printf("Number of USB devices: %ld\n", cnt);

    libusb_free_device_list(devs, 1);
    libusb_exit(ctx);
    return 0;
}

用以下命令编译这个程序:

gcc -o usb_list usb_list.c $(pkg-config --cflags --libs libusb-1.0)

运行它,瞧!你已经迈出了低级USB通信的第一步。

用户空间驱动:谁还需要内核空间呢?

现在,我知道你在想什么:“但编写驱动不是内核空间的事情吗?”好吧,欢迎来到用户空间驱动的叛逆世界!它们的好处在于:

  • 无需重新编译内核(为懒惰欢呼!)
  • 更容易调试(printf是你的朋友)
  • 减少整个系统崩溃的可能性(总是一个优点)

让我们为一个假设的USB设备创建一个简单的用户空间驱动:


#include <stdio.h>
#include <libusb-1.0/libusb.h>

#define VENDOR_ID  0x1234
#define PRODUCT_ID 0x5678

int main() {
    libusb_device_handle *dev_handle;
    libusb_context *ctx = NULL;
    int r;
    
    r = libusb_init(&ctx);
    if(r < 0) {
        fprintf(stderr, "Init Error %d\n", r);
        return 1;
    }

    dev_handle = libusb_open_device_with_vid_pid(ctx, VENDOR_ID, PRODUCT_ID);
    if(dev_handle == NULL) {
        fprintf(stderr, "Cannot open device\n");
        libusb_exit(ctx);
        return 1;
    }

    printf("Device opened successfully!\n");

    if(libusb_kernel_driver_active(dev_handle, 0) == 1) {
        printf("Kernel driver active\n");
        if(libusb_detach_kernel_driver(dev_handle, 0) == 0)
            printf("Kernel driver detached\n");
    }

    r = libusb_claim_interface(dev_handle, 0);
    if(r < 0) {
        fprintf(stderr, "Cannot claim interface\n");
        libusb_close(dev_handle);
        libusb_exit(ctx);
        return 1;
    }

    printf("Interface claimed\n");

    // 在这里进行实际的设备通信

    libusb_release_interface(dev_handle, 0);
    libusb_close(dev_handle);
    libusb_exit(ctx);

    return 0;
}

这个例子展示了如何打开设备、在内核驱动活跃时将其分离,并声明接口。从这里开始,你可以开始向设备发送命令!

注意陷阱

尽管低级USB通信令人兴奋,但并非一帆风顺。以下是一些需要注意的事项:

  • 权限:确保你有访问USB设备的正确权限。你可能需要以sudo运行程序或设置udev规则。
  • 资源管理:始终释放你的资源!libusb_free_device_list、libusb_close和libusb_exit是你的朋友。
  • 错误处理:USB通信可能不稳定。始终检查返回值并优雅地处理错误。
  • 设备特定的怪癖:某些USB设备有……我们称之为“独特的个性”。准备好处理意外行为。

超越基础:高级技术

一旦你掌握了基本的USB通信,就有一整套高级技术可以探索:

异步I/O

当你需要处理多个USB操作时:


void callback(struct libusb_transfer *transfer)
{
    // 处理已完成的传输
}

// 在你的主函数中:
unsigned char data[64];
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
libusb_fill_bulk_transfer(transfer, dev_handle, endpoint, data, sizeof(data), callback, NULL, 0);
libusb_submit_transfer(transfer);

等时传输

适合流式数据,如音频或视频:


struct libusb_transfer *transfer = libusb_alloc_transfer(8);
libusb_fill_iso_transfer(transfer, dev_handle, endpoint, buffer, buffer_length, num_iso_packets, callback, NULL, 0);
libusb_set_iso_packet_lengths(transfer, packet_size);
libusb_submit_transfer(transfer);

低级控制的力量

到现在,你可能会想,“为什么要费这么大劲,而我可以直接使用高级API?”好吧,我好奇的朋友,低级USB通信给你:

  • 对设备通信的精细控制
  • 实现自定义协议的能力
  • 对时间敏感应用程序的更好性能
  • 了解底层发生了什么的满足感

总结:USB掌握达成?

我们只是触及了Linux上低级USB通信的表面,但希望这篇文章能让你了解可能性。无论你是在逆向工程一个神秘设备,构建自定义驱动,还是仅仅满足你的好奇心,理解低级USB打开了一个可能性的世界。

记住,能力越大,责任越大。明智地使用你新获得的USB能力,愿你的缓冲区始终正确分配!

“在USB的世界里,我们都只是试图找到终点的包。” - 匿名USB爱好者

进一步阅读

如果这次深入探讨激发了你的兴趣,这里有一些资源可以继续你的USB之旅:

祝你玩得开心,愿你的USB冒险没有意外断开连接!