让我们花点时间来欣赏一下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冒险没有意外断开连接!