Linux中libusb读写CDC类型USB设备的代码及解析
2024-07-30
154
前言
在Linux中对USB类型的CDC设备,默认识别为一个USBTTY设备,也就是串口设备,可以使用串口通讯的方式进行通讯,但在USB固件开发时,CDC类型的设备更容易实现,参考的代码也更多,实际并不是一个串口设备,或者想通过USB的EndPoint调用libusb的API直接进行通讯。
参考代码
以下是在linux中直接调用libusb进行USB通讯的参考代码:
/*
* This is a simple example to communicate with a CDC-ACM USB device
* using libusb.
*/
#include unistd.h
#include stdlib.h
#include stdio.h
#include errno.h
#include libusb-1.0/libusb.h
/* You may want to change the VENDOR_ID and PRODUCT_ID
* depending on your device.
*/
#define VENDOR_ID 0x2341 // Arduino LLC
#define PRODUCT_ID 0x0034 // Arduino Leonardo
#define ACM_CTRL_DTR 0x01
#define ACM_CTRL_RTS 0x02
/* We use a global variable to keep the device handle
*/
static struct libusb_device_handle *devh = NULL;
/* The Endpoint address are hard coded. You should use lsusb -v to find
* the values corresponding to your device.
*/
static int ep_in_addr = 0x83;
static int ep_out_addr = 0x02;
void write_char(unsigned char c)
{
/* To send a char to the device simply initiate a bulk_transfer to the
* Endpoint with address ep_out_addr.
*/
int actual_length;
if (libusb_bulk_transfer(devh, ep_out_addr, c, 1,
actual_length, 0) 0) {
fprintf(stderr, Error while sending char\n);
}
}
int read_chars(unsigned char * data, int size)
{
/* To receive characters from the device initiate a bulk_transfer to the
* Endpoint with address ep_in_addr.
*/
int actual_length;
int rc = libusb_bulk_transfer(devh, ep_in_addr, data, size, actual_length,
1000);
if (rc == LIBUSB_ERROR_TIMEOUT) {
printf(timeout (%d)\n, actual_length);
return -1;
} else if (rc 0) {
fprintf(stderr, Error while waiting for char\n);
return -1;
}
return actual_length;
}
int main(int argc, char **argv)
{
int rc;
/* Initialize libusb
*/
rc = libusb_init(NULL);
if (rc 0) {
fprintf(stderr, Error initializing libusb: %s\n, libusb_error_name(rc));
exit(1);
}
/* Set debugging output to max level.
*/
libusb_set_debug(NULL, 3);
/* Look for a specific device and open it.
*/
devh = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
if (!devh) {
fprintf(stderr, Error finding USB device\n);
goto out;
}
/* As we are dealing with a CDC-ACM device, its highly probable that
* Linux already attached the cdc-acm driver to this device.
* We need to detach the drivers from all the USB interfaces. The CDC-ACM
* Class defines two interfaces: the Control interface and the
* Data interface.
*/
for (int if_num = 0; if_num 2; if_num++) {
if (libusb_kernel_driver_active(devh, if_num)) {
libusb_detach_kernel_driver(devh, if_num);
}
rc = libusb_claim_interface(devh, if_num);
if (rc 0) {
fprintf(stderr, Error claiming interface: %s\n,
libusb_error_name(rc));
goto out;
}
}
/* Start configuring the device:
* - set line state
*/
rc = libusb_control_transfer(devh, 0x21, 0x22, ACM_CTRL_DTR | ACM_CTRL_RTS,
0, NULL, 0, 0);
if (rc 0) {
fprintf(stderr, Error during control transfer: %s\n,
libusb_error_name(rc));
}
/* - set line encoding: here 9600 8N1
* 9600 = 0x2580 ~ 0x80, 0x25 in little endian
*/
unsigned char encoding[] = { 0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08 };
rc = libusb_control_transfer(devh, 0x21, 0x20, 0, 0, encoding,
sizeof(encoding), 0);
if (rc 0) {
fprintf(stderr, Error during control transfer: %s\n,
libusb_error_name(rc));
}
/* We can now start sending or receiving data to the device
*/
unsigned char buf[65];
int len;
while(1) {
write_char(t);
len = read_chars(buf, 64);
buf[len] = 0;
fprintf(stdout, Received: \%s\\n, buf);
sleep(1);
}
libusb_release_interface(devh, 0);
out:
if (devh)
libusb_close(devh);
libusb_exit(NULL);
return rc;
}
代码解析
以上代码,初始化init和open都与正常调用libusb设备一样,不同的是如下代码:
for (int if_num = 0; if_num 2; if_num++) {
if (libusb_kernel_driver_active(devh, if_num)) {
libusb_detach_kernel_driver(devh, if_num);
}
rc = libusb_claim_interface(devh, if_num);
if (rc 0) {
fprintf(stderr, Error claiming interface: %s\n,
libusb_error_name(rc));
goto out;
}
}
由于我们要使用CDC-ACM设备,很可能Linux已经将cdc-acm驱动程序连接到此设备,我们需要从所有USB接口上分离驱动程序。这里CDC-ACM类定义了两个接口:Control接口和数据接口。所以,这里循环遍历两个接口,将接口已经配置的内核驱动进行分离,然后claim接口就可以调用libusb与设备进行通讯了。
然后,再调用libusb_control_transfer对串口参数进行配置(如果需要)后,就可以试用libusb_bulk_transfer进行数据传输了。
更新于:3个月前赞一波!1
相关文章
- 【说站】java代码块的执行顺序是什么
- 【说站】javascript字符串类型的转换
- 【说站】php上传文件代码
- 【说站】java泛型中类型擦除的转换
- 设计模式之高质量代码
- 【说站】MySQL的五种索引类型极其特点
- 【说站】java求圆的面积代码
- 【说站】java内存屏障有哪些类型
- 【说站】Python代码中编译是什么
- 【说站】java语言代码大全
- 【说站】python代码提速有哪些方法
- 【说站】Java如何创建类型实例
- iOS 图片压缩方法的示例代码
- php语法技巧代码实例
- PHP平滑关闭/重启的实现代码
- PHP实现生成二维码代码展示
- 谷歌的代码即政策允许机器人编写自己的代码
- 自研、好用、够快、稳定、代码可读性强的ORM
- git commit 代码提交规范
- PHP 代码复用机制 trait
文章评论
评论问答