目录
1. ft260_driver_init初始化
1.1 tty设备
1.1.1 申请tty驱动设备
1.1.2 初始化tty驱动程序
1.1.3 注册tty设备
1.2 hid设备
2. ft260_driver_exit注销模块
3. 调试
hid-ft260.c的最底部可以看到该驱动的注册与注销接口的申明。
module_init(ft260_driver_init);
module_exit(ft260_driver_exit);
MODULE_DESCRIPTION("FTDI FT260 USB HID to I2C host bridge and TTY driver");
MODULE_AUTHOR("Michael Zaidman <michael.zaidman@gmail.com>");
MODULE_LICENSE("GPL v2");
module_init即模块注册接口,当Linux中运行lsmod或modprobe安装驱动时系统会调用这个函数。
module_exit是模块的注销接口,当Linux中运行rmmod卸载驱动时系统会调用这个函数。
MODULE_LICENSE用来指定模块许可协议,一般是GPL,这里的GPL v2表示GNU通用公共许可证第2版(GNU General Public License version 2)。这意味着该模块的源代码必须对所有用户开放,并且任何基于此模块修改或衍生的作品也必须在相同的许可证下发布,保证了软件的自由度和可获得性。这个申明一般是必须的,否则编译时会提示警告。
MODULE_AUTHOR申明作者,不是必须。
MODULE_DESCRIPTION是模块的说明,不是必须。
1. ft260_driver_init初始化
FT260可以作为UART、I2C和GPIO的设备存在,所以在初始化时注册了2个设备。
static int __init ft260_driver_init(void)
{
}
__init是注册模块时固定的写法。
1.1 tty设备
1.1.1 申请tty驱动设备
通过调用tty_alloc_driver
函数,为串口通信(UART)分配一个终端(TTY)驱动。
ft260_tty_driver = tty_alloc_driver(UART_COUNT_MAX,
TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
if (IS_ERR(ft260_tty_driver)) {
pr_err("tty_alloc_driver failed: %d\n",
(int)PTR_ERR(ft260_tty_driver));
return PTR_ERR(ft260_tty_driver);
}
UART_COUNT_MAX
:指定最多可支持的UART设备数量。TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV
:这两个标志合起来表示驱动将以真实原始模式工作(不进行任何字符处理,直接传递数据)且允许动态创建设备文件。
返回的是分配好的tty驱动设备。全局变量ft260_tty_driver:
static struct tty_driver *ft260_tty_driver;
1.1.2 初始化tty驱动程序
ft260_tty_driver->driver_name = "ft260_ser";
ft260_tty_driver->name = "ttyFT";
ft260_tty_driver->major = 0;
ft260_tty_driver->minor_start = 0;
ft260_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
ft260_tty_driver->subtype = SERIAL_TYPE_NORMAL;
ft260_tty_driver->init_termios = tty_std_termios;
ft260_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
ft260_tty_driver->init_termios.c_ispeed = 9600;
ft260_tty_driver->init_termios.c_ospeed = 9600;
tty_set_operations(ft260_tty_driver, &ft260_uart_ops);
这里设置了驱动程序的名称、设备名称、主设备号、从设备号类型和子类型。然后,它初始化了串口的终端属性,包括波特率、数据位、停止位、校验位等。最后,它将操作函数指针设置为ft260_uart_ops
,以便在设备文件操作时调用相应的函数。
driver_name是该设备的name,无特殊设定。
name是该设备在Linux中显示的设备名称,这里配置为ttyFT,在/dev目录内就会有一个类似‘ttyFT0’的终端设备。
major是设备的主设备号,这里设置为0一般是表示该主设备号有系统分配空闲设备号。一般设置为0。
minor_start是设备的从设备号,一般为0起始。
type和subtype分别表示设备的类型和子类型。TTY_DRIVER_TYPE_SERIAL
表示这是一个串口驱动程序,而SERIAL_TYPE_NORMAL
表示这个串口是标准类型的串口。
init_termios设置为tty_std_termios意味着当该串口设备被打开时,它会使用默认的标准终端设置,包括输入输出速度、控制字符等配置,以确保终端能按照常见规则正确工作。
c_cflag初始化一个串口终端的控制模式。具体来说,它设置了以下参数:
- 波特率(baud rate)为9600;
- 数据位(data bits)为8位;
- 奇偶校验(parity)为无;
- 停止位(stop bits)为1位;
- 启用读取(read)和挂断(hang up)操作;
- 忽略modem控制线(local mode)。
c_ispeed和c_ospeed设置输入速度(c_ispeed
)和输出速度(c_ospeed
)为9600波特率。
最后配置操作函数指针ft260_uart_ops是串口驱动的核心部分,串口数据的读写等操作就是这个函数指针结构体实现的。
static const struct tty_operations ft260_uart_ops = {
.open = ft260_uart_open,
.close = ft260_uart_close,
.write = ft260_uart_write,
.write_room = ft260_uart_write_room,
.chars_in_buffer = ft260_uart_chars_in_buffer,
.set_termios = ft260_uart_set_termios,
.hangup = ft260_uart_hangup,
.install = ft260_uart_install,
.cleanup = ft260_uart_cleanup,
.proc_show = ft260_uart_proc_show,
.get_icount = ft260_uart_get_icount,
};
1.1.3 注册tty设备
ret = tty_register_driver(ft260_tty_driver);
if (ret) {
pr_err("tty_register_driver failed: %d\n", ret);
goto err_reg_driver;
}
tty_register_driver
函数用于将ft260_tty_driver
注册到TTY系统中。如果注册失败,会输出错误信息并跳转到err_reg_driver
标签处。
1.2 hid设备
对于GPIO和I2C,这部分属于一个非标准的hid设备,所以这里只注册为hid设备。
ret = hid_register_driver(&ft260_driver);
if (ret) {
pr_err("hid_register_driver failed: %d\n", ret);
goto err_reg_hid;
}
hid_register_driver用于注册一个hid驱动程序,它接受一个指向ft260_driver结构体的指针作为参数,并返回一个整数值。ft260_driver是一个hid驱动结构体:
static struct hid_driver ft260_driver = {
.name = "ft260",
.id_table = ft260_devices,
.probe = ft260_probe,
.remove = ft260_remove,
.raw_event = ft260_raw_event,
};
这个结构体用于描述一个USB HID设备的驱动程序,具体包含了以下成员:
name
:驱动程序的名称,此处为"ft260"。id_table
:指向一个设备ID列表的指针,用于匹配设备。probe
:指向一个函数ft260_probe
的指针,该函数在设备插入时被调用,用于初始化设备。remove
:指向一个函数ft260_remove
的指针,该函数在设备移除时被调用,用于反初始化设备。raw_event
:指向一个函数ft260_raw_event
的指针,该函数用于处理设备的原始事件。
2. ft260_driver_exit注销模块
static void __exit ft260_driver_exit(void)
{
hid_unregister_driver(&ft260_driver);
tty_unregister_driver(ft260_tty_driver);
tty_driver_kref_put(ft260_tty_driver);
}
__exit 是注销模块的标准写法。
函数中调用了hid_unregister_driver
来注销hid设备驱动程序ft260_driver
,调用tty_unregister_driver
来注销串口驱动程序ft260_tty_driver
,最后通过tty_driver_kref_put
释放ft260_tty_driver
的引用计数,它是和初始模块中的tty_alloc_driver成对出现的。
3. 调试
在Linux驱动模块中,不能使用printf打印信息,需要使用printk打印。在init和exit返回的位置添加打印看一下驱动安装与卸载
- lsmod查看有没有hid-ft260
- 命令"sudo insmod hid-ft260.ko" 安装驱动
- 命令“cat /proc/device”查看驱动主设备号分配情况
- 命令“sudo dmesg”查看log信息
- 命令“sudo rmmod hid-ft260”卸载驱动
- 命令“sudo dmesg”查看log信息