ftdi_sio驱动学习笔记 3 - 端口操作
目录
1. ftdi_port_probe
1.1 私有数据结构ftdi_private
1.2 特殊probe处理
1.3 确定FTDI设备类型
1.4 确定最大数据包大小
1.5 设置读取延迟时间
1.6 初始化GPIO
1.6.1 使能GPIO
1.6.2 添加到系统
1.6.2.1 设置GPIO控制器的基本信息
1.6.2.2 设置GPIO控制器的元信息
1.6.3 GPIO实例
2. ftdi_gpio_remove
int (*port_probe)(struct usb_serial_port *port): 端口探测函数ftdi_port_probe,用于初始化单个端口。
void (*port_remove)(struct usb_serial_port *port): 端口移除函数ftdi_port_remove
ftdi_port_remove,用于清理单个端口。
1. ftdi_port_probe
1.1 私有数据结构ftdi_private
这个结构是FTDI设备特有的,其实就是用来在驱动中传递参数用的。在这个结构体中定义了FTDI设备私有的一些变量。
struct ftdi_private *priv;
priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL);
if (!priv)return -ENOMEM;
这个私有数据是通过usb_set_serial_port_data放到port的私有数据保存。
usb_set_serial_port_data(port, priv);
1.2 特殊probe处理
和上一节一样,端口的probe处理也是有特殊的情况。这个结构体是通过port->serial传递的。
const struct ftdi_quirk *quirk = usb_get_serial_data(port->serial);
if (quirk && quirk->port_probe)quirk->port_probe(priv);
1.3 确定FTDI设备类型
result = ftdi_determine_type(port);
if (result)goto err_free;
该类型是通过设备描述符中的bcdDevice来区别的。
version = le16_to_cpu(udev->descriptor.bcdDevice);
以FT4232H为例,该值可以在设备属性的硬件ID中查看到,如下图REV_0800
case 0x800:priv->chip_type = FT4232H;break;
通过当前接口初始化通道编号,这只对多串口有意义(比如FT2232H和FT4232H)
#define CHANNEL_A 1
#define CHANNEL_B 2
#define CHANNEL_C 3
#define CHANNEL_D 4ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
priv->channel = CHANNEL_A + ifnum;
而baud_base是指波特率产生器的参考时钟,默认设置的是H系列的值:120M的二分频。
priv->baud_base = 120000000 / 2;
最后是向设备节点发送消息,表明检测到了哪种类型的FTDI芯片。
dev_info(&udev->dev, "Detected %s\n", ftdi_chip_name[priv->chip_type]);
在命令“sudo dmesg”返回结果中可以找到这句信息内容,例如:
[16005.322690] usb 2-1: Detected FT4232H
1.4 确定最大数据包大小
ftdi_set_max_packet_size(port);
这是从设备的端点描述符中获取设备端点最大数据包大小。这一步更像是检查FT232R的端点数据包最大值被客制为0的情况,当被改为0时数据包大小设置为64字节。
1.5 设置读取延迟时间
if (read_latency_timer(port) < 0)priv->latency = 16;
write_latency_timer(port);
这段代码的作用是确保设备的读取延迟时间被正确设置,如果无法获取当前的延迟时间,则会使用一个默认值进行设置。
获取这个参数是通过函数usb_control_msg_recv实现的。
int usb_control_msg_recv(struct usb_device *dev, __u8 endpoint, __u8 request,__u8 requesttype, __u16 value, __u16 index,void *driver_data, __u16 size, int timeout,gfp_t memflags)
对应的调用:
rv = usb_control_msg_recv(udev, 0, FTDI_SIO_GET_LATENCY_TIMER_REQUEST,FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE, 0,priv->channel, &buf, 1, WDR_TIMEOUT,GFP_KERNEL);
其中参数request和requesttype在ftdi_sio.h中有定义:
#define FTDI_SIO_GET_LATENCY_TIMER 0x0a /* Get the latency timer */
#define FTDI_SIO_GET_LATENCY_TIMER_REQUEST FTDI_SIO_GET_LATENCY_TIMER
#define FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE 0xC0
这属于FTDI定义的命令。
同样,写这个参数的命令是:
#define FTDI_SIO_SET_LATENCY_TIMER 9 /* Set the latency timer */
#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST FTDI_SIO_SET_LATENCY_TIMER
#define FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE 0x40rv = usb_control_msg(udev,usb_sndctrlpipe(udev, 0),FTDI_SIO_SET_LATENCY_TIMER_REQUEST,FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,l, priv->channel,NULL, 0, WDR_TIMEOUT);
1.6 初始化GPIO
这部分是利用了FTDI的CBUS实现USB转GPIO的功能。这一步是受内核配置CONFIG_GPIOLIB控制的,如果没有使能GPIOLIB,则没有这部分功能。
1.6.1 使能GPIO
由于不是所有的FTDI芯片都支持CBUS功能,所以前面有做芯片类型的判断,这里会引用这个变量分别处理。
switch (priv->chip_type) {
case FT232H:result = ftdi_gpio_init_ft232h(port);break;
case FT232R:result = ftdi_gpio_init_ft232r(port);break;
case FTX:result = ftdi_gpio_init_ftx(port);break;
default:return 0;
}
只有3类芯片支持CBUS功能,这里3个分支都是从芯片(或外置EEPROM)中读取CBUS配置信息使能CBUS脚。例如FT232H,一共有4个CBUS脚,分别为AC5,AC6,AC8和AC9,从eeprom的地址0x1a读入4个字节
ret = ftdi_read_eeprom(port->serial, buf, 0x1a, 4);
if (ret < 0)goto out_free;
每个GPIO由4位表示方向和电平。
/*
* FT232H CBUS Memory Map
*
* 0x1a: X- (upper nibble -> AC5)
* 0x1b: -X (lower nibble -> AC6)
* 0x1c: XX (upper nibble -> AC9 | lower nibble -> AC8)
*/
cbus_config = buf[2] << 8 | (buf[1] & 0xf) << 4 | (buf[0] & 0xf0) >> 4;
最后更新priv->gc.ngpio和gpio_altfunc,对应GPIO使能的话,gpio_altfunc对应的位清零
priv->gc.ngpio = 4;
priv->gpio_altfunc = 0xff;for (i = 0; i < priv->gc.ngpio; ++i) {if ((cbus_config & 0xf) == FTDI_FTX_CBUS_MUX_GPIO)priv->gpio_altfunc &= ~BIT(i);cbus_config >>= 4;
}
1.6.2 添加到系统
这一步是通过标准的gpiochip的驱动API函数gpiochip_add_data实现。
priv->gc.label = "ftdi-cbus";
priv->gc.request = ftdi_gpio_request;
priv->gc.get_direction = ftdi_gpio_direction_get;
priv->gc.direction_input = ftdi_gpio_direction_input;
priv->gc.direction_output = ftdi_gpio_direction_output;
priv->gc.init_valid_mask = ftdi_gpio_init_valid_mask;
priv->gc.get = ftdi_gpio_get;
priv->gc.set = ftdi_gpio_set;
priv->gc.get_multiple = ftdi_gpio_get_multiple;
priv->gc.set_multiple = ftdi_gpio_set_multiple;
priv->gc.owner = THIS_MODULE;
priv->gc.parent = &serial->interface->dev;
priv->gc.base = -1;
priv->gc.can_sleep = true;result = gpiochip_add_data(&priv->gc, port);
if (!result)priv->gpio_registered = true;
第一个参数priv->gc是配置好的`gpio_chip`结构体,第二个参数是该gpio_chip的私有数据,即实现接口参数的传递,例如:
static int ftdi_gpio_request(struct gpio_chip *gc, unsigned int offset)
{struct usb_serial_port *port = gpiochip_get_data(gc);
1.6.2.1 设置GPIO控制器的基本信息
- priv->gc.label: 设置GPIO控制器的标签为"ftdi-cbus"。
- priv->gc.request: 设置请求GPIO引脚的函数为`ftdi_gpio_request`,用于在使用前申请GPIO资源。
- priv->gc.get_direction: 设置获取GPIO引脚方向(输入或输出)的函数为`ftdi_gpio_direction_get`。
- priv->gc.direction_input: 设置将GPIO引脚配置为输入模式的函数为`ftdi_gpio_direction_input`。
- priv->gc.direction_output: 设置将GPIO引脚配置为输出模式的函数为`ftdi_gpio_direction_output`。
- priv->gc.init_valid_mask: 设置初始化有效掩码的函数为`ftdi_gpio_init_valid_mask`,用于确定哪些GPIO引脚可以被使用。
- priv->gc.get: 设置读取GPIO引脚状态的函数为`ftdi_gpio_get`。
- priv->gc.set: 设置设置GPIO引脚状态的函数为`ftdi_gpio_set`。
- priv->gc.get_multiple: 设置批量读取GPIO引脚状态的函数为`ftdi_gpio_get_multiple`。
- priv->gc.set_multiple: 设置批量设置GPIO引脚状态的函数为`ftdi_gpio_set_multiple`。
1.6.2.2 设置GPIO控制器的元信息
- priv->gc.owner: 设置拥有者为当前模块,表明这个GPIO控制器是由当前驱动程序管理的。
- priv->gc.parent: 设置父设备为`serial->interface->dev`,这通常是一个USB设备或串口设备,表明GPIO控制器与之关联。
- priv->gc.base: 设置GPIO引脚的起始编号为-1,实际值将在注册时由系统分配。
- priv->gc.can_sleep: 设置为`true`表示这个GPIO控制器可以在睡眠状态下工作。
1.6.3 GPIO实例
例如使用FT232H,使用FT_PROG设置AC9为I/O Mode
在Linux中可以看到多一个gpiochip512的文件夹
/sys/class/gpio$ ls
export gpiochip512 unexport
/sys/class/gpio/gpiochip512$ ls
base device label ngpio power subsystem uevent
/sys/class/gpio/gpiochip512$ cat label
ftdi-cbus
/sys/class/gpio/gpiochip512$ cat ngpio
4
/sys/class/gpio/gpiochip512$ cat base
512
可以看到一共分配了4个gpio,因为AC9是第四个GPIO,所以其GPIO编号应该为512 + 3 = 515.
/sys/class/gpio# echo "515" > /sys/class/gpio/export
/sys/class/gpio# ls
export gpio515 gpiochip512 unexport
注意,这里FT_PROG虽然只配置了一个GPIO,但是驱动层还是按照4个GPIO分配,如果暴露512可以看到返回错误:
/sys/class/gpio# echo "512" > /sys/class/gpio/export
bash: echo: 写入错误:无效的参数
2. ftdi_gpio_remove
这个函数将gpio移除和释放内存。
相关文章:

ftdi_sio驱动学习笔记 3 - 端口操作
目录 1. ftdi_port_probe 1.1 私有数据结构ftdi_private 1.2 特殊probe处理 1.3 确定FTDI设备类型 1.4 确定最大数据包大小 1.5 设置读取延迟时间 1.6 初始化GPIO 1.6.1 使能GPIO 1.6.2 添加到系统 1.6.2.1 设置GPIO控制器的基本信息 1.6.2.2 设置GPIO控制器的元信息…...
[leetcode]39_组合总和_给定数组且数组可重复
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的数字可以无限制重复被选取。说明: 所有数字(包括 target)都是正整数。 解集不能包含重复的组合…...

【笔记】第三节 组织与性能
3.1 基本成分 3.2 微观组织特征 0.6-0.8C%碳素钢的组织为珠光体和少量的铁素体。 如何把组织和性能联系起来?德国克虏伯公司的研究——珠光体片间距与渗碳体片层厚度成比例: t s 0 ( ρ 15 ( C % ) − 1 ) ts_0(\frac{\rho}{15(C\%)}-1) ts0(15(C%)…...

数据库——sql语言学习 查找语句
一、什么是sql SQL是结构化查询语言(Structured Query Language)的缩写,它是一种专门为数据库设计的操作命令集,用于管理关系数据库管理系统(RDBMS)。 二、查找相关语句 首先,我们已经设…...
【计算机网络 - 基础问题】每日 3 题(二十三)
✍个人博客:Pandaconda-CSDN博客 📣专栏地址:http://t.csdnimg.cn/fYaBd 📚专栏简介:在这个专栏中,我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话,欢迎点赞👍收藏&…...

JPA + Thymeleaf 增删改查
一、 什么是 Thymeleaf JPA(Java Persistence API):是一种用于对象关系映射(ORM)的 Java 规范,它简化了数据库操作,使开发者可以以面向对象的方式处理数据存储。通过定义实体类和数据访问接口&a…...
Android常用C++特性之std::this_thread
声明:本文内容生成自ChatGPT,目的是为方便大家了解学习作为引用到作者的其他文章中。 std::this_thread 是 C11 标准库中的一个命名空间,提供了一组与当前线程(即调用这些函数的线程)相关的操作。通过 std::this_threa…...
成语700词(31~45组)
目录 31.对待错误的态度(12 个)32.改变与不变(19 个)33.顺势造势(6 个)34.自然会发生(6 个)35.提早准备和补救(11 个)36.办公、管理相关(8 个)37.空谈与虚幻(8 个)38.来者众多(11 个)39.人多热闹(6)40.好坏掺杂(7 个)41.流行与名声(14 个)42.与传播、传闻…...

vue3组件通信(组合式API)
vue3组件通信(组合式API) vue3组件通信,采用组合式API。选项式API,参看官网 Vue3组件通信和Vue2的区别: 移出事件总线,使用mitt代替。 vuex换成了pinia。把.sync优化到了v-model里面了。把$listeners所…...

从预测性维护到智能物流:ARM边缘计算控制器的工业实践
工业4.0时代的到来,边缘计算技术成为连接物理世界与数字世界的桥梁。ARM架构的边缘计算控制器凭借其低功耗、高能效和灵活性等特点,在工业自动化领域展现出巨大潜力。本文将通过几个实际应用案例来探讨ARM边缘计算控制器是如何提升生产线效率和安全性的&…...

2024年汉字小达人区级自由报名备考冲刺:最新问题和官模题练一练
今天是2024年第十一届汉字小达人的区级自由报名活动的第二天。 我们继续回答几个关于汉字小达人的最新问题,做几道2024年官方模拟题,帮助孩子们少走弯路,再冲刺一般,更精准地备考2024年汉字小达人。 【温馨提示】本专题在比赛期…...

Linux相关概念和重要知识点(8)(操作系统、进程的概念)
1.操作系统(OS) (1)基本结构的认识 任何计算机系统都包含一个基本的程序集合,用于实现计算机最基本最底层的操作,这个软件称为操作系统。操作系统大部分使用C语言编写,少量使用汇编语言。 从…...

测序技术--组蛋白甲基化修饰、DNA亲和纯化测序,教授(优青)团队指导:从实验设计、结果分析到SCI论文辅助
组蛋白甲基化修饰工具(H3K4me3 ChIP-seq)组蛋白甲基化类型也有很多种,包括赖氨酸甲基化位点H3K4、H3K9、H3K27、H3K36、H3K79和H4K20等。组蛋白H3第4位赖氨酸的甲基化修饰(H3K4)在进化上高度保守,是被研究最多的组蛋白修饰之一。 DNA亲和纯化测序 DNA亲…...

Llama 3.2来了,多模态且开源!AR眼镜黄仁勋首批体验,Quest 3S头显价格低到离谱
如果说 OpenAI 的 ChatGPT 拉开了「百模大战」的序幕,那 Meta 的 Ray-Ban Meta 智能眼镜无疑是触发「百镜大战」的导火索。自去年 9 月在 Meta Connect 2023 开发者大会上首次亮相,短短数月,Ray-Ban Meta 就突破百万销量,不仅让马…...

软考高级:SOA 和微服务 AI 解读
概念讲解 SOA(面向服务架构)和微服务虽然都是服务架构的设计模式,但它们的侧重点和实现方式有很大区别。为了帮助你理解这两个概念,我们可以从生活中的例子、概念本身的讲解以及记忆方法三方面入手。 生活化例子 **SOA…...

【每天学个新注解】Day 6 Lombok注解简解(五)—@SneakyThrows
SneakyThrows 简化异常处理 并不建议日常开发中通过此注解解决异常捕获问题!!! 允许方法抛出检查型异常而无需显式声明或捕获这些异常。这对于那些不希望在方法签名中声明异常或不愿意编写复杂的 try-catch 块的场景非常有用。 使用 SneakyT…...

C语言 | Leetcode C语言题解之第437题路径总和III
题目: 题解: /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/ //递归遍历树节点,判断是否为有效路径 int dfs(struct TreeNode * root, int ta…...

Linux-TCP重传
问题描述: 应用系统进行切换,包含业务流量切换(即TongWeb主备切换)和MYSQL数据库主备切换。首先进行流量切换,然后进行数据库主备切换。切换后发现备机TongWeb上有两批次慢请求,第一批慢请求响应时间在133…...

Python通过Sqlalchemy框架实现增删改查
目录 简介 什么是SQLAlchemy? SQLAlchemy可以分为两个部分:Core和ORM。 一、首先安装sqlalchemy 二、在配置文件中添加数据库连接信息,我这里是Mysql 三、 创建数据库连接类,我这里是动态读取数据库的表字段,自动…...

windows C++ - 任务计划程序(并发运行时)
如果希望微调并发运行时的现有代码的性能,则任务计划程序会很有用。 无法从通用 Windows 平台 (UWP) 应用获取任务计划程序。 在 Visual Studio 2015 及更高版本中,concurrency::task 类和 ppltasks.h 中的相关类型使用 Windows 线程池作为其计划程序。…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...

JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...