首页 程序笔记 Linux中libusb读写CDC类型USB设备的代码及解析

Linux中libusb读写CDC类型USB设备的代码及解析

前言

在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进行数据传输了。

1

站心网

前言 在Linux中对USB类型的CDC设备,默认识别为一个USBTTY设备,也就是串口设备,可以使用串口通讯的方式进..

为您推荐

代码照进现实:对公司管理策略的技术性解构

上学的时候觉得计算机专业的一些理论晦涩难懂,跟现实世界的关联太少,每当遇到一些精妙的设计时都会发出一种感叹:究竟是什么脑袋才能想出这么有意思的东西。一晃工作十年,阅历渐丰,隐约发现其实社会中的一些现象..

编写优秀 CSS 代码的 8 个策略

编写基本的CSS和HTML是我们作为Web开发人员学习的首要事情之一。然而,我遇到的很多应用程序显然没有人花时间真正考虑前端开发的长久性和可维护性。我认为这主要是因为许多开发人员对组织CSS / HTML和JavaScript的策..

天天写业务代码,如何成为技术大牛?

不管是开发、测试、运维,每个技术人员心理多多少少都有一个成为技术大牛的梦,毕竟"梦想总是要有的,万一实现了呢"!正是对技术梦的追求,促使我们不断地努力和提升自己。然而"梦想是美好的,现实却是残酷的",很多..

如何选择更适合你的 Linux 发行版?

很多人经常会问我这样一个问题:“嘿,你是用Linux的,对吧?我应该使用Linux的哪个版本?我有这个朋友推荐我_____,你觉得怎么样?”我通常会以这样的问题回复:这取决于你想做什么?今天我决定写一篇关于如何选择L..

记我经历的一次公司破产经历,一行代码害死一家公司

前言这是一篇亲身经历的真实记录,事情发生在2010年。狗血剧情一再上演,使我的程序员生涯变得跌宕起伏,也从中学到了很多。在写这篇文章之前,我还专门去查了这家公司的资料。有如下事实:1.官网已经打不开了。2.天..

如何处理前任程序员留下的代码

作为软件工程师不可避免会遇到的一个场景是:我们在改变或添加一个功能到不是我们创建的、我们不熟悉的、与我们负责的系统部分无关的代码中时,会遇到麻烦。虽然这可能会是一个繁琐而艰巨的任务,但是由于使用其他开..

对码农而言什么样的代码才能叫做好代码?

好的代码,就像是好的笑话——无需解释就能让别人明白。如果你的代码能够做到不解自明,在大多数时候,你根本无需为其配备说明文档。好的代码,就像是一辆配备了优秀音响和杯架的汽车,这辆车在行驶到最高速度的时候..

当一个程序员写不出代码了,该怎么办?

你已经对着电脑n个小时了。不知道该写什么代码,或者一种摔键盘的冲动正在你的胸中酝酿。咖啡一杯接着一杯。不敢再喝了,因为搞不好要有副作用了,心跳加速,身体不由自主地颤抖,出冷汗,但还是无法产出任何代码。..

float 与 double 类型区别

float 单精度浮点数在机内占 4 个字节,用 32 位二进制描述。double 双精度浮点数在机内占 8 个字节,用 64 位二进制描述。浮点数在机内用指数型式表示,分解为:数符,尾数,指数符,指数四部分。数符占 1 位二进制..

VS创建.NET Core项目使用Docker方式部署到Linux服务器

在 Visual Studio(VS) 中,使用 Docker 方式部署 .NET Core 项目 到 Linux 服务器,可以简化环境管理并提高部署效率。以下是完整教程:1. 在 VS 创建 .NET Core 项目并启用 Docker新建 ASP.NET Core 项目打开 Visu..

ASP.NET MVC中有四种过滤器类型

在ASP.NET MVC中有四种过滤器类型Action1、在ASP.NET MVC项目中,新建文件夹Filter,然后新建类MyCustormFilter,继承自ActionFilterAttribute类,我们来看下ActionFilterAttribute类有如下四个方法,从命名我应该就..

ASP.NET MVC最常用的设计模式代码示例

ASP.NET MVC 是一个基于分层架构的框架,其核心架构本身已经实现了 MVC 模式(Model-View-Controller)。除了 MVC 模式,开发者在使用 ASP.NET MVC 开发应用时,通常会结合其他设计模式以提高代码的可维护性、可扩展..

分享5个开源的.NET Excel读写操作库

本文给大家分享 5 个开源的 .NET Excel 读写操作库,它们广泛用于处理 Excel 文件,包括读取、写入、导入和导出数据。1. EPPlus简介:EPPlus 是功能强大的 .NET 库,用于创建和读取 Excel 文件(.xlsx 和 .xlsm 格式..

10款.NET开发中推荐的代码分析和质量工具

以下是10款.NET开发中常用的代码分析和质量工具列表,以及它们的主要功能和使用场景:1. SonarQube简介:一个流行的开源静态代码分析平台,用于检测代码中的漏洞、错误、技术债务等问题。主要功能:支持代码质量监测..

.NET C# EntityFramework(EF)连接SQLite代码示例

在.NET C#中使用Entity Framework(EF)连接SQLite数据库是一种常见的做法,可以有效地管理和操作数据。以下是一个简单的示例代码,展示了如何使用EF Core连接到SQLite数据库并执行基本的CRUD操作。首先,确保你已经..

设计模式之高质量代码

0,什么是高质量代码我觉得回答这个问题,应该从两个方面考虑。从业务角度考虑。首先,在公司开发一款软件,应该是业务在驱动。所以,从这个角度来说,代码第一个应该满足的是业务需求,如果连最基本的业务需求都满..

CLS 问题:超过 0.1(桌面设备) 是什么意思?

在网页设计和开发中,CLS(Cumulative Layout Shift)是指页面布局的累计偏移量。当一个用户与页面互动时,如果页面的某些元素突然改变位置或大小,导致整个布局发生偏移,就会产生布局偏移。这种偏移可能会影响用户..

C#发送邮件代码简洁示例(附源码下载)

C#发送邮件,主要使用的是System.Net.Mail命名空间下的方法实现,方法很简单,短短十几行代码即可完成发送,具体代码如下。 try { MailMessage myMail = new MailMessage(); myMail.From = new MailAddress..

AutoMapper.AutoMapperMappingException”类型的异常在 AutoMapper.dll 中发生,但未在用户代码中进行处理

今天修改别人的代码抛出了这样的异常: AutoMapper.AutoMapperMappingException”类型的异常在 AutoMapper.dll 中发生,但未在用户代码中进行处理。进行了调试,往下走的时候直接报错了,百度之~中文网站上没..

c#无损压缩图片代码,可设置压缩质量

之前写过一篇文章《使用htmlagilitypack+xpath抓取网页内容示例》,提到使用htmlagilitypack抓取网页信息。想做一个网络爬虫,但是想把网页上的图片也下载到本地,于是写了下载图片的功能。但是第三方网站上的图片大..

发表回复

返回顶部