`
isiqi
  • 浏览: 16558689 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

UART设备驱动分析

阅读更多

尽管一个特定的UART设备驱动完全可以遵循14.2~14.5的方法来设计,即定义tty_driver并实现其中的成员函数,但是Linux已经在文件 serial_core.c中实现了UART设备的通用tty驱动层(姑且称其为串口核心层),这样,UART驱动的主要任务演变成实现serial- core.c中定义的一组uart_xxx接口而非tty_xxx接口,如图14.5所示。
serial_core.c串口核心层完全可以被当作14.2~14.5节tty设备驱动的实例,它实现了UART设备的tty驱动。
提示:Linux驱动的这种分层思想在许多类型的设备驱动中都得到了体现,例如上一章IDE设备驱动中,内核实现了通用的IDE层用于处理块设备I/O请求,而具体的IDE则只需使用ide_xxx这样的接口,甚至不必理会复杂的块设备驱动结构。


图14.5 串口核心层
串口核心层为串口设备驱动提供了如下3个结构体:
1、uart_driver
uart_driver包含串口设备的驱动名、设备名、设备号等信息,它封装了tty_driver,使得底层的UART驱动无需关心tty_driver,其定义如代码清单14.13。
代码清单14.13 uart_driver结构体
1struct uart_driver
2{
3 struct module *owner;
4 const char *driver_name; //驱动名
5 const char *dev_name; //设备名
6 const char *devfs_name;//设备文件系统
7 int major;//主设备号
8 int minor; //次设备号
9 int nr;
10 struct console *cons;
11
12 /* 私有的,底层驱动不应该访问这些成员,应该被初始化为NULL */
13 struct uart_state *state;
14 struct tty_driver *tty_driver;
15 };
一个tty驱动必须注册/注销tty_driver,而一个UART驱动则演变为注册/注销uart_driver,使用如下接口:
int uart_register_driver(struct uart_driver *drv);
void uart_unregister_driver(struct uart_driver *drv);
实际上,uart_register_driver()和uart_unregister_driver()中分别包含了tty_register_driver()和tty_unregister_driver()的操作,如代码清单14.14所示。
代码清单14.14 uart_register_driver()和uart_unregister_driver()函数
1int uart_register_driver(struct uart_driver *drv)
2{
3 struct tty_driver *normal = NULL;
4 int i, retval;
5 ...
6 /* 分配tty_driver */
7 normal= alloc_tty_driver(drv->nr);
8 if (!normal)
9 goto out;
10drv->tty_driver = normal;
11 /* 初始化tty_driver */
12normal->owner= drv->owner;
13normal->driver_name = drv->driver_name;
14normal->devfs_name = drv->devfs_name;
15normal->name= drv->dev_name;
16normal->major= drv->major;
17normal->minor_start = drv->minor;
18normal->type= TTY_DRIVER_TYPE_SERIAL;
19normal->subtype= SERIAL_TYPE_NORMAL;
20normal->init_termios = tty_std_termios;
21normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
22normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
23normal->driver_state = drv;
24tty_set_operations(normal, &uart_ops);
25
26...
27/* 注册tty驱动 */
28retval = tty_register_driver(normal);
29out:
30if (retval state);
33}
34return retval;
35 }
36
37 void uart_unregister_driver(struct uart_driver *drv)
38 {
39struct tty_driver *p = drv->tty_driver;
40tty_unregister_driver(p);/* 注销tty驱动 */
41put_tty_driver(p);
42kfree(drv->state);
43drv->tty_driver = NULL;
44 }
2、uart_port
uart_port用于描述一个UART端口(直接对应于一个串口)的I/O端口或I/O内存地址、FIFO大小、端口类型等信息,其定义如代码清单14.15。
代码清单14.15 uart_port结构体
1struct uart_port
2{
3 spinlock_t lock; /* 端口锁 */
4 unsigned int iobase; /* IO端口基地址 */
5 unsigned char __iomem *membase; /* IO内存基地址 */
6 unsigned int irq; /* 中断号 */
7 unsigned int uartclk; /* UART时钟 */
8 unsigned char fifosize; /* 传输fifo大小 */
9 unsigned char x_char; /* xon/xoff字符 */
10 unsigned char regshift; /* 寄存器位移 */
11 unsigned char iotype; /* IO存取类型 */
12
13 #define UPIO_PORT(0)/* IO端口*/
14 #define UPIO_HUB6(1)
15 #define UPIO_MEM(2)/* IO内存*/
16 #define UPIO_MEM32(3)
17 #define UPIO_AU (4) /* Au1x00类型IO */
18
19 unsigned int read_status_mask; /* 驱动相关的 */
20 unsigned int ignore_status_mask; /* 驱动相关的 */
21 struct uart_info *info; /* 指向parent信息 */
22 struct uart_icount icount; /* 计数 */
23
24 struct console *cons; /* console结构体 */
25 #ifdef CONFIG_SERIAL_CORE_CONSOLE
26 unsigned long sysrq; /* sysrq超时 */
27 #endif
28
29 upf_t flags;
30
31 #define UPF_FOURPORT((__force upf_t) (1 目前modem控制设置 */
54 unsigned int timeout; /* 基于字符的超时 */
55 unsigned int type; /* 端口类型 */
56 const struct uart_ops *ops; /* UART操作集 */
57 unsigned int custom_divisor;
58 unsigned int line; /* 端口索引 */
59 unsigned long mapbase; /* ioremap后基地址 */
60 struct device *dev; /* parent设备 */
61 unsigned char hub6;
62 unsigned char unused[3];
63 };
串口核心层提供如下函数来添加1个端口:
int uart_add_one_port(struct uart_driver *drv, struct uart_port *port);
对上述函数的调用应该发生在uart_register_driver()之后,uart_add_one_port()的一个最重要作用是封装了tty_register_device()。
uart_add_one_port()的“反函数”是uart_remove_one_port(),其中会调用tty_unregister_device(),原型为:
int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port);
驱动中虽然不需要处理uart_port的uart_info成员,但是在发送时,从用户来的数据被保存在xmit(被定义为circ_buf,即环形缓冲 区)中,因此UART驱动在发送数据时(一般在发送中断处理函数中),需要从这个circ_buf获取上层传递下来的字符。
3、uart_ops
uart_ops 定义了针对UART的一系列操作,包括发送、接收及线路设置等,如果说tty_driver中的tty_operations对于串口还较为抽象,那么 uart_ops则直接面向了串口的UART,其定义如代码清单14.16。Linux驱动的这种层次非常类似于面向对象编程中基类、派生类的关系,派生类针对特定的事物会更加具体,而基类则站在更高的抽象层次上。
代码清单14.16 uart_ops结构体
1struct uart_ops
2{
3 unsigned int(*tx_empty)(struct uart_port*);
4 void(*set_mctrl)(struct uart_port *, unsigned int mctrl);
5 unsigned int(*get_mctrl)(struct uart_port*);
6 void(*stop_tx)(struct uart_port*); //停止发送
7 void(*start_tx)(struct uart_port*); //开始发送
8 void(*send_xchar)(struct uart_port *, char ch); //发送xchar
9 void(*stop_rx)(struct uart_port*); //停止接收
10 void(*enable_ms)(struct uart_port*);
11 void(*break_ctl)(struct uart_port *, int ctl);
12 int(*startup)(struct uart_port*);
13 void(*shutdown)(struct uart_port*);
14 void(*set_termios)(struct uart_port *, struct termios *new, struct termios
15 *old); //设置termios
16 void(*pm)(struct uart_port *, unsigned int state, unsigned int oldstate);
17 int(*set_wake)(struct uart_port *, unsigned int state);
18
19 /* 返回1个描述端口类型的字符串 */
20 const char *(*type)(struct uart_port*);
21
22 /* 释放端口使用的IO和内存资源,必要的情况下,应该进行iounmap操作 */
23 void(*release_port)(struct uart_port*);
24 /* 申请端口使用的IO和内存资源 */
25 int(*request_port)(struct uart_port*);
26
27 void(*config_port)(struct uart_port *, int);
28 int(*verify_port)(struct uart_port *, struct serial_struct*);
29 int(*ioctl)(struct uart_port *, unsigned int, unsigned long);
30 };
serial_core.c 中定义了tty_operations的实例,包含uart_open()、uart_close()、uart_write()、 uart_send_xchar()等成员函数(如代码清单14.17),这些函数会借助uart_ops结构体中的成员函数来完成具体的操作,代码清单 14.18给出了tty_operations的uart_send_xchar()成员函数利用uart_ops中start_tx()、 send_xchar()成员函数的例子。
代码清单14.17 串口核心层的tty_operations实例
1static struct tty_operations uart_ops =
2{
3 .open= uart_open,//串口打开
4 .close= uart_close,//串口关闭
5 .write= uart_write,//串口发送
6 .put_char = uart_put_char,//...
7 .flush_chars = uart_flush_chars,
8 .write_room = uart_write_room,
9 .chars_in_buffer= uart_chars_in_buffer,
10.flush_buffer = uart_flush_buffer,
11.ioctl= uart_ioctl,
12.throttle = uart_throttle,
13.unthrottle = uart_unthrottle,
14.send_xchar = uart_send_xchar,
15.set_termios = uart_set_termios,
16.stop= uart_stop,
17.start= uart_start,
18.hangup= uart_hangup,
19.break_ctl = uart_break_ctl,
20.wait_until_sent= uart_wait_until_sent,
21 #ifdef CONFIG_PROC_FS
22.read_proc = uart_read_proc, //proc入口读函数
23 #endif
24.tiocmget = uart_tiocmget,
25.tiocmset = uart_tiocmset,
26 };
代码清单14.18 串口核心层的tty_operations与uart_ops关系
1static void uart_send_xchar(struct tty_struct *tty, char ch)
2{
3 struct uart_state *state = tty->driver_data;
4 struct uart_port *port = state->port;
5 unsigned long flags;
6 //如果uart_ops中实现了send_xchar成员函数
7 if (port->ops->send_xchar)
8 port->ops->send_xchar(port, ch);
9 else //uart_ops中未实现send_xchar成员函数
10 {
11 port->x_char = ch; //xchar赋值
12 if (ch)
13 {
14 spin_lock_irqsave(&port->lock, flags);
15 port->ops->start_tx(port);//发送xchar
16 spin_unlock_irqrestore(&port->lock, flags);
17 }
18 }
19 }
注意:整个调用流程为:系统调用write()->uart_write()(tty_driver)->port->ops->start_tx();
在使用串口核心层这个通用串口tty驱动层的接口后,一个串口驱动要完成的主要工作将包括:
•定义uart_driver、uart_ops、uart_port等结构体的实例并在适当的地方根据具体硬件和驱动的情况初始化它们,当然具体设备 xxx的驱动可以将这些结构套在新定义的xxx_uart_driver、xxx_uart_ops、xxx_uart_port之内。
•在模块初始化时调用uart_register_driver()和uart_add_one_port()以注册UART驱动并添加端口,在模块卸载时 调用uart_unregister_driver()和uart_remove_one_port()以注销UART驱动并移除端口。
•根据具体硬件的datasheet实现uart_ops中的成员函数,这些函数的实现成为UART驱动的主体工作。
14.7实例:S3C2410 UART
的驱动
14.7.1 S3C2410串口硬件描述
S3C2410 内部具有3个独立的UART控制器,每个控制器都可以工作在Interrupt(中断)模式或DMA(直接内存访问)模式,也就是说UART控制器可以在 CPU与UART控制器传送资料的时候产生中断或DMA请求。S3C2410集成的每个UART均具有16字节的FIFO,支持的最高波特率可达到 230.4Kbps。
ULCONn(UART Line Control Register)寄存器用于S3C2410 UART的线路控制,用于设置模式、每帧的数据位数、停止位数及奇偶校验,如表14.1。
表14.1 S3C2410 UART的ULCONn寄存器
ULCONn 位 描述
保留 [7]
红外模式 [6] 0:正常模式1:红外模式
奇偶校验 [5:3] 0xx:无校验100:奇校验101:偶校验...
停止位 [2] 0:1个停止位1:2个停止位
字长 [1:0] 00:5位 01:6位 10:7位 11:8位
UCONn(UART Control Register)寄存器用于从整体上控制S3C2410 UART的中断模式及工作模式(DMA、中断、轮询)等,如表14.2。
表14.2 S3C2410 UART的UCONn寄存器
UCONn 位 描述
时钟选择 [10] 为UART的波特率产生选择PCLK或UCLK时钟
Tx中断 [9] 0:脉冲 1:电平
Rx中断 [8] 0:脉冲 1:电平
Rx超时使能 [7] 当UART被使能,使能/禁止Rx超时中断0:禁止1:使能
Rx错误状态中断使能 [6] 使能接收异常中断(如break、帧错误、校验错、溢出等)
loopback [5] 0:正常模式 1:回环
发送break [4] 设置该位将造成UART在1帧的时间内发送break,当发送完break后,该位将自动被清除
发送模式 [3:2] 发送数据到UART的模式,00:禁止 01:中断或轮询 10:DMA0(仅针对UART0)、DMA3(仅针对UART3) 11:DMA1(仅针对UART1)
接收模式 [1:0] 从UART接收数据的模式,00:禁止 01:中断或轮询 10:DMA0(仅针对UART0)
UFCONn(UART FIFO Conrtol Register)寄存器用于S3C2410 UART的FIFO控制,用于控制FIFO中断的触发级别以及复位时是否清空FIFO中的内容,如表14.3。
表14.3 S3C2410 UART的UFCONn寄存器
UFCONn 位 描述
Tx FIFO触发级别 [7:6] 决定发送FIFO的触发级别: 00:空 01:4字节 10:8字节 11:12字节
Rx FIFO触发级别 [5:4] 决定接收FIFO的触发级别: 00:4字节 01:8字节 10:12字节 11:16字节
Tx FIFO复位 [2] 复位FIFO后自动清除FIFO0:正常 1:Tx FIFO复位
Rx FIFO复位 [1] 复位FIFO后自动清除FIFO0:正常 1:Tx FIFO复位
FIFO使能 [0] 0:禁止 1:使能
代码清单14.19给出了UFCONn寄存器的位掩码和默认设置(使能FIFO、Tx FIFO为空时触发中断、Rx FIFO中包含8个字节时触发中断)。
代码清单14.19 S3C2410 UART UFCONn寄存器的位掩码和默认设置
1#define S3C2410_UFCON_FIFOMODE (1(UART FIFO Status Register)寄存器用于表征UART FIFO的状态,如表14.4。
表14.4 S3C2410 UART的UFSTATn寄存器
UFSTATn 位 描述
保留 [15:10]
Tx FIFO满 [9] 当Tx FIFO满后,将自动被设置为10:0字节 ≤ Tx FIFO数据数 ≤ 151:Tx FIFO数据数 = 15
Rx FIFO满 [8] 当Rx FIFO满后,将自动被设置为10:0字节 ≤ Rx FIFO数据数 ≤ 151:Tx FIFO数据数 = 15
Tx FIFO数据数 [7:4]
Rx FIFO数据数 [3:0]
由于UFSTATn寄存器中的Tx FIFO数据数和Rx FIFO数据数分别占据[7:4]和[3:0]位,因此定义S3C2410_UFSTAT_TXSHIFT和 S3C2410_UFSTAT_RXSHIFT分别为4和0,代码清单14.20给出了UFSTATn寄存器的位掩码等信息。
代码清单14.20 S3C2410 UART UFSTATn寄存器的位掩码
1 #define S3C2410_UFSTAT_TXFULL (1(UART Transmit Buffer Register)和 URXHn(UART Receive Buffer Register)分别是UART发送和接收数据寄存器,这2个寄存器存放着发送和接收的数据。
UTRSTATn(UART TX/RX Status Register)寄存器反映了发送和接收的状态,通过这个寄存器,驱动程序可以判断URXHn中是否有数据接收到或UTXHn是否为空,这个寄存器主要在非FIFO模式时使用。
UMCONn(UART Modem Control Register)用于S3C2410 UART的modem控制,设置是否使用RTS流控,若采用流控,可选择自动流控(Auto Flow Control,AFC)或由软件控制RTS信号的“高”或“低”电平。
14.7.2 S3C2410串口驱动数据结构
S3C2410串口驱动中uart_driver结构体实例的定义如代码清单14.21,设备名为“s3c2410_serial”,驱动名为“ttySAC”。
代码清单14.21 S3C2410串口驱动uart_driver结构体
1#define S3C24XX_SERIAL_NAME "ttySAC"
2#define S3C24XX_SERIAL_DEVFS "tts/"
3#define S3C24XX_SERIAL_MAJOR 204
4#define S3C24XX_SERIAL_MINOR 64
5
6static struct uart_driver s3c24xx_uart_drv =
7{
8 .owner= THIS_MODULE,
9 .dev_name = "s3c2410_serial",
10.nr= 3,
11.cons= S3C24XX_SERIAL_CONSOLE,
12.driver_name = S3C24XX_SERIAL_NAME,
13.devfs_name = S3C24XX_SERIAL_DEVFS,
14.major= S3C24XX_SERIAL_MAJOR,
15.minor= S3C24XX_SERIAL_MINOR,
16 };
S3C2410 串口驱动中定义了结构体s3c24xx_uart_port,该结构体中封装了uart_port结构体及一些针对S3C2410 UART的附加信息,代码清单14.22给出了s3c24xx_uart_port结构体及其实例s3c24xx_serial_ports[]数组。
代码清单14.22 S3C2410串口驱动s3c24xx_uart_port结构体
1struct s3c24xx_uart_port
2{
3 unsigned char rx_claimed;
4 unsigned char tx_claimed;
5
6 struct s3c24xx_uart_info *info;
7 struct s3c24xx_uart_clksrc *clksrc;
8 struct clk *clk;
9 struct clk *baudclk;
10struct uart_portport;
11 };
12
13 static struct s3c24xx_uart_port s3c24xx_serial_ports[NR_PORTS] = {
14[0] = {
15 .port = {
16 .lock= SPIN_LOCK_UNLOCKED,
17 .iotype= UPIO_MEM,
18 .irq= IRQ_S3CUART_RX0,
19 .uartclk = 0,
20 .fifosize = 16,
21 .ops= &s3c24xx_serial_ops,
22 .flags= UPF_BOOT_AUTOCONF,
23 .line= 0,//端口索引:0
24 }
25},
26[1] = {
27 .port = {
28 .lock= SPIN_LOCK_UNLOCKED,
29 .iotype= UPIO_MEM,
30 .irq= IRQ_S3CUART_RX1,
31 .uartclk = 0,
32 .fifosize = 16,
33 .ops= &s3c24xx_serial_ops,
34 .flags= UPF_BOOT_AUTOCONF,
35 .line= 1, //端口索引:1
36 }
37},
38 #if NR_PORTS > 2
39
40[2] = {
41 .port = {
42 .lock= SPIN_LOCK_UNLOCKED,
43 .iotype= UPIO_MEM,
44 .irq= IRQ_S3CUART_RX2,
45 .uartclk = 0,
46 .fifosize = 16,
47 .ops= &s3c24xx_serial_ops,
48 .flags= UPF_BOOT_AUTOCONF,
49 .line= 2, //端口索引:2
50 }
51}
52 #endif
53 };
S3C2410串口驱动中uart_ops结构体实例的定义如代码清单14.23,将一系列s3c24xx_serial_函数赋值给了uart_ops结构体的成员。
代码清单14.23 S3C2410串口驱动uart_ops结构体
1static struct uart_ops s3c24xx_serial_ops =
2{
3 .pm= s3c24xx_serial_pm,
4 .tx_empty = s3c24xx_serial_tx_empty,//发送缓冲区空
5 .get_mctrl = s3c24xx_serial_get_mctrl,//得到modem控制设置
6 .set_mctrl = s3c24xx_serial_set_mctrl, //设置modem控制(MCR)
7 .stop_tx = s3c24xx_serial_stop_tx, //停止接收字符
8 .start_tx = s3c24xx_serial_start_tx,//开始传输字符
9 .stop_rx = s3c24xx_serial_stop_rx, //停止接收字符
10.enable_ms = s3c24xx_serial_enable_ms,// modem状态中断使能
11.break_ctl = s3c24xx_serial_break_ctl,// 控制break信号的传输
12.startup = s3c24xx_serial_startup,//启动端口
13.shutdown = s3c24xx_serial_shutdown,// 禁用端口
14.set_termios = s3c24xx_serial_set_termios,//改变端口参数
15.type= s3c24xx_serial_type,//返回描述特定端口的常量字符串指针
16.release_port = s3c24xx_serial_release_port,//释放端口占用的内存及IO资源
17.request_port = s3c24xx_serial_request_port,//申请端口所需的内存和IO资源
18.config_port = s3c24xx_serial_config_port,//执行端口所需的自动配置步骤
19.verify_port = s3c24xx_serial_verify_port,//验证新的串行端口信息
20 };
set_mctrl()函数的原型为:
void (*set_mctrl)(struct uart_port *port, u_int mctrl);
它将参数port所对应的调制解调器控制线的值设为参数mctrl的值。
get_mctrl()函数的原型为:
unsigned int (*get_mctrl)(struct uart_port *port);
该函数返回调制解调器控制输入的现有状态,这些状态信息包括:TIOCM_CD(CD 信号状态)、TIOCM_CTS(CTS信号状态)、TIOCM_DSR(DSR信号状态)、TIOCM_RI(RI信号状态)等。如果信号被置为有效,则对应位将被置位。
端口启动函数startup()的原型为:
int (*startup)(struct uart_port *port, struct uart_info *info);
该函数申请所有中断资源,初始化底层驱动状态,并开启端口为可接收数据的状态。
shutdown()函数完成与startup()函数的作用相反,其原型:
void (*shutdown)(struct uart_port *port, struct uart_info *info);
这个函数禁用端口,释放所有的中断资源。
回过头来看s3c24xx_uart_port结构体,其中的s3c24xx_uart_info成员(代码清单14.22第6行)是一些针对S3C2410 UART的信息,其定义如代码清单14.24。
代码清单14.24 S3C2410串口驱动s3c24xx_uart_info结构体
1static struct s3c24xx_uart_info s3c2410_uart_inf =
2{
3 .name= "Samsung S3C2410 UART",
4 .type= PORT_S3C2410,
5 .fifosize = 16,
6 .rx_fifomask = S3C2410_UFSTAT_RXMASK,
7 .rx_fifoshift = S3C2410_UFSTAT_RXSHIFT,
8 .rx_fifofull = S3C2410_UFSTAT_RXFULL,
9 .tx_fifofull = S3C2410_UFSTAT_TXFULL,
10.tx_fifomask = S3C2410_UFSTAT_TXMASK,
11.tx_fifoshift = S3C2410_UFSTAT_TXSHIFT,
12.get_clksrc = s3c2410_serial_getsource,
13.set_clksrc = s3c2410_serial_setsource,
14.reset_port = s3c2410_serial_resetport,
15 };
在S3C2410串口驱动中,针对UART的设置(UCONn、ULCONn、UFCONn寄存器等)被封装到s3c2410_uartcfg结构体中,其定义如代码清单14.25。
代码清单14.25 S3C2410串口驱动s3c2410_uartcfg结构体
1struct s3c2410_uartcfg
2{
3 unsigned char hwport; /* 硬件端口号 */
4 unsigned char unused;
5 unsigned short flags;
6 unsigned long uart_flags; /* 缺省的uart标志 */
7
8 unsigned long ucon; /* 端口的ucon值 */
9 unsigned long ulcon; /* 端口的ulcon值 */
10 unsigned long ufcon; /* 端口的ufcon值 */
11
12 struct s3c24xx_uart_clksrc *clocks;
13 unsigned int clocks_size;
14 };
14.7.3 S3C2410串口驱动初始化与释放
在S3C2410 串口驱动的模块加载函数中会调用uart_register_driver()注册s3c24xx_uart_drv这个uart_driver,同时经过s3c2410_serial_init()→s3c24xx_serial_init()→platform_driver_register()的调用导致s3c24xx_serial_probe()被执行,而s3c24xx_serial_probe()函数中会调用 s3c24xx_serial_init_port()初始化UART端口并调用uart_add_one_port()添加端口,整个过程的对应代码如 清单14.26。
代码清单14.26 S3C2410串口驱动初始化过程
1 static int __init s3c24xx_serial_modinit(void)
2 {
3 int ret;
4 //注册uart_driver
5 ret = uart_register_driver(&s3c24xx_uart_drv);
6 if (ret 初始化s3c2410的串口
11 s3c2410_serial_init();
12
13 return 0;
14}
15
16static inline int s3c2410_serial_init(void)
17{
18 return s3c24xx_serial_init(&s3c2410_serial_drv, &s3c2410_uart_inf);
19}
20
21static int s3c24xx_serial_init(struct platform_driver *drv,
22 struct s3c24xx_uart_info *info)
23{
24 dbg("s3c24xx_serial_init(%p,%p)\n", drv, info);
25 return platform_driver_register(drv);//注册平台驱动
26}
27
28//平台驱动probe()函数
29static int s3c24xx_serial_probe(struct platform_device *dev,
30 struct s3c24xx_uart_info *info)
31{
32 struct s3c24xx_uart_port *ourport;
33 int ret;
34
35 dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index);
36
37 ourport = &s3c24xx_serial_ports[probe_index];
38 probe_index++;
39
40 dbg("%s: initialising port %p...\n", __FUNCTION__, ourport);
41 //初始化uart端口
42 ret = s3c24xx_serial_init_port(ourport, info, dev);
43 if (ret 添加uart_port
48 uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
49 platform_set_drvdata(dev, &ourport->port);
50
51 return 0;
52
53 probe_err:
54 return ret;
55}
56//42行调用的s3c24xx_serial_init_port()函数
57static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
58 struct s3c24xx_uart_info *info,
59 struct platform_device *platdev)
60{
61 struct uart_port *port = &ourport->port;
62 struct s3c2410_uartcfg *cfg;
63 struct resource *res;
64
65 dbg("s3c24xx_serial_init_port: port=%p, platdev=%p\n", port, platdev);
66
67 if (platdev == NULL)
68 return -ENODEV;
69
70 cfg = s3c24xx_dev_to_cfg(&platdev->dev);
71
72 if (port->mapbase != 0)
73 return 0;
74
75 if (cfg->hwport > 3)
76 return -EINVAL;
77
78 /* 为端口设置info成员 */
79 port->dev = &platdev->dev;
80 ourport->info = info;
81
82 /* 初始化fifosize */
83 ourport->port.fifosize = info->fifosize;
84
85 dbg("s3c24xx_serial_init_port: %p (hw %d)...\n", port, cfg->hwport);
86
87 port->uartclk = 1;
88 /* 如果使用流控 */
89 if (cfg->uart_flags & UPF_CONS_FLOW) {
90 dbg("s3c24xx_serial_init_port: enabling flow control\n");
91 port->flags |= UPF_CONS_FLOW;
92 }
93
94 /* 利用平台资源中记录的信息初始化uart端口的基地址、中断号 */
95 res = platform_get_resource(platdev, IORESOURCE_MEM, 0);
96 if (res == NULL) {
97 printk(KERN_ERR "failed to find memory resource for uart\n");
98 return -EINVAL;
99 }
100
101dbg("resource %p (%lx..%lx)\n", res, res->start, res->end);
102
103port->mapbase = res->start;
104port->membase = S3C24XX_VA_UART+(res->start - S3C24XX_PA_UART);
105port->irq = platform_get_irq(platdev, 0);
106
107ourport->clk = clk_get(&platdev->dev, "uart");
108
109dbg("port: map=%08x, mem=%08x, irq=%d, clock=%ld\n",
110 port->mapbase, port->membase, port->irq, port->uartclk);
111
112/* 复位fifo并设置uart */
113s3c24xx_serial_resetport(port, cfg);
114return 0;
115 }
116 //113行调用的s3c24xx_serial_resetport()函数
117 static inline int s3c24xx_serial_resetport(struct uart_port * port,
118 struct s3c2410_uartcfg *cfg)
119 {
120struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
121
122return (info->reset_port)(port, cfg);
123 }
124 //122行调用的info->reset_port()函数
125 static int s3c2410_serial_resetport(struct uart_port *port,
126 struct s3c2410_uartcfg *cfg)
127 {
128dbg("s3c2410_serial_resetport: port=%p (%08lx), cfg=%p\n",
129 port, port->mapbase, cfg);
130
131wr_regl(port, S3C2410_UCON,cfg->ucon);
132wr_regl(port, S3C2410_ULCON, cfg->ulcon);
133
134/* 复位2个fifo */
135wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
136wr_regl(port, S3C2410_UFCON, cfg->ufcon);
137
138return 0;
139 }
在S3C2410串口驱动模块被卸载时,它会最终调用uart_remove_one_port()释放uart_port并调用uart_unregister_driver()注销uart_driver,如代码清单14.27所示。
代码清单14.27 S3C2410串口驱动释放过程
1static void __exit s3c24xx_serial_modexit(void)
2{
3 s3c2410_serial_exit();
4 //注销uart_driver
5 uart_unregister_driver(&s3c24xx_uart_drv);
6}
7
8static inline void s3c2410_serial_exit(void)
9{
10//注销平台驱动
11platform_driver_unregister(&s3c2410_serial_drv);
12 }
13
14 static int s3c24xx_serial_remove(struct platform_device *dev)
15 {
16struct uart_port *port = s3c24xx_dev_to_port(&dev->dev);
17
18//移除uart端口
19if (port)
20 uart_remove_one_port(&s3c24xx_uart_drv, port);
21
22return 0;
23 }
上述代码中对S3C24xx_serial_remove()的调用发生在platform_driver_unregister()之后,由于S3C2410的UART是集成于SoC芯片内部的一个独立的硬件单元,因此也被作为1个平台设备而定义。
14.7.4 S3C2410串口数据收发
S3C2410串口驱动uart_ops结构体的startup ()成员函数s3c24xx_serial_startup()用于启动端口,申请端口的发送、接收中断,使能端口的发送和接收,其实现如代码清单14.28。
代码清单14.28 S3C2410串口驱动startup ()函数
1static int s3c24xx_serial_startup(struct uart_port *port)
2{
3 struct s3c24xx_uart_port *ourport = to_ourport(port);
4 int ret;
5
6 dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
7 port->mapbase, port->membase);
8
9 rx_enabled(port) = 1;//置接收使能状态为1
10 //申请接收中断
11ret = request_irq(RX_IRQ(port),
12 s3c24xx_serial_rx_chars, 0,
13 s3c24xx_serial_portname(port), ourport);
14
15if (ret != 0) {
16 printk(KERN_ERR "cannot get irq %d\n", RX_IRQ(port));
17 return ret;
18}
19
20ourport->rx_claimed = 1;
21
22dbg("requesting tx irq...\n");
23
24tx_enabled(port) = 1;//置发送使能状态为1
25 //申请发送中断
26ret = request_irq(TX_IRQ(port),
27 s3c24xx_serial_tx_chars, 0,
28 s3c24xx_serial_portname(port), ourport);
29
30if (ret) {
31 printk(KERN_ERR "cannot get irq %d\n", TX_IRQ(port));
32 goto err;
33}
34
35ourport->tx_claimed = 1;
36
37dbg("s3c24xx_serial_startup ok\n");
38
39/* 端口复位代码应该已经为端口控制设置了正确的寄存器 */
40
41return ret;
42
43err:
44s3c24xx_serial_shutdown(port);
45return ret;
46 }
s3c24xx_serial_startup()的“反函数”为s3c24xx_serial_shutdown(),其释放中断,禁止发送和接收,实现如代码清单14.29。
代码清单14.29 S3C2410串口驱动shutdown ()函数
1static void s3c24xx_serial_shutdown(struct uart_port *port)
2{
3 struct s3c24xx_uart_port *ourport = to_ourport(port);
4
5 if (ourport->tx_claimed) {
6 free_irq(TX_IRQ(port), ourport);
7 tx_enabled(port) = 0; //置发送使能状态为0
8 ourport->tx_claimed = 0;
9 }
10
11if (ourport->rx_claimed) {
12 free_irq(RX_IRQ(port), ourport);
13 ourport->rx_claimed = 0;
14 rx_enabled(port) = 0; //置接收使能状态为1
15}
16 }
S3C2410 串口驱动uart_ops结构体的tx_empty()成员函数s3c24xx_serial_tx_empty()用于判断发送缓冲区是否为空,其实现 如代码清单14.30,当使能FIFO模式的时候,判断UFSTATn寄存器,否则判断UTRSTATn寄存器的相应位。
代码清单14.30 S3C2410串口驱动tx_empty()函数
1/* 检查发送缓冲区/FIFO是否为空 */
2static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port)
3{
4 //fifo模式,检查UFSTATn寄存器
5 struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
6 unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT);
7 unsigned long ufcon = rd_regl(port, S3C2410_UFCON);
8
9 if (ufcon &S3C2410_UFCON_FIFOMODE)
10 {
11 if ((ufstat &info->tx_fifomask) != 0 ||//Tx fifo数据数非0
12 (ufstat &info->tx_fifofull)) // Tx fifo满
13 return 0; //0:非空
14
15 return 1; //1:空
16 }
17
18 return s3c24xx_serial_txempty_nofifo(port);
19 }
20
21 static int s3c24xx_serial_txempty_nofifo(struct uart_port *port)
22 {
23 //非fifo模式,检查UTRSTATn寄存器
24 return (rd_regl(port, S3C2410_UTRSTAT) &S3C2410_UTRSTAT_TXE);
25 }
S3C2410 串口驱动uart_ops结构体的start_tx ()成员函数s3c24xx_serial_start_tx()用于启动发送,而stop_rx()成员函数 s3c24xx_serial_stop_rx()用于停止发送,代码清单14.31给出了这2个函数的实现。
代码清单14.31 S3C2410串口驱动start_tx ()、stop_rx()函数
1static void s3c24xx_serial_start_tx(struct uart_port *port)
2{
3 if (!tx_enabled(port)) {//如果端口发送未使能
4 if (port->flags & UPF_CONS_FLOW)
5 s3c24xx_serial_rx_disable(port);
6
7 enable_irq(TX_IRQ(port));//使能发送中断
8 tx_enabled(port) = 1;//置端口发送使能状态为1
9 }
10 }
11
12 static void s3c24xx_serial_stop_tx(struct uart_port *port)
13 {
14if (tx_enabled(port)) {//如果端口发送已使能
15 disable_irq(TX_IRQ(port));//禁止发送中断
16 tx_enabled(port) = 0;//置端口发送使能状态为0
17 if (port->flags & UPF_CONS_FLOW)
18 s3c24xx_serial_rx_enable(port);
19}
20 }
S3C2410 串口驱动uart_ops结构体的stop_rx ()成员函数s3c24xx_serial_stop_rx ()用于停止接收,代码清单14.32给出了这个函数的实现。注意uart_ops中没有start_rx()成员,因为接收并不是由“我方”启动,而是 由“它方”的发送触发“我方”的接收中断,“我方”再被动响应。
代码清单14.32 S3C2410串口驱动stop_rx ()函数
1static void s3c24xx_serial_stop_rx(struct uart_port *port)
2{
3 if (rx_enabled(port)) {//如果接收为使能
4 dbg("s3c24xx_serial_stop_rx: port=%p\n", port);
5 disable_irq(RX_IRQ(port));//禁止接收中断
6 rx_enabled(port) = 0;//置接收使能状态为0
7 }
8}
在S3C2410 串口驱动中,与数据收发关系最密切的函数不是上述uart_ops成员函数,而是s3c24xx_serial_startup()为发送和接收中断注册 的中断处理函数s3c24xx_serial_rx_chars()和s3c24xx_serial_tx_chars()。s3c24xx_serial_rx_chars ()读取URXHn寄存器以获得接收到的字符,并调用uart_insert_char()将该字符添加了tty设备的flip缓冲区中,当接收到64个 字符或者不再能接受到字符后,调用tty_flip_buffer_push()函数向上层“推”tty设备的flip缓冲,其实现如代码清单 14.33。
代码清单14.33 S3C2410串口驱动接收中断处理函数
1static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id, struct
2 pt_regs *regs)
3{
4 struct s3c24xx_uart_port *ourport = dev_id;
5 struct uart_port *port = &ourport->port; //获得uart_port
6 struct tty_struct *tty = port->info->tty; //获得tty_struct
7 unsigned int ufcon, ch, flag, ufstat, uerstat;
8 int max_count = 64;
9
10 while (max_count-- > 0)
11 {
12 ufcon = rd_regl(port, S3C2410_UFCON);
13 ufstat = rd_regl(port, S3C2410_UFSTAT);
14 //如果接收到0个字符
15 if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
16 break;
17
18 uerstat = rd_regl(port, S3C2410_UERSTAT);
19 ch = rd_regb(port, S3C2410_URXH); //读出字符
20
21 if (port->flags &UPF_CONS_FLOW)
22 {
23 int txe = s3c24xx_serial_txempty_nofifo(port);
24
25 if (rx_enabled(port)) //如果端口为使能接收状态
26 {
27 if (!txe) //如果发送缓冲区为空
28 {
29 rx_enabled(port) = 0; //置端口为使能接收状态为0
30 continue;
31 }
32 }
33 else //端口为禁止接收状态
34 {
35 if (txe)//如果发送缓冲区非空
36 {
37 ufcon |= S3C2410_UFCON_RESETRX;
38 wr_regl(port, S3C2410_UFCON, ufcon);
39 rx_enabled(port) = 1;//置端口为使能接收状态为1
40 goto out;
41 }
42 continue;
43 }
44 }
45
46 /* 将接收到的字符写入buffer */
47 flag = TTY_NORMAL;
48 port->icount.rx++;
49
50 if (unlikely(uerstat &S3C2410_UERSTAT_ANY))
51 {
52 dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n", ch, uerstat);
53
54 if (uerstat &S3C2410_UERSTAT_BREAK)
55 {
56 dbg("break!\n");
57 port->icount.brk++;
58 if (uart_handle_break(port))
59 goto ignore_char;
60 }
61
62 if (uerstat &S3C2410_UERSTAT_FRAME)
63 port->icount.frame++;
64 if (uerstat &S3C2410_UERSTAT_OVERRUN)
65 port->icount.overrun++;
66
67 uerstat &= port->read_status_mask;
68
69 if (uerstat &S3C2410_UERSTAT_BREAK)
70 flag = TTY_BREAK;
71 else if (uerstat &S3C2410_UERSTAT_PARITY)
72 flag = TTY_PARITY;
73 else if (uerstat &(S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_OVERRUN))
74 flag = TTY_FRAME;
75 }
76
77 if (uart_handle_sysrq_char(port, ch, regs)) //处理sysrq字符
78 goto ignore_char;
79 //插入字符到tty设备的flip 缓冲
80 uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag);
81
82 ignore_char: continue;
83 }
84 tty_flip_buffer_push(tty);//刷新tty设备的flip 缓冲
85
86 out: return IRQ_HANDLED;
87 }
上述代码第80行的uart_insert_char()函数是串口核心层对tty_insert_flip_char()的封装,它作为内联函数被定义于serial_core.h文件中。
如代码清单14.34,s3c24xx_serial_tx_chars()读取uart_info中环形缓冲区中的字符,写入调用UTXHn寄存器。
代码清单14.34 S3C2410串口驱动发送中断处理函数
1static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id, struct pt_regs
2 *regs)
3{
4 struct s3c24xx_uart_port *ourport = id;
5 struct uart_port *port = &ourport->port;
6 struct circ_buf *xmit = &port->info->xmit;//得到环形缓冲区
7 int count = 256; //最多1次发256个字符
8
9 if (port->x_char) //如果定义了xchar,发送
10 {
11 wr_regb(port, S3C2410_UTXH, port->x_char);
12 port->icount.tx++;
13 port->x_char = 0;
14 goto out;
15 }
16
17 /* 如果没有更多的字符需要发送,或者uart Tx停止,则停止uart并退出 */
18 if (uart_circ_empty(xmit) || uart_tx_stopped(port))
19 {
20 s3c24xx_serial_stop_tx(port);
21 goto out;
22 }
23
24 /* 尝试把环行buffer中的数据发空 */
25 while (!uart_circ_empty(xmit) && count-- > 0)
26 {
27 if (rd_regl(port, S3C2410_UFSTAT) &ourport->info->tx_fifofull)
28 break;
29
30 wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
31 xmit->tail = (xmit->tail + 1) &(UART_XMIT_SIZE - 1);
32 port->icount.tx++;
33 }
34 /* 如果环形缓冲区中剩余的字符少于WAKEUP_CHARS,唤醒上层 */
35 if (uart_circ_chars_pending(xmit) 如果发送环形buffer为空
39 s3c24xx_serial_stop_tx(port); //停止发送
40
41 out: return IRQ_HANDLED;
42 }
上述代码第35行的宏WAKEUP_CHARS的含义为:当发送环形缓冲区中的字符数小于该数时,驱动将请求上层向下传递更多的数据,uart_write_wakeup()完成此目的。
uart_circ_chars_pending()、uart_circ_empty()是定义于serial_core.h中的宏,分别返回环形缓冲区剩余的字符数以及判断缓冲区是否为空。
14.7.5 S3C2410串口线路设置
S3C2410 串口驱动uart_ops结构体的set_termios()成员函数用于改变端口的参数设置,包括波特率、字长、停止位、奇偶校验等,它会根据传递给它 的port、termios参数成员的值设置S3C2410 UART的ULCONn、UCONn、UMCONn等寄存器,其实现如代码清单14.35。
代码清单14.35 S3C2410串口驱动set_termios()函数
1 static void s3c24xx_serial_set_termios(struct uart_port *port,
2 struct termios *termios,
3 struct termios *old)
4 {
5 struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
6 struct s3c24xx_uart_port *ourport = to_ourport(port);
7 struct s3c24xx_uart_clksrc *clksrc = NULL;
8 struct clk *clk = NULL;
9 unsigned long flags;
10 unsigned int baud, quot;
11 unsigned int ulcon;
12 unsigned int umcon;
13
14 /* 不支持modem控制信号线 */
15 termios->c_cflag &= ~(HUPCL | CMSPAR);
16 termios->c_cflag |= CLOCAL;
17
18 /* 请求内核计算分频以便产生对应的波特率 */
19 baud = uart_get_baud_rate(port, termios, old, 0, 115200*8);
20
21 if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
22 quot = port->custom_divisor;
23 else
24 quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud);
25
26 /* 检查以确定是否需要改变时钟源 */
27 if (ourport->clksrc != clksrc || ourport->baudclk != clk) {
28 s3c24xx_serial_setsource(port, clksrc);
29
30 if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) {
31 clk_disable(ourport->baudclk);
32 ourport->baudclk= NULL;
33 }
34
35 clk_enable(clk);
36
37 ourport->clksrc = clksrc;
38 ourport->baudclk = clk;
39 }
40
41 /* 设置字长 */
42 switch (termios->c_cflag & CSIZE) {
43 case CS5:
44 dbg("config: 5bits/char\n");
45 ulcon = S3C2410_LCON_CS5;
46 break;
47 case CS6:
48 dbg("config: 6bits/char\n");
49 ulcon = S3C2410_LCON_CS6;
50 break;
51 case CS7:
52 dbg("config: 7bits/char\n");
53 ulcon = S3C2410_LCON_CS7;
54 break;
55 case CS8:
56 default:
57 dbg("config: 8bits/char\n");
58 ulcon = S3C2410_LCON_CS8;
59 break;
60 }
61
62 /* 保留以前的lcon IR设置 */
63 ulcon |= (cfg->ulcon & S3C2410_LCON_IRM);
64
65 if (termios->c_cflag & CSTOPB)
66 ulcon |= S3C2410_LCON_STOPB;
67 /* 设置是否采用RTS、CTS自动流空 */
68 umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0;
69
70 if (termios->c_cflag & PARENB) {
71 if (termios->c_cflag & PARODD)
72 ulcon |= S3C2410_LCON_PODD;//计校验
73 else
74 ulcon |= S3C2410_LCON_PEVEN;//偶校验
75 } else {
76 ulcon |= S3C2410_LCON_PNONE;//无校验
77 }
78
79 spin_lock_irqsave(&port->lock, flags);
80
81 dbg("setting ulcon to %08x, brddiv to %d\n", ulcon, quot);
82
83 wr_regl(port, S3C2410_ULCON, ulcon);
84 wr_regl(port, S3C2410_UBRDIV, quot);
85 wr_regl(port, S3C2410_UMCON, umcon);
86
87 dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
88 rd_regl(port, S3C2410_ULCON),
89 rd_regl(port, S3C2410_UCON),
90 rd_regl(port, S3C2410_UFCON));
91
92 /* 更新端口的超时 */
93 uart_update_timeout(port, termios->c_cflag, baud);
94
95 /* 我们对什么字符状态状态标志感兴趣?*/
96 port->read_status_mask = S3C2410_UERSTAT_OVERRUN;
97 if (termios->c_iflag & INPCK)
98 port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;
99
100/* 我们要忽略什么字符状态标志?*/
101port->ignore_status_mask = 0;
102if (termios->c_iflag & IGNPAR)
103 port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN;
104if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)
105 port->ignore_status_mask |= S3C2410_UERSTAT_FRAME;
106
107/* 如果CREAD未设置,忽略所用字符 */
108if ((termios->c_cflag & CREAD) == 0)
109 port->ignore_status_mask |= RXSTAT_DUMMY_READ;
110
111spin_unlock_irqrestore(&port->lock, flags);
112 }
由于S3C2410集成UART并不包含完整的Modem控制信号线,因此其uart_ops结构体的get_mctrl()、set_mctrl()成员 函数的实现非常简单,如代码清单14.36,get_mctrl()返回DSR一直有效,而CTS则根据UMSTATn寄存器的内容获得, set_mctrl()目前为空。
代码清单14.36 S3C2410串口驱动get_mctrl()和set_mctrl()函数
1static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port)
2{
3 unsigned int umstat = rd_regb(port,S3C2410_UMSTAT);
4
5 if (umstat & S3C2410_UMSTAT_CTS)//CTS信号有效(低电平)
6 return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
7 else
8 return TIOCM_CAR | TIOCM_DSR;
9}
10
11 static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
12 {
13/* todo:可能移除AFC,并手工进行CTS */
14 }
14.8
总结
TTY设备驱动的主体工作围绕tty_driver这个结构体的成员函数展开,主要应实现其中的数据发送和接收流程以及tty设备线路设置接口函数。
针对串口,内核实现了串口核心层,这个层实现了串口设备通用的tty_driver。因此,串口设备驱动的主体工作从tty_driver转移到了uart_driver。

分享到:
评论

相关推荐

    Linux设备模型之uart驱动架构分析

    UART驱动架构涉及到了多个层次的封装,以及相关数据结构的设计和操作,下面详细分析这些知识点。 首先,UART驱动架构是建立在tty_driver之上的封装层。在Linux系统中,终端(TTY)和串口(Serial)之间有着紧密的...

    自写TQ2440的UART1驱动程序

    在嵌入式系统开发中,驱动程序是连接硬件设备与操作系统之间的重要桥梁,它使得操作系统可以有效地管理和控制硬件资源。本文将深入探讨如何编写自定义的TQ2440平台上的UART1驱动程序,主要关注中断接收和轮询发送...

    16550_serpi-master_16550_16550驱动_linux串口_UART16550_

    在Linux内核中,串口驱动属于字符设备驱动,负责将来自用户空间的I/O请求转化为对硬件的操作。16550驱动程序是Linux内核的一部分,它实现了与硬件交互的底层逻辑,包括初始化、配置波特率、读写操作以及错误处理等...

    linux设备模型之uart驱动架构分析

    接着前面的终端控制台分析,接下来分析serial的驱动。在linux中,serial也对应着终端,通常被称为串口终端。...从上图可以看到,uart设备是继tty_driver的又一层封装。实际上uart_driver就是对应tty_driver.

    GD32F303RCT6芯片的UART串口驱动程序

    在嵌入式系统开发中,UART(通用异步收发传输器)是一种常见的通信接口,用于设备之间的串行通信。GD32F303RCT6是一款基于ARM Cortex-M3内核的高性能微控制器,由GD Microsystems生产。这款芯片集成了多种外设,包括...

    LINUX 下SPI转UART驱动

    在Linux操作系统中,SPI(Serial Peripheral Interface)和UART(Universal Asynchronous Receiver/Transmitter)...通过编写这样的驱动,你可以实现SPI设备与UART设备之间的无缝通信,扩展了系统的兼容性和应用范围。

    uart.rar_Linux 串口驱动_arm uart_arm 串口_linux uart_linux uart 驱动

    5. **设备注册和注销**:驱动程序需要将UART设备注册到Linux设备模型中,以便用户空间程序能够通过/dev节点访问。当设备不再使用时,驱动程序会注销设备。 在S3C410这样的ARM处理器上,UART驱动可能需要对特定的...

    Uart 串口调试助手+串口驱动

    UART(通用异步收发传输器)是一种广泛用于嵌入式系统和计算机之间的串行通信接口,它允许设备间以较低的数据速率交换数据。在本文中,我们将详细探讨UART串口通信、串口调试助手SSCOM5.13.1以及CH341串口驱动,这些...

    UART0驱动程序.rar

    UART0驱动程序是针对EFM8SB10F8G微控制器进行串行通信的关键组件。EFM8SB10F8G是一款基于8051内核的微控制器,由Silicon Labs(芯科实验室)制造,适用于各种低功耗、高性能的应用。在该芯片上,UART(通用异步收发...

    E200 UART驱动

    E200 UART驱动是指针对E200处理器的UART功能进行的操作系统层面的编程,以实现设备与操作系统之间的通信。在嵌入式系统开发中,理解并编写这样的驱动程序对于设备的正常工作至关重要。 首先,我们要了解UART的基本...

    ADSP-BF533的例程源码12:UART串口驱动

    开发者可以通过阅读和分析这些代码,了解UART驱动的具体实现细节。 总结来说,"ADSP-BF533的例程源码12:UART串口驱动"是一个学习和实践ADSP-BF533处理器UART通信的关键资源,涵盖了UART的基本概念、硬件接口、软件...

    uart.rar_ucos uart_ucos 驱动

    设备驱动层负责与硬件交互,包括初始化硬件、设置波特率、发送和接收数据等低级操作。这部分代码通常涉及对UART寄存器的直接读写。例如,通过设置波特率寄存器实现不同速度的通信,通过数据寄存器进行数据的发送和...

    Linux 蓝牙协议栈的USB+设备驱动

    - **设备驱动层**:这一层主要负责与具体的硬件交互,比如USB设备或UART接口。 - **蓝牙核心及HCI层**:这部分处理蓝牙主机控制器接口(HCI),用于控制蓝牙芯片的底层通信。 - **L2CAP与SCO音频层**:L2CAP(逻辑链路...

    串口驱动程序分析

    ### 串口驱动程序分析 #### 一、概述 串口通信作为一种常见的数据传输方式,在嵌入式系统中被广泛运用。本篇文章将基于Mini2440平台深入解析串口驱动程序的工作原理及其内部机制,并对串口的硬件结构进行简要说明...

    蓝牙驱动及Bluez使用流程分析

    配置UART驱动需要正确设置波特率、奇偶校验等参数,并确保设备树或内核模块正确识别蓝牙控制器。 **2. `hciattach`工作原理:** `hciattach`是BlueZ工具集中的一个重要工具,用于初始化和附加到蓝牙控制器。它可以...

    Linux UART

    Linux UART,全称为通用异步接收发送器(Universal Asynchronous Receiver-Transmitter),是计算机硬件中常见的串行通信接口,...理解和掌握这些知识对于进行嵌入式系统开发、设备驱动编程或者系统调试工作至关重要。

    16550驱动程序完整例程

    为了深入理解驱动工作原理,你需要阅读源代码,分析每个模块的功能,同时也可以通过编译和调试来验证代码的正确性。 总的来说,"16550驱动程序完整例程"是一个宝贵的资源,它能让你掌握驱动开发的核心技术,并为...

    uart.zip_C8051f120 lcd_UART 驱动LCD

    通过以上分析,我们可以看出,这个项目的核心在于理解C8051F120的UART外设操作,以及LCD 240x64的显示特性,编写出有效的驱动程序以实现两者之间的高效通信。在实际开发过程中,还需要关注电源管理、功耗优化、抗...

    uart裸板驱动

    同时,也可以通过连接另一个UART设备进行通信测试,确保发送和接收数据的正确性。 在开发过程中,理解硬件手册和参考文档至关重要,它们提供了关于UART硬件特性和寄存器的详细信息。通过熟练掌握这些知识,可以有效...

Global site tag (gtag.js) - Google Analytics