linux驱动结构体参数传递出错,Linux驱动分析之Uart驱动架构

Uart体系结构

UART设备驱动可以使用tty驱动的框架来实现,但是因为串口之间有共性,所以Linux在tty接口上封装了一层(serial core)。后面我们再拿一篇文章来解释tty驱动,tty其实就是各种终端设备,串口其实也是终端设备。

驱动工程师没必要关心上层的流程,只需注册一个uart_driver,并按硬件规范将对应接口函数完成就可以了。

e50b1125a97c6efe16587859765f8ec6.png

上图我们只需要实现xxx_uart.c ,  而我们实现所需要的结构体和函数接口就是由serial_core.c提供。接下来我们来看一下对应的结构体和接口函数。

重要结构体

内核版本:4.20.12

uart_driver

struct uart_driver {

struct module *owner;

const char *driver_name;

const char *dev_name; //设备名,即dev下的节点名

int major;

int minor;

int nr;

struct console *cons;//console配置,串口作为console时才需要

//私有的,底层驱动把它初始化为NULL即可

struct uart_state *state;

struct tty_driver *tty_driver;

};

串口设备也是字符设备,所以看到很多字符设备相关的,console就是控制台,我们平常所使用的debug口就是console。

uart_port

//描述一个UART端口

struct uart_port {

spinlock_t lock; /* port lock */

unsigned long iobase; /* in/out[bwl] */

unsigned char __iomem *membase; /* read/write[bwl] */

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);

void (*set_ldisc)(struct uart_port *,

struct ktermios *);

unsigned int (*get_mctrl)(struct uart_port *);

void (*set_mctrl)(struct uart_port *, unsigned int);

unsigned int (*get_divisor)(struct uart_port *,

unsigned int baud,

unsigned int *frac);

void (*set_divisor)(struct uart_port *,

unsigned int baud,

unsigned int quot,

unsigned int quot_frac);

int (*startup)(struct uart_port *port);

void (*shutdown)(struct uart_port *port);

void (*throttle)(struct uart_port *port);

void (*unthrottle)(struct uart_port *port);

//中断处理

int (*handle_irq)(struct uart_port *);

void (*pm)(struct uart_port *, unsigned int state,

unsigned int old); //电源管理

void (*handle_break)(struct uart_port *);

//485配置

int (*rs485_config)(struct uart_port *,

struct serial_rs485 *rs485);

int (*iso7816_config)(struct uart_port *,

struct serial_iso7816 *iso7816);

unsigned int irq; /* 中断号 */

unsigned long    irqflags;    /* 中断标志  */

unsigned int    uartclk;    /* 串口时钟 */

unsigned int fifosize; /* tx fifo size */

unsigned char x_char; /* xon/xoff char */

unsigned char regshift; /* reg offset shift */

unsigned char iotype; /* io access style */

unsigned char quirks; /* internal quirks */

//省略宏定义....

unsigned int read_status_mask; /* driver specific */

unsigned int ignore_status_mask; /* driver specific */

struct uart_state *state; /* pointer to parent state */

struct uart_icount icount; /* statistics */

struct console *cons; /* struct console, if any */

#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)

unsigned long sysrq; /* sysrq timeout */

#endif

/* flags must be updated while holding port mutex */

upf_t flags;

//省略宏定义....

upstat_t status;

//省略宏定义....

int hw_stopped; /* sw-assisted CTS flow state */

unsigned int mctrl; /* current modem ctrl settings */

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 */

unsigned int minor;

resource_size_t mapbase; /* for ioremap */

resource_size_t mapsize;

struct device *dev; /* parent device */

unsigned char hub6; /* this should be in the 8250 driver */

unsigned char suspended;

unsigned char unused[2];

const char *name; /* port name */

struct attribute_group *attr_group; /* port specific attributes */

const struct attribute_group **tty_groups; /* all attributes (serial core use only) */

struct serial_rs485 rs485;

struct serial_iso7816 iso7816;

void *private_data; /* generic platform data pointer */

};

uart_port用于描述一个UART端口的I/O端口或I/O内存地址、FIFO大小、端口类型等信息。这个结构体参数很多,还有很多对串口进行配置的函数。

uart_ops

//物理硬件的所有操作

struct uart_ops {

//一些操作函数

unsigned int (*tx_empty)(struct uart_port *);//判断发送FIFO是否为空

void (*set_mctrl)(struct uart_port *, unsigned int mctrl); //设置控制信息

unsigned int (*get_mctrl)(struct uart_port *); //获取当前控制信息

void (*stop_tx)(struct uart_port *); //停止tx

void (*start_tx)(struct uart_port *);//启动tx

void (*throttle)(struct uart_port *);//通知串口驱动,线路规程输入缓冲区接近满了

void (*unthrottle)(struct uart_port *);//通知串口驱动可以将字符发送到线路规程输入缓冲区

void (*send_xchar)(struct uart_port *, char ch); //传输高优先级字符,即使端口已停止。

void (*stop_rx)(struct uart_port *); //停止Rx

void (*enable_ms)(struct uart_port *); //使能modem状态中断

void (*break_ctl)(struct uart_port *, int ctl); //控制中断信号的传输

int (*startup)(struct uart_port *); //启动串口

void (*shutdown)(struct uart_port *); //关闭串口

void (*flush_buffer)(struct uart_port *); //刷新写buffer,复位DMA

void (*set_termios)(struct uart_port *, struct ktermios *new,

struct ktermios *old); //改变串口参数,包括字长,奇偶校验,停止位。

void (*set_ldisc)(struct uart_port *, struct ktermios *); //通知线路规程改变

void (*pm)(struct uart_port *, unsigned int state,

unsigned int oldstate); //电源管理

//返回一个描述串口类型的字符串

const char *(*type)(struct uart_port *);

//释放IO和内存资源

void (*release_port)(struct uart_port *);

//申请IO和内存资源

int (*request_port)(struct uart_port *);

//配置串口

void (*config_port)(struct uart_port *, int);

int (*verify_port)(struct uart_port *, struct serial_struct *);

int (*ioctl)(struct uart_port *, unsigned int, unsigned long);

#ifdef CONFIG_CONSOLE_POLL

int (*poll_init)(struct uart_port *);

void (*poll_put_char)(struct uart_port *, unsigned char);

int (*poll_get_char)(struct uart_port *);

#endif

};

uart_driver是对tty_driver的封装,uart_driver和platform_driver还是有区别的,因为它并没有probe回调函数。它主要是一些字符设备的信息。

uart_port用来描述具体的串口,主要是一些串口参数。

uart_ops就是一些串口的操作函数,和字符设备中的file_operations差不多。

API函数

//注册/注销uart_driver到串口核心层

int uart_register_driver(struct uart_driver *drv)

void uart_unregister_driver(struct uart_driver *drv)

//关联具体串口和驱动

int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)

//移除串口和驱动的管理

int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport)

我们使用到的接口函数很少,所以其实蛮简单的,Linux封装完之后就是填充结构体,然后调用接口注册一下。

总结

首先我们要清楚,在底层,Uart驱动是为每个port都分配了缓存空间的。所以应用层读取的都是缓存空间中的。然后uart_driver不能和platform_driver混淆。后面我们分析实例时会发现Uart的驱动是由platform_driver来回调probe的。之前说过,控制器都是使用platform_driver, 串口对于芯片而言,也是一个控制器。

分析一大堆代码是不是看着很累,所以千万别全部看,挑重点看,理清思路即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值