一、linux tty驱动框架
本人是linux驱动初学者,最近在初步学习uart驱动,在这记录下来自己的理解
linux3.10
soc:君正x1000e
四位大佬写的很好
https://blog.csdn.net/cosmoslhf/article/details/16945009
https://blog.csdn.net/lizuobin2/article/details/51801183
https://blog.csdn.net/Luckiers/article/details/123577836
https://blog.csdn.net/mike8825/article/details/107598833
在Linux kernel中,tty驱动不像于spi,iic等那么架构简单,它是一个庞大的系统,它的框架大体如下图一。我们作为普通的驱动开发移植人员,不会从零写tty驱动,一般都是厂家根据现有的tty驱动和自家芯片修改,拿到板子按照厂家的配置,串口应该使能直接使用的。但是开发的过程中也有可能需要用到串口,一般会修改serial驱动,这样我们不会动tty_core层。
Linux tty子系统包含:tty核心(tty_core),tty线路规程(tty_line_discipine)和tty驱动(tty_driver)。tty核心是对整个tty设备的抽象,对用户提供统一的接口,tty线路规程是对传输数据的格式化,tty驱动则是面向tty设备的硬件驱动。
图一
二、uart驱动数据架构
1、主要用到的数据架构图二
图二
uart_driver描述uart设备,其他下面有2个重要的结构体uart_state(用于描述底层硬件信息),tty_drvier(tty层驱动)。
uart_driver结构体
struct uart_driver {
struct module *owner;
const char *driver_name;//串口驱动名字
const char *dev_name;//设备名字
int major;
int minor;
int nr;
struct console *cons;
/*
有两个成员未被赋值,对于tty_driver 代表的是上层,它会在 register_uart_driver 中的过程中赋值,
而uart_state 则代表下层,uart_state 也会在register_uart_driver 的过程中分配空间,
但是它里面真正设置硬件相关的东西是 uart_state->uart_port ,
这个uart_port 是需要我们从其它地方调用 uart_add_one_port 来添加的。
*/
struct uart_state *state;//底层指定硬件参数
struct tty_driver *tty_driver;//tty层
};
一般来说一个芯片具有多个串口,比如君正x1000e具有3个串口,那uart_state应该有多个,每个保存一个串口信息。
tty_prot是TTY device的一种抽象
circ_buf是一个发送缓存,在写数据时,当tty层调用驱动提供的写函数时,数据会首先进入circ_buf的环形缓存区,然后由uart_port从缓存区中取数据,将其写入到串口设备中。
当uart_port从串口设备接收到数据时,它会直接将数据放入tty的缓存中(tty缓存属于tty_port),进而放入对应的line discipline的缓存区中。
uart_state结构体
uart_state中最重要的结构体成员uart_port
uart_state->uart_port(在这保存硬件串口信息)
truct uart_port {
spinlock_t lock; /* port lock */
unsigned long iobase; /* io基地址 */
unsigned char __iomem *membase; /*内存基地址 */
unsigned int (*serial_in)(struct uart_port *, int);//串口接受函数指针
void (*serial_out)(struct uart_port *, int, int);//串口发送函数指针
void (*set_termios)(struct uart_port *,
struct ktermios *new,
struct ktermios *old);
int (*handle_irq)(struct uart_port *);
void (*pm)(struct uart_port *, unsigned int state,
unsigned int old);
void (*handle_break)(struct uart_port *);
unsigned int irq; /* irq number */
unsigned long irqflags; /* irq flags */
unsigned int uartclk; /* base uart clock */
unsigned int fifosize; /* tx fifo size */
unsigned char x_char; /* xon/xoff char */
unsigned char regshift; /* reg offset shift */
unsigned char iotype; /* io访问方式 */
unsigned char unused1;
....
unsigned int mctrl; /* 当前控制模式*/
unsigned int timeout; /* character-based timeout */
unsigned int type; /* port type */
const struct uart_ops *ops;
unsigned int custom_divisor;
unsigned int line; /* port index */
resource_size_t mapbase; /* for ioremap */
struct device *dev; /* parent device */
unsigned char hub6; /* this should be in the 8250 driver */
unsigned char suspended;
unsigned char irq_wake;
unsigned char unused[2];
void *private_data; /* generic platform data pointer */
};
tty_port结构体
保存着tty层驱动信息
struct tty_driver {
int magic; /* magic number for this structure */
struct kref kref; /* Reference management */
struct cdev *cdevs;
struct module *owner;
const char *driver_name;
const char *name;
int name_base; /* offset of printed name */
int major; /* major device number */
int minor_start; /* start of minor device number */
unsigned int num; /* number of devices allocated */
short type; /* type of tty driver */
short subtype; /* subtype of tty driver */
struct ktermios init_termios; /* Initial termios */
unsigned long flags; /* tty driver flags */
struct proc_dir_entry *proc_entry; /* /proc fs entry */
struct tty_driver *other; /* only used for the PTY driver */
/*
* Pointer to the tty data structures
*/
struct tty_struct **ttys;
struct tty_port **ports;
struct ktermios **termios;
void *driver_state;
/*
* Driver methods
*/
const struct tty_operations *ops;
struct list_head tty_drivers;
};
三、uart驱动编写步骤
uart它是linux在tty的基础上又做了一层封装,得益于该封装层,可以比较容易的编写新的串口驱动程序。
只需要2大步
(1)uart_register_driver函数注册一个串口设备
(2)platform_driver_register注册一个平台设备(最后执行uart_add_one_port)文章来源:https://www.toymoban.com/news/detail-402778.html
驱动大概框架文章来源地址https://www.toymoban.com/news/detail-402778.html
static int serial_jz47xx_startup(struct uart_port *port)
{
/*request_irq注册驱动*/
retval = request_irq(up->port.irq, serial_jz47xx_irq, 0, up->name, up);//request irq
if (retval)
return retval;
/*清理寄存器*/
/*初始化串口*/
/*使能中断*/
}
/*操作集合*/
struct uart_ops serial_jz47xx_pops = {
/*回调函数*/
.startup = serial_jz47xx_startup,//start endport
....
};
static struct uart_driver serial_jz47xx_reg = {
.owner = THIS_MODULE,
.driver_name = "JZ47XX serial", //串口驱动名字
.dev_name = "ttyS", //串口设备名字
.major = TTY_MAJOR, //主设备号
.minor = 64,
.nr = PORT_NR,/* 该uart_driver支持的串口个数(最大) */
.cons = JZ47XX_CONSOLE, /* 其对应的console.若该uart_driver支持serial console,否则为NULL */
};
static int
到了这里,关于tty驱动初步了解学习的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!