当前位置: 首页 > ops >正文

Linux驱动学习day28(USB驱动,libusb操作)

一、USB设备的枚举过程

状态切换流程图

当我们将一个USB设备插入PC中,host会给USB设备上电,上电之后发出reset信号,在此过程中检测设备的速率类型(低速、全速或高速),这时候设备进入到默认状态(Default State),一个属于默认状态的USB设备其地址为0,以后这个PC机会使用地址0与这个USB设备通信,会访问地址为0的设备,访问其端点0,将新地址发送给USB设备,之后就工作于address模式,PC会使用新地址于之通信。PC配置USB设备,使其工作于某一种配置下(Configuration State),这就是设备的枚举过程。

USB设备插入之后,PC主要做下面几件事:1、获取设备的设备描述符,2、发送新地址给USB设备,3、再次获取设备描述符,4、获取配置描述符,5、设置configuration。

二、LibUSB

可以通过libusb访问USB设备,不需要USB设备端的驱动程序,需要移除原来的驱动程序。然后就可以直接访问USB控制器的驱动程序,使用open/read/write/ioctl/close这些接囗来打开设备、收发数据、关闭设备。libusb封装了更好用的函数,这些函数的使用可以分为5个步骤:

初始化、打开设备、移除原驱动/认领接口、传输、关闭设备。

2.1 libUSB库使用流程

1、打开设备:(libusb_init ==> libusb_get_device_list ==>libusb_get_device_descriptor/libusb_get_config_descriptor(得到所有的描述符) ==> libusb_open ==> libusb_free_config_descriptor ==> libusb_free_device_list)

2、移除原有的驱动程序/认领接口(逻辑上的认领,就是说我已经移除了USB设备驱动,之后使用libusb来操作USB设备)

libusb_set_auto_detach_kernel_driver==>libusb_claim_interface(设置标志位,认领清除驱动)

libusb_detach_kernel_driver==>libusb_claim_interface(直接清除设备驱动)

3、认领接口之后可以发起传输

同步传输:libusb_control_transfer / libusb_bulk_transfer / libusb_interrupt_transfer 启动和等待

异步传输(更灵活,可以启动多个传输,一次性等待这些传输有没有完成):libusb_alloc_transfer(分配一个传输结构体) ==>libusb_fill_control_transfer \ libusb fill bulk transfer \ libusb fill interrupt transfer \ libusb fill iso transfer(填充传输结构体,填充完之后才可以启动传输) ==> libusb_submit_transfer(启动传输) ==> libusb_handle_events_timeout_completed / libusb_handle_events_timeout / libusb_handle_events_completed / libusb_handle_events 等待所有的传输结果
异步传输除了分配设置构造传输libusb_transfer结构体之外,还需要设置一个回调函数,当传输结束之后,该回调函数会被调用。(回调函数是核心)

4、传输完成之后关闭设备

libusb_free_transfer ==> libusb_release_interface ==> libusb_close ==> libusb_exit

2.2 同步传输和异步传输的区别

USB数据传输分为两个步骤,对于读数据,先给设备发出读数据的请求,一段时间后数据返回;对于写数据,先发送数据给设备,一段时间后得到回应。
同步接口的核心在于把上述两个步骤放在一个函数里面。比如想去读取一个USB键盘的数据,给键盘发出读请求后,如果用户一直没有按下键盘,那么读函数会一直等待。
异步接口的核心在于把上述两个步骤分开:使用一个非阻塞的函数启动传输,它会立刻返回;提供一个回调函数用来处理返回结果。

对于多端点的设备,使用同步传输无法同一时刻在同一个线程中访问多个端点,必须等待上一个传输结束之后才能进行下一次传输,或者启动多个线程。使用异步传输,可以先设置好多个传输结构体和传输端点,然后使用libusb_submit_transfer一次性提交所有结构体,同时启动多个传输。

2.3 使用libusb读取鼠标数据

可以从接口描述符得到其设备是否是鼠标,鼠标的描述符如下:

在usb设备里面,所有的输入输出都是站在主机的角度来判断。

主要流程:找到设备的设备描述符,在设备描述符中找到配置描述符,再找到接口描述符,看看接口描述符的接口类是不是3,子类是不是1,protocol协议是不是2(1是键盘),有没有中断端点。

2.3.1 鼠标上报的数据信息

第0个字节的第1位代表鼠标左键按下,等等。

内核源码的定义:

2.3.2 同步读取代码

#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libusb.h>int main(int argc , char * argv[])
{int err , i , interface_num , endpoint , ep , transferred;int find_mouse = 0;int num_dev;libusb_device *dev, **dev_list; struct libusb_config_descriptor *config_desc;struct libusb_device_handle *dev_handle = NULL;/* libusb_init */err = lisusb_init(NULL);if (err < 0) {fprintf(stderr, "failed to initialise libusb %d - %s\n", err, libusb_strerror(err));exit(1);}/* get_device_list */if((num_dev = libusb_get_device_list(NULL, &dev_list)) < 0 ){fprintf(stderr, "failed to libusb_get_device_list libusb %d - %s\n", err, libusb_strerror(err));exit(1);}/* for each device get config descriptor */for(i = 0 ; i < num_dev ; i++){dev = dev_list[i];err = libusb_get_config_descriptor(dev, 0, &config_desc);if (err) {fprintf(stderr, "could not get configuration descriptor\n");continue;}fprintf(stdout, "libusb_get_config_descriptor() ok\n");/* parse interface descriptor, find usb mouse */        for(interface_num = 0 ; interface_num < config_desc->bNumInterfaces ; i++){const struct libusb_interface_descriptor *intf_desc = &config_desc->interface[interface].altsetting[0];// interface_num = intf_desc->bInterfaceNumber;if (intf_desc->bInterfaceClass != 3 || intf_desc->bInterfaceProtocol != 2)continue;else{/* find usb mouse interrupt endpoint */for(ep = 0 ; endpoint < intf_desc->bNumEndpoints ; ep++){if ((intf_desc->endpoint[ep].bmAttributes & 3) == LIBUSB_TRANSFER_TYPE_INTERRUPT ||(intf_desc->endpoint[ep].bEndpointAddress & 0x80) == LIBUSB_ENDPOINT_IN) {/* find input eps interrupt */endpoint = intf_desc->endpoint[ep].bEndpointAddress;find_mouse = 1;break;}}}if(find_mouse)break;}if(find_mouse)break;}/* open device */if(find_mouse){err = libusb_open(dev, &dev_handle);if (err) {fprintf(stderr, "could not get configuration descriptor\n");continue;}}/* free device list */libusb_free_device_list(devs, 1);/* claim interface */libusb_set_auto_detach_kernel_driver(dev_handle, 1); err = libusb_claim_interface(dev_handle, interface_num);if (err) {fprintf(stderr, "could not libusb_claim_interface()\n");continue;}/* libusb_interrupt_transfer */while(1){/* set libusb_transfer struct *//* handle , interrupt endpoint , data buffer , size , really size , timeout ms*/err = libusb_interrupt_transfer(dev_handle, endpoint, buffer, 16, &transferred, 5000);if (!err) {/* parser data */printf("%04d datas: ", count++);for (int i = 0; i < transferred; i++){printf("%02x ", buffer[i]);}printf("\n");} else if (err == LIBUSB_ERROR_TIMEOUT){fprintf(stderr, "libusb_interrupt_transfer timout\n");} else {fprintf(stderr, "libusb_interrupt_transfer err : %d\n", err);exit(1);}}/* libusb close */libusb_release_interface(dev_handle, interface_num);libusb_close(dev_handle);libusb_exit(NULL);}

2.4 上机实验(交叉编译)

在开发板上进行实验需要交叉编译libusb库

2.4.1 交叉编译阶段

解压库文件

sudo apt-get install libtool

cd libusb 

./autogen.sh 

./configure --host=gcc前缀

make and make install

2.4.2 安装库、头文件到工具链里

gcc xxx.c -o xxx -v 可以找到头文件

gcc xxx.c -o xxx lusb -v 可以找到在哪里寻找相关的库

http://www.xdnf.cn/news/17824.html

相关文章:

  • RabbitMQ核心架构与应用
  • DeepSeek-V2:一种强大、经济且高效的混合专家语言模型
  • 区块链技术原理(13)-以太坊燃料费Gas
  • 【数据结构初阶】--排序(三):冒泡排序、快速排序
  • 旋钮键盘项目---foc讲解(开环)
  • 基于WSL搭建Ubuntu 22.04.x LTS开发环境
  • 102、【OS】【Nuttx】【周边】文档构建渲染:安装 Esbonio 服务器
  • Codeforces 无路可走
  • Git代码版本管理
  • 一文打通 AI 知识脉络:大语言模型等关键内容详解
  • Python基础-数据结构
  • 【部署K8S集群】 1、安装前环境准备配置
  • 重塑工业设备制造格局:明远智睿 T113-i 的破局之道
  • 基于多模型的零售销售预测实战指南
  • Spring IOC容器在Web环境中的启动奥秘:深入源码解析
  • 从 LLM 到自主 Agent:OpenCSG 打造开源 AgenticOps 生态
  • 云原生俱乐部-k8s知识点归纳(4)
  • EhViewer安卓ios全版本类下载安装工具的完整路径解析
  • OpenTelemetry WebSocket 监控终极方案:打通最后一公里
  • TCP传输层协议(4)
  • 攻防世界—fakebook(两种方法)
  • Java集合Map与Stream流:Map实现类特点、遍历方式、Stream流操作及Collections工具类方法
  • 集合车位租售、充电桩共享、二手市场、便民服务的家政服务平台,带源码
  • STM32的PWM
  • Linux网络基础概念
  • NAT 和 PNAT
  • AI提高投放效率的核心策略
  • 使用原生css实现word目录样式,标题后面的...动态长度并始终在标题后方(生成点线)
  • JUC LongAdder并发计数器设计
  • 优先级反转问题