目录
ftdi_sio是Linux内核中对FTDI设备的USB转串口的驱动程序。该源代码可以通过Linux源码下载,例如6.10.9版本的下载地址:
serial - drivers/usb/serial - Linux source code (v6.10.9) - Bootlin
和ftdi_sio有关的源文件是:ftdi_sio.c、ftdi_sio.h和ftdi_sio_ids.h。可以新建一个makefile文件
ifeq ($(KERNELRELEASE), )
KERNELDIR := /lib/modules/$(shell uname -r)/build
PWD :=$(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD)
clean:
rm -rf *.mk .tmp_versions Module.symvers *.mod.c *.o *.ko .*.cmd Module.markers modules.order *.a *.mod
load:
insmod ftdi_sio.ko
unload:
rmmod ftdi_sio
install: default
rmmod ftdi_sio || true
mkdir -p /lib/modules/$(shell uname -r)/kernel/drivers/usb/serial/ || true
cp -f ./ftdi_sio.ko /lib/modules/$(shell uname -r)/kernel/drivers/usb/serial/ || true
modprobe ftdi_sio || true
# depmod -a
uninstall:
rmmod ftdi_sio || true
rm -rf /lib/modules/$(shell uname -r)/kernel/drivers/usb/serial/ftdi_sio.ko || true
# depmod -a
else
obj-m := ftdi_sio.o
endif
命令make是编译生成ftdi_sio.ko,sudo make install是安装。
1. usb串口驱动模块
程序不是直接通过xxx_init和xxx_exit注册和注销的。它是通过通用usb串口设备的接口函数实现的。
module_usb_serial_driver(serial_drivers, id_table_combined);
在内核里面定义的这个接口module_usb_serial_driver和常规的驱动的实现方式是一样的。
#define usb_serial_module_driver(__name, __serial_drivers, __ids) \
static int __init usb_serial_module_init(void) \
{ \
return usb_serial_register_drivers(__serial_drivers, \
__name, __ids); \
} \
module_init(usb_serial_module_init); \
static void __exit usb_serial_module_exit(void) \
{ \
usb_serial_deregister_drivers(__serial_drivers); \
} \
module_exit(usb_serial_module_exit);
对于参数__serial_drivers是一个串口驱动数组
static struct usb_serial_driver * const serial_drivers[] = {
&ftdi_device, NULL
};
这里只有ftdi一种驱动,NULL则表示这个串口驱动数组的结尾。
而参数__ids是USB设备的VID和PID数组。
2. usb串口驱动数组ftdi_device
static struct usb_serial_driver ftdi_device
这个结构体描述FTDI USB串行设备的属性和操作函数指针。它包含了设备的描述信息、端口数量、数据缓冲区大小等硬件特性,以及设备插拔、打开、关闭、控制信号等操作的函数指针。
所有的串口属性和操作都是这个结构体实现的,所以是重点学习的对象。
2.1 设备驱动属性
.driver = {
.owner = THIS_MODULE,
.name = "ftdi_sio",
.dev_groups = ftdi_groups,
},
这段代码是Linux设备驱动程序的一部分,用于注册一个名为"ftdi_sio"的设备驱动程序。其中,.owner
字段表示该驱动程序所属的模块,.name
字段指定了驱动程序的名称,.dev_groups
字段指定了该设备支持的权限组。
static const struct attribute_group ftdi_group = {
.attrs = ftdi_attrs,
.is_visible = ftdi_is_visible,
};
static const struct attribute_group *ftdi_groups[] = {
&ftdi_group,
NULL
};
在 Linux 内核的设备模型中,`attribute_group` 结构体用于组织一组可被用户空间程序访问的设备属性。
- attrs成员
每个 `attribute` 结构体代表一个可读或可写的设备属性,例如设备的状态、配置参数等。这些属性可以通过 `/sys/class/` 目录下的相应文件来访问。
static struct attribute *ftdi_attrs[] = {
&dev_attr_event_char.attr,
&dev_attr_latency_timer.attr,
NULL
};
数组中的每个元素都是一个指向struct attribute
结构体的指针。ftdi_attrs
数组包含了两个设备属性:event_char
和latency_timer
,以及一个空指针NULL
作为结束标志。
这2个属性的定义:
static DEVICE_ATTR_WO(event_char);
static DEVICE_ATTR_RW(latency_timer);
static DEVICE_ATTR_WO(event_char);
:这行代码定义了一个名为event_char
的只写设备属性。DEVICE_ATTR_WO
宏创建了一个只能写入而不能读取的设备属性
static DEVICE_ATTR_RW(latency_timer);
:这行代码定义了一个名为latency_timer
的读写设备属性。DEVICE_ATTR_RW
宏创建了一个可以读取和写入的设备属性。
- is_visible成员
当用户尝试访问设备的属性时,内核会调用 `is_visible` 函数检查这些属性是否应该显示给用户。
总结来说,`ftdi_group` 定义了 FTDI 设备的一组属性及其可见性规则,使得用户空间程序能够通过 sysfs 文件系统接口安全地读取或修改设备状态。
在Ubuntu中可以查到这个设备的这些属性,如下图中的event_char和latency_timer。
:/sys/class/tty/ttyUSB0/device$ ls
driver event_char latency_timer port_number power subsystem tty uevent
2.2 描述符
.description = "FTDI USB Serial Device",
这是一串字符串,当设备插入和移除时在系统log中使用,例如输入命令"sudo dmesg | grep ftdi":
[ 7316.555196] ftdi_sio 2-1:1.0: FTDI USB Serial Device converter detected
[ 7316.564388] ftdi_sio 2-1:1.1: FTDI USB Serial Device converter detected
[ 7316.573474] ftdi_sio 2-1:1.2: FTDI USB Serial Device converter detected
[ 7316.582621] ftdi_sio 2-1:1.3: FTDI USB Serial Device converter detected
可以改一下这个字符串看一下效果。
2.3 端口配置
.num_ports = 1,
.bulk_in_size = 512,
.bulk_out_size = 256,
- num_ports设定设备将拥有的不同端口的数量。这通常指的是USB设备上可连接外设的物理接口数量。
- bulk_in_size设定每个批量输入端点的缓冲区大小,单位为字节。如果设置为0,则表示使用端点本身的默认大小。
- bulk_out_size设定每个批量输出端点的缓冲区大小,单位为字节。同样,如果设置为0,则表示使用端点本身的默认大小。
2.4 操作接口函数
这些函数分2种情况,一种是使用usb串口标准的函数实现,而其他大部分函数是FTDI设备需要实现的。usb串口标准的函数实现有下面4种:
.throttle = usb_serial_generic_throttle,
.unthrottle = usb_serial_generic_unthrottle,
.tiocmiwait = usb_serial_generic_tiocmiwait,
.get_icount = usb_serial_generic_get_icount,
- throttle: 数据流控制函数
- unthrottle: 取消数据流控制
- tiocmiwait: 等待调制解调器状态变化
- get_icount: 获取串口的中断计数信息,并将这些信息存储在
icount
指向的结构体中