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 线程池作为其计划程序。…...

多米诺骨牌(模拟)
初始化数据结构: 使用一个布尔数组 arr 来表示每个位置是否被占用。初始时所有位置均为 false(未占用)。使用一个 LinkedHashMap(命名为 queue)来记录最近的 R 操作的位置。这个结构可以保持插入顺序,方便后…...

Unity DOTS系列之Struct Change核心机制分析
最近DOTS发布了正式的版本, 我们来分享一下DOTS里面Struct Change机制,方便大家上手学习掌握Unity DOTS开发。 基于ArchType与Chunk的Entity管理机制 我们回顾以下ECS的内存管理核心机制,基于ArchTypeChunk的Entity管理模式。每个Entity不直接存放数据,…...

「数组」定长滑动窗口|不定长滑动窗口 / LeetCode 2461|2958(C++)
目录 概述 1.定长滑动窗口 思路 复杂度 Code 2.不定长滑动窗口 思路 复杂度 Code 总结 概述 在双指针合集中,我们介绍了双指针算法: 「数组」数组双指针算法合集:二路合并|逆向合并|快慢去重|对撞指针 / LeetCode 88|26|11&#…...

【华为】用策略路由解决双出口运营商问题
需求描述 不同网段访问互联网资源时,走不同的出口,即PC1走电信出口,PC2走移动出口。 客户在内网接口下应用策略路由后往往出现无法访问内网管理地址的现象,该举例给出解决办法。 拓扑图 基础配置 #sysname R1 # # interface G…...

第L2周:机器学习|线性回归模型 LinearRegression:1. 简单线性回归模型
本文为🔗365天深度学习训练营 中的学习记录博客原作者:K同学啊 任务: ●1. 通过本文学习LinearRegression简单线形回归模型。 ●2. 模仿本文代码,通过鸢尾花花瓣长度预测花瓣宽度。 一、概念 什么是回归 回归的目的是为了预测&…...

1.5 测试用例
欢迎大家订阅【软件测试】 专栏,开启你的软件测试学习之旅! 文章目录 前言1 测试用例介绍2 测试用例编写3 案例分析 前言 测试用例的设计和编制是软件活动中最重要的工作。本文详细讲解了测试用例的基本概念以及如何编写测试用例。 本篇文章参考黑马程序…...

P1101 单词方阵
1. 题目链接P1101 单词方阵 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) #include <bits/stdc.h> using namespace std; #define endl \n #define int long long int int xx[] {1,1,1,0,0,-1,-1,-1}; int yy[] {1,0,-1,1,-1,1,0,-1}; int vis[110][110]; char a[11…...

通过 OBD Demo 体验 OceanBase 4.3 社区版
本文作者:马顺华 引言 OceanBase 4.3 是一个专为实时分析 AP 业务设计的重大更新版本。它基于LSM-Tree架构,引入了列存引擎,实现了行存与列存数据存储的无缝整合。这一版本不仅显著提升了AP场景的查询性能,同时也确保了TP业务场景…...

浅拷贝和深拷贝(Java 与 JavaScript)
一、Java 浅拷贝和深拷贝 在Java中,浅拷贝和深拷贝的主要区别在于对对象的引用和内容的复制方式。 浅拷贝 Java 的类型有基本数据类型和引用类型,基本数据类型是可以由 CPU 直接操作的类型,无论是深拷贝还是浅拷贝,都是会复制出…...

力扣每日一题 2306.公司命名
做题过程中使用到的java语法: 1.从一个字符串中取出一部分字符串: String str "Hello, World!"; String part str.substring(7); // 从索引7开始到字符串末尾 System.out.println(part); // 输出: World! class Solution { public lo…...