linux 串口 驱动 理解

一、核心数据结构

串口驱动有3个核心数据结构,它们都定义在

1、uart_driver

uart_driver包含了串口设备名、串口驱动名、主次设备号、串口控制台(可选)等信息,还封装了tty_driver

(底层串口驱动无需关心tty_driver)

struct uart_driver {

struct module *owner; /* 拥有该uart_driver的模块,一般为THIS_MODULE */

const char *driver_name; /* 串口驱动名,串口设备文件名以驱动名为基础 */

const char *dev_name; /* 串口设备名 */

int major; /* 主设备号 */

int minor; /* 次设备号 */

int nr; /* 该uart_driver支持的串口个数(最大) */

struct console *cons; /* 其对应的console.若该uart_driver支持serial console,否则为NULL */

/*

* these are private; the low level driver should not

* touch these; they should be initialised to NULL

*/

struct uart_state *state;

struct tty_driver *tty_driver;

};

2、uart_port

uart_port用于描述串口端口的I/O端口或I/O内存地址、FIFO大小、端口类型、串口时钟等信息。

实际上,一个uart_port实例对应一个串口设备

struct uart_port {

spinlock_t lock; /* 串口端口锁 */

unsigned int iobase; /* IO端口基地址 */

unsigned char __iomem *membase; /* IO内存基地址,经映射(如ioremap)后的IO内存虚拟基地址 */

unsigned int irq; /* 中断号 */

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

unsigned int fifosize; /* 串口FIFO缓冲大小 */

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

unsigned char regshift; /* 寄存器位移 */

unsigned char iotype; /* IO访问方式 */

unsigned char unused1;

#define UPIO_PORT (0) /* IO端口 */

#define UPIO_HUB6 (1)

#define UPIO_MEM (2) /* IO内存 */

#define UPIO_MEM32 (3)

#define UPIO_AU (4) /* Au1x00 type IO */

#define UPIO_TSI (5) /* Tsi108/109 type IO */

#define UPIO_DWAPB (6) /* DesignWare APB UART */

#define UPIO_RM9000 (7) /* RM9000 type IO */

unsigned int read_status_mask; /* 关心的Rx error status */

unsigned int ignore_status_mask;/* 忽略的Rx error status */

struct uart_info *info; /* pointer to parent info */

struct uart_icount icount; /* 计数器 */

struct console *cons; /* console结构体 */

#ifdef CONFIG_SERIAL_CORE_CONSOLE

unsigned long sysrq; /* sysrq timeout */

#endif

upf_t flags;

#define UPF_FOURPORT ((__force upf_t) (1 << 1))

#define UPF_SAK ((__force upf_t) (1 << 2))

#define UPF_SPD_MASK ((__force upf_t) (0x1030))

#define UPF_SPD_HI ((__force upf_t) (0x0010))

#define UPF_SPD_VHI ((__force upf_t) (0x0020))

#define UPF_SPD_CUST ((__force upf_t) (0x0030))

#define UPF_SPD_SHI ((__force upf_t) (0x1000))

#define UPF_SPD_WARP ((__force upf_t) (0x1010))

#define UPF_SKIP_TEST ((__force upf_t) (1 << 6))

#define UPF_AUTO_IRQ ((__force upf_t) (1 << 7))

#define UPF_HARDPPS_CD ((__force upf_t) (1 << 11))

#define UPF_LOW_LATENCY ((__force upf_t) (1 << 13))

#define UPF_BUGGY_UART ((__force upf_t) (1 << 14))

#define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16))

#define UPF_CONS_FLOW ((__force upf_t) (1 << 23))

#define UPF_SHARE_IRQ ((__force upf_t) (1 << 24))

#define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28))

#define UPF_FIXED_PORT ((__force upf_t) (1 << 29))

#define UPF_DEAD ((__force upf_t) (1 << 30))

#define UPF_IOREMAP ((__force upf_t) (1 << 31))

#define UPF_CHANGE_MASK ((__force upf_t) (0x17fff))

#define UPF_USR_MASK ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))

unsigned int mctrl; /* 当前的moden设置 */

unsigned int timeout; /* character-based timeout */

unsigned int type; /* 端口类型 */

const struct uart_ops *ops; /* 串口端口操作函数集 */

unsigned int custom_divisor;

unsigned int line; /* 端口索引 */

resource_size_t mapbase; /* IO内存物理基地址,可用于ioremap */

struct device *dev; /* 父设备 */

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

unsigned char suspended;

unsigned char unused[2];

void *private_data; /* 端口私有数据,一般为platform数据指针 */

};

uart_info有两个成员在底层串口驱动会用到:xmit和tty。用户空间程序通过串口发送数据时,

上层驱动将用户数据保存在xmit;而串口发送中断处理函数就是通过xmit获取到用户数据并将它

们发送出去。串口接收中断处理函数需要通过tty将接收到的数据传递给行规则层。

/* uart_info实例仅在串口端口打开时有效,它可能在串口关闭时被串口核心层释放。因此,在使用uart_port的uart_info成员时必须保证串口已打开。底层驱动和核心层驱动都可以修改uart_info实例。

* This is the state information which is only valid when the port

* is open; it may be freed by the core driver once the device has

* been closed. Either the low level driver or the core can modify

* stuff here.

*/

struct uart_info {

struct tty_struct *tty;

struct circ_buf xmit;

uif_t flags;

/*

* Definitions for info->flags. These are _private_ to serial_core, and

* are specific to this structure. They may be queried by low level drivers.

*/

#define UIF_CHECK_CD ((__force uif_t) (1 << 25))

#define UIF_CTS_FLOW ((__force uif_t) (1 << 26))

#define UIF_NORMAL_ACTIVE ((__force uif_t) (1 << 29))

#define UIF_INITIALIZED ((__force uif_t) (1 << 31))

#define UIF_SUSPENDED ((__force uif_t) (1 << 30))

int blocked_open;

struct tasklet_struct tlet;

wait_queue_head_t open_wait;

wait_queue_head_t delta_msr_wait;

};

uart_iconut为串口信息计数器,包含了发送字符计数、接收字符计数等。在串口的发送中断处理函数

和接收中断处理函数中,我们需要管理这些计数。

struct uart_icount {

__u32 cts;

__u32 dsr;

__u32 rng;

__u32 dcd;

__u32 rx; /* 发送字符计数 */

__u32 tx; /* 接受字符计数 */

__u32 frame; /* 帧错误计数 */

__u32 overrun; /* Rx FIFO溢出计数 */

__u32 parity; /* 帧校验错误计数 */

__u32 brk; /* break计数 */

__u32 buf_overrun;

};

3、uart_ops

uart_ops涵盖了串口驱动可对串口设备进行的所有操作。

/*

* This structure describes all the operations that can be

* done on the physical hardware.

*/

struct uart_ops {

unsigned int (*tx_empty)(struct uart_port *); /* 串口的Tx FIFO缓存是否为空 */

void (*set_mctrl)(struct uart_port *, unsigned int mctrl); /* 设置串口modem控制 */

unsigned int (*get_mctrl)(struct uart_port *); /* 获取串口modem控制 */

void (*stop_tx)(struct uart_port *); /* 禁止串口发送数据 */

void (*start_tx)(struct uart_port *); /* 使能串口发送数据 */

void (*send_xchar)(struct uart_port *, char ch);/* 发送xChar */

void (*stop_rx)(struct uart_port *); /* 禁止串口接收数据 */

void (*enable_ms)(struct uart_port *); /* 使能modem的状态信号 */

void (*break_ctl)(struct uart_port *, int ctl); /* 设置break信号 */

int (*startup)(struct uart_port *); /* 启动串口,应用程序打开串口设备文件时,该函数会被调用 */

void (*shutdown)(struct uart_port *); /* 关闭串口,应用程序关闭串口设备文件时,该函数会被调用 */

void (*set_termios)(struct uart_port *, struct ktermios *new, struct ktermios*old); /* 设置串口参数 */

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

unsigned int oldstate); /* 串口电源管理 */

int (*set_wake)(struct uart_port *, unsigned int state); /* */

const char *(*type)(struct uart_port *); /* 返回一描述串口类型的字符串 */

void (*release_port)(struct uart_port *); /* 释放串口已申请的IO端口/IO内存资源,必要时还需iounmap */

int (*request_port)(struct uart_port *); /* 申请必要的IO端口/IO内存资源,必要时还可以重新映射串口端口 */

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); /* IO控制 */

};

二、串口驱动API

1、uart_register_driver

/* 功能: uart_register_driver用于将串口驱动uart_driver注册到内核(串口核心层)中,通常在模块初始化函数调用该函数。

* 参数 drv:要注册的uart_driver

* 返回值: 成功,返回0;否则返回错误码

*/

int uart_register_driver(struct uart_driver *drv)

2、uart_unregister_driver

/* 功能: uart_unregister_driver用于注销我们已注册的uart_driver,通常在模块卸载函数调用该函数

* 参数 drv:要注销的uart_driver

* 返回值: 成功,返回0;否则返回错误码

*/

void uart_unregister_driver(struct uart_driver *drv)

3、uart_add_one_port

/* 功能: uart_add_one_port用于为串口驱动添加一个串口端口,通常在探测到设备后(驱动的设备probe方法)调用该函数

* 参数 drv:串口驱动

* port:要添加的串口端口

* 返回值: 成功,返回0;否则返回错误码

*/

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

4、uart_remove_one_port

/* 功能: uart_remove_one_port用于删除一个已添加到串口驱动中的串口端口,通常在驱动卸载时调用该函数

* 参数 drv: 串口驱动

* port: 要删除的串口端口

* 返回值: 成功,返回0;否则返回错误码

*/

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

5、uart_write_wakeup

/* 功能: uart_write_wakeup唤醒上层因向串口端口写数据而阻塞的进程,通常在串口发送中断处理函数中调用该函数

* 参数 port:需要唤醒写阻塞进程的串口端口

*/

void uart_write_wakeup(struct uart_port *port)

6、uart_suspend_port

/* 功能: uart_suspend_port用于挂起特定的串口端口

* 参数 drv: 要挂起的串口端口所属的串口驱动

* port:要挂起的串口端口

* 返回值: 成功返回0;否则返回错误码

*/

int uart_suspend_port(struct uart_driver *drv, struct uart_port *port)

7、uart_resume_port

/* 功能: uart_resume_port用于恢复某一已挂起的串口

* 参数 drv: 要恢复的串口端口所属的串口驱动

* port:要恢复的串口端口

* 返回值: 成功返回0;否则返回错误码

*/

int uart_resume_port(struct uart_driver *drv, struct uart_port *port)

8、uart_get_baud_rate

/* 功能: uart_get_baud_rate通过解码termios结构体来获取指定串口的波特率

* 参数 port: 要获取波特率的串口端口

* termios:当前期望的termios配置(包含串口波特率)

* old: 以前的termios配置,可以为NULL

* min: 可接受的最小波特率

* max: 可接受的最大波特率

* 返回值: 串口的波特率

*/

unsigned int

uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,

struct ktermios *old, unsigned int min, unsigned int max)

9、uart_get_divisor

/* 功能: uart_get_divisor用于计算某一波特率的串口时钟分频数(串口波特率除数)

* 参数 port:要计算时钟分频数的串口端口

* baud:期望的波特率

*返回值: 串口时钟分频数

*/

unsigned int uart_get_divisor(struct uart_port *port, unsigned int baud)

10、uart_update_timeout

/* 功能: uart_update_timeout用于更新(设置)串口FIFO超时时间

* 参数 port: 要更新超时时间的串口端口

* cflag:termios结构体的cflag值

* baud: 串口的波特率

*/

void uart_update_timeout(struct uart_port *port, unsigned int cflag, unsigned int baud)

11、uart_match_port

/* 功能:uart_match_port用于判断两串口端口是否为同一端口

* 参数 port1、port2:要判断的串口端口

* 返回值:不同返回0;否则返回非0

*/

int uart_match_port(struct uart_port *port1, struct uart_port *port2)

12、uart_console_write

/* 功能: uart_console_write用于向串口端口写一控制台信息

* 参数 port: 要写信息的串口端口

* s: 要写的信息

* count: 信息的大小

* putchar: 用于向串口端口写字符的函数,该函数函数有两个参数:串口端口和要写的字符

*/

void uart_console_write(struct uart_port *port, const char *s,

unsigned int count,

void (*putchar)(struct uart_port *, int))

三、串口驱动例子

该串口驱动例子是我针对s3c2410处理器的串口2(uart2)独立开发的。因为我通过博创2410s开发板的GRPS扩展板来测试该驱动(已通过测试),所以我叫该串口为gprs_uart。

该驱动将串口看作平台(platform)设备。platform可以看作一伪总线,用于将集成于片上系统的轻量级设备与Linux设备驱动模型联系到一起,它包含以下两部分(有关platform的声明都在#include ,具体实现在drivers/base/platform.c):

1、platform设备。我们需要为每个设备定义一个platform_device实例

struct platform_device {

const char *name; /* 设备名 */

int id; /* 设备的id号 */

struct device dev; /* 其对应的device */

u32 num_resources;/* 该设备用有的资源数 */

struct resource *resource; /* 资源数组 */

};

发表于2012-07-17 10:522#

回复:Linux串口驱动程序设计

二、串口驱动API

1、uart_register_driver

/* 功能: uart_register_driver用于将串口驱动uart_driver注册到内核(串口核心层)中,通常在模块初始化函数调用该函数。

* 参数 drv:要注册的uart_driver

* 返回值: 成功,返回0;否则返回错误码

*/

int uart_register_driver(struct uart_driver *drv)

2、uart_unregister_driver

/* 功能: uart_unregister_driver用于注销我们已注册的uart_driver,通常在模块卸载函数调用该函数

* 参数 drv:要注销的uart_driver

* 返回值: 成功,返回0;否则返回错误码

*/

void uart_unregister_driver(struct uart_driver *drv)

3、uart_add_one_port

/* 功能: uart_add_one_port用于为串口驱动添加一个串口端口,通常在探测到设备后(驱动的设备probe方法)调用该函数

* 参数 drv:串口驱动

* port:要添加的串口端口

* 返回值: 成功,返回0;否则返回错误码

*/

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

4、uart_remove_one_port

/* 功能: uart_remove_one_port用于删除一个已添加到串口驱动中的串口端口,通常在驱动卸载时调用该函数

* 参数 drv: 串口驱动

* port: 要删除的串口端口

* 返回值: 成功,返回0;否则返回错误码

*/

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

5、uart_write_wakeup

/* 功能: uart_write_wakeup唤醒上层因向串口端口写数据而阻塞的进程,通常在串口发送中断处理函数中调用该函数

* 参数 port:需要唤醒写阻塞进程的串口端口

*/

void uart_write_wakeup(struct uart_port *port)

6、uart_suspend_port

/* 功能: uart_suspend_port用于挂起特定的串口端口

* 参数 drv: 要挂起的串口端口所属的串口驱动

* port:要挂起的串口端口

* 返回值: 成功返回0;否则返回错误码

*/

int uart_suspend_port(struct uart_driver *drv, struct uart_port *port)

7、uart_resume_port

/* 功能: uart_resume_port用于恢复某一已挂起的串口

* 参数 drv: 要恢复的串口端口所属的串口驱动

* port:要恢复的串口端口

* 返回值: 成功返回0;否则返回错误码

*/

int uart_resume_port(struct uart_driver *drv, struct uart_port *port)

8、uart_get_baud_rate

/* 功能: uart_get_baud_rate通过解码termios结构体来获取指定串口的波特率

* 参数 port: 要获取波特率的串口端口

* termios:当前期望的termios配置(包含串口波特率)

* old: 以前的termios配置,可以为NULL

* min: 可接受的最小波特率

* max: 可接受的最大波特率

* 返回值: 串口的波特率

*/

unsigned int

uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,

struct ktermios *old, unsigned int min, unsigned int max)

9、uart_get_divisor

/* 功能: uart_get_divisor用于计算某一波特率的串口时钟分频数(串口波特率除数)

* 参数 port:要计算时钟分频数的串口端口

* baud:期望的波特率

*返回值: 串口时钟分频数

*/

unsigned int uart_get_divisor(struct uart_port *port, unsigned int baud)

10、uart_update_timeout

/* 功能: uart_update_timeout用于更新(设置)串口FIFO超时时间

* 参数 port: 要更新超时时间的串口端口

* cflag:termios结构体的cflag值

* baud: 串口的波特率

*/

void uart_update_timeout(struct uart_port *port, unsigned int cflag, unsigned int baud)

11、uart_match_port

/* 功能:uart_match_port用于判断两串口端口是否为同一端口

* 参数 port1、port2:要判断的串口端口

* 返回值:不同返回0;否则返回非0

*/

int uart_match_port(struct uart_port *port1, struct uart_port *port2)

12、uart_console_write

/* 功能: uart_console_write用于向串口端口写一控制台信息

* 参数 port: 要写信息的串口端口

* s: 要写的信息

* count: 信息的大小

* putchar: 用于向串口端口写字符的函数,该函数函数有两个参数:串口端口和要写的字符

*/

void uart_console_write(struct uart_port *port, const char *s,

unsigned int count,

void (*putchar)(struct uart_port *, int))

三、串口驱动例子

该串口驱动例子是我针对s3c2410处理器的串口2(uart2)独立开发的。

因为我通过博创2410s开发板的GRPS扩展板来测试该驱动(已通过测试),所以我叫该串口为gprs_uart。

该驱动将串口看作平台(platform)设备。platform可以看作一伪总线,用于将集成于片上系统的轻量级设备

与Linux设备驱动模型联系到一起,它包含以下两部分(有关platform的声明都在

#include ,具体实现在drivers/base/platform.c):

1、platform设备。我们需要为每个设备定义一个platform_device实例

struct platform_device {

const char *name; /* 设备名 */

int id; /* 设备的id号 */

struct device dev; /* 其对应的device */

u32 num_resources;/* 该设备用有的资源数 */

struct resource *resource; /* 资源数组 */

};

为我们的设备创建platform_device实例有两种方法:填充一个platform_device结构体后用

platform_device_register(一次注册一个)或platform_add_devices(一次可以注册多个platform设备)

将platform_device注册到内核;更简单的是使用platform_device_register_simple来

建立并注册我们的platform_device。

2、 platform驱动。platform设备由platform驱动进行管理。当设备加入到系统中时,platform_driver的

probe方法会 被调用来见对应的设备添加或者注册到内核;当设备从系统中移除时,platform_driver的

remove方法会被调用来做一些清理工作,如移除该 设备的一些实例、注销一些已注册到系统中去的东西。

struct platform_driver {

int (*probe)(struct platform_device *);

int (*remove)(struct platform_device *);

void (*shutdown)(struct platform_device *);

int (*suspend)(struct platform_device *, pm_message_t state);

int (*suspend_late)(struct platform_device *, pm_message_t state);

int (*resume_early)(struct platform_device *);

int (*resume)(struct platform_device *);

struct device_driver driver;

};

更详细platform资料可参考网上相关文章。

例子驱动中申请和释放IO内存区的整个过程如下:

insmod gprs_uart.ko→gprs_init_module()→uart_register_driver()→gprs_uart_probe()→ uart_add_one_port()→gprs_uart_config_port()→gprs_uart_request_port()→request_mem_region()

rmmod gprs_uart.ko→gprs_exit_module()→uart_unregister_driver()→gprs_uart_remove()→uart_remove_one_port()→gprs_uart_release_port()→release_mem_region()

例子驱动中申请和释放IRQ资源的整个过程如下:

open /dev/gprs_uart→gprs_uart_startup()→request_irq()

close /dev/gprs_uart→gprs_uart_shutdown()→free_irq()

想了解更详细的调用过程可以在驱动模块各函数头插入printk(KERN_DEBUG "%s\n", __FUNCTION__);并在函数尾插入printk(KERN_DEBUG "%s done\n", __FUNCTION__);

下面是串口驱动例子和其GPRS测试程序源码

#include

#include

#include /* printk() */

#include /* kmalloc() */

#include /* everything... */

#include /* error codes */

#include /* size_t */

#include /* O_ACCMODE */

#include /* cli(), *_flags */

#include /* copy_*_user */

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define DEV_NAME "gprs_uart" /* 设备名 */

/* 这里将串口的主设备号设为0,则串口设备编号由内核动态分配;你也可指定串口的设备编号 */

#define GPRS_UART_MAJOR 0 /* 主设备号 */

#define GPRS_UART_MINOR 0 /* 次设备号 */

#define GPRS_UART_FIFO_SIZE 16 /* 串口FIFO的大小 */

#define RXSTAT_DUMMY_READ (0x10000000)

#define MAP_SIZE (0x100) /* 要映射的串口IO内存区大小 */

/* 串口发送中断号 */

#define TX_IRQ(port) ((port)->irq + 1)

/* 串口接收中断号 */

#define RX_IRQ(port) ((port)->irq)

/* 允许串口接收字符的标志 */

#define tx_enabled(port) ((port)->unused[0])

/* 允许串口发送字符的标志 */

#define rx_enabled(port) ((port)->unused[1])

/* 获取寄存器地址 */

#define portaddr(port, reg) ((port)->membase + (reg))

/* 读8位宽的寄存器 */

#define rd_regb(port, reg) (ioread8(portaddr(port, reg)))

/* 读32位宽的寄存器 */

#define rd_regl(port, reg) (ioread32(portaddr(port, reg)))

/* 写8位宽的寄存器 */

#define wr_regb(port, reg, val) \

do { iowrite8(val, portaddr(port, reg)); } while(0)

/* 写32位宽的寄存器 */

#define wr_regl(port, reg, val) \

do { iowrite32(val, portaddr(port, reg)); } while(0)

/* 禁止串口发送数据 */

static void gprs_uart_stop_tx(struct uart_port *port)

{

if (tx_enabled(port)) /* 若串口已启动发送 */

{

disable_irq(TX_IRQ(port)); /* 禁止发送中断 */

tx_enabled(port) = 0; /* 设置串口为未启动发送 */

}

}

/* 使能串口发送数据 */

static void gprs_uart_start_tx(struct uart_port *port)

{

if (!tx_enabled(port)) /* 若串口未启动发送 */

{

enable_irq(TX_IRQ(port)); /* 使能发送中断 */

tx_enabled(port) = 1; /* 设置串口为已启动发送 */

}

}

/* 禁止串口接收数据 */

static void gprs_uart_stop_rx(struct uart_port *port)

{

if (rx_enabled(port)) /* 若串口已启动接收 */

{

disable_irq(RX_IRQ(port)); /* 禁止接收中断 */

rx_enabled(port) = 0; /* 设置串口为未启动接收 */

}

}

/* 使能modem的状态信号 */

static void gprs_uart_enable_ms(struct uart_port *port)

{

}

/* 串口的Tx FIFO缓存是否为空 */

static unsigned int gprs_uart_tx_empty(struct uart_port *port)

{

int ret = 1;

unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT);

unsigned long ufcon = rd_regl(port, S3C2410_UFCON);

if (ufcon & S3C2410_UFCON_FIFOMODE) /* 若使能了FIFO */

{

if ((ufstat & S3C2410_UFSTAT_TXMASK) != 0 || /* 0

(ufstat & S3C2410_UFSTAT_TXFULL)) /* FIFO满 */

ret = 0;

}

else /* 若未使能了FIFO,则判断发送缓存和发送移位寄存器是否均为空 */

{

ret = rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE;

}

return ret;

}

/* 获取串口modem控制,因为uart2无modem控制,所以CTS、DSR直接返回有效 */

static unsigned int gprs_uart_get_mctrl(struct uart_port *port)

{

return (TIOCM_CTS | TIOCM_DSR | TIOCM_CAR);

}

/* 设置串口modem控制 */

static void gprs_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)

{

}

/* 设置break信号 */

static void gprs_uart_break_ctl(struct uart_port *port, int break_state)

{

unsigned long flags;

unsigned int ucon;

spin_lock_irqsave(&port->lock, flags);

ucon = rd_regl(port, S3C2410_UCON);

if (break_state)

ucon |= S3C2410_UCON_SBREAK;

else

ucon &= ~S3C2410_UCON_SBREAK;

wr_regl(port, S3C2410_UCON, ucon);

spin_unlock_irqrestore(&port->lock, flags);

}

/* 返回Rx FIFO已存多少数据 */

static int gprs_uart_rx_fifocnt(unsigned long ufstat)

{

/* 若Rx FIFO已满,返回FIFO的大小 */

if (ufstat & S3C2410_UFSTAT_RXFULL)

return GPRS_UART_FIFO_SIZE;

/* 若FIFO未满,返回Rx FIFO已存了多少字节数据 */

return (ufstat & S3C2410_UFSTAT_RXMASK) >> S3C2410_UFSTAT_RXSHIFT;

}

#define S3C2410_UERSTAT_PARITY (0x1000)

/* 串口接收中断处理函数,获取串口接收到的数据,并将这些数据递交给行规则层 */

static irqreturn_t gprs_uart_rx_chars(int irq, void *dev_id)

{

struct uart_port *port = dev_id;

struct tty_struct *tty = port->info->tty;

unsigned int ufcon, ch, flag, ufstat, uerstat;

int max_count = 64;

/* 循环接收数据,最多一次中断接收64字节数据 */

while (max_count-- > 0)

{

ufcon = rd_regl(port, S3C2410_UFCON);

ufstat = rd_regl(port, S3C2410_UFSTAT);

/* 若Rx FIFO无数据了,跳出循环 */

if (gprs_uart_rx_fifocnt(ufstat) == 0)

break;

/* 读取Rx error状态寄存器 */

uerstat = rd_regl(port, S3C2410_UERSTAT);

/* 读取已接受到的数据 */

ch = rd_regb(port, S3C2410_URXH);

/* insert the character into the buffer */

/* 先将tty标志设为正常 */

flag = TTY_NORMAL;

/* 递增接收字符计数器 */

port->icount.rx++;

/* 判断是否存在Rx error

* if (unlikely(uerstat & S3C2410_UERSTAT_ANY))等同于

* if (uerstat & S3C2410_UERSTAT_ANY)

* 只是unlikely表示uerstat & S3C2410_UERSTAT_ANY的值为假的可能性大一些

* 另外还有一个likely(value)表示value的值为真的可能性更大一些

*/

if (unlikely(uerstat & S3C2410_UERSTAT_ANY))

{

/* 若break错误,递增icount.brk计算器 */

if (uerstat & S3C2410_UERSTAT_BREAK)

{

port->icount.brk++;

if (uart_handle_break(port))

goto ignore_char;

}

/* 若frame错误,递增icount.frame计算器 */

if (uerstat & S3C2410_UERSTAT_FRAME)

port->icount.frame++;

/* 若overrun错误,递增icount.overrun计算器 */

if (uerstat & S3C2410_UERSTAT_OVERRUN)

port->icount.overrun++;

/* 查看我们是否关心该Rx error

* port->read_status_mask保存着我们感兴趣的Rx error status

*/

uerstat &= port->read_status_mask;

/* 若我们关心该Rx error,则将flag设置为对应的error flag */

if (uerstat & S3C2410_UERSTAT_BREAK)

flag = TTY_BREAK;

else if (uerstat & S3C2410_UERSTAT_PARITY)

flag = TTY_PARITY;

else if (uerstat & ( S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_OVERRUN))

flag = TTY_FRAME;

}

/* 处理sys字符 */

if (uart_handle_sysrq_char(port, ch))

goto ignore_char;

/* 将接收到的字符插入到tty设备的flip缓冲 */

uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag);

ignore_char:

continue;

}

/* 刷新tty设备的flip缓冲,将接受到的数据传给行规则层 */

tty_flip_buffer_push(tty);

return IRQ_HANDLED;

}

/* 串口发送中断处理函数,将用户空间的数据(保存在环形缓冲xmit里)发送出去 */

static irqreturn_t gprs_uart_tx_chars(int irq, void *dev_id)

{

struct uart_port *port = dev_id;

struct circ_buf *xmit = &port->info->xmit; /* 获取环线缓冲 */

int count = 256;

/* 若设置了xChar字符 */

if (port->x_char)

{

/* 将该xChar发送出去 */

wr_regb(port, S3C2410_UTXH, port->x_char);

/* 递增发送计数 */

port->icount.tx++;

/* 清除xChar */

port->x_char = 0;

/* 退出中断处理 */

goto out;

}

/* 如果没有更多的字符需要发送(环形缓冲为空),

* 或者uart Tx已停止,

* 则停止uart并退出中断处理函数

*/

if (uart_circ_empty(xmit) || uart_tx_stopped(port))

{

gprs_uart_stop_tx(port);

goto out;

}

/* 循环发送数据,直到环形缓冲为空,最多一次中断发送256字节数据 */

while (!uart_circ_empty(xmit) && count-- > 0)

{

/* 若Tx FIFO已满,退出循环 */

if (rd_regl(port, S3C2410_UFSTAT) & S3C2410_UFSTAT_TXFULL)

break;

/* 将要发送的数据写入Tx FIFO */

wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);

/* 移向循环缓冲中下一要发送的数据 */

xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);

port->icount.tx++;

}

/* 如果环形缓冲区中剩余的字符少于WAKEUP_CHARS,唤醒上层 */

if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)

uart_write_wakeup(port);

/* 如果环形缓冲为空,则停止发送 */

if (uart_circ_empty(xmit))

gprs_uart_stop_tx(port);

out:

return IRQ_HANDLED;

}

/* 启动串口端口,在打开该驱动的设备文件时会调用该函数来申请串口中断,并设置串口为可接受,也可发送 */

static int gprs_uart_startup(struct uart_port *port)

{

unsigned long flags;

int ret;

const char *portname = to_platform_device(port->dev)->name;

/* 设置串口为不可接受,也不可发送 */

rx_enabled(port) = 0;

tx_enabled(port) = 0;

spin_lock_irqsave(&port->lock, flags);

/* 申请接收中断 */

ret = request_irq(RX_IRQ(port), gprs_uart_rx_chars, 0, portname, port);

if (ret != 0)

{

printk(KERN_ERR "cannot get irq %d\n", RX_IRQ(port));

return ret;

}

/* 设置串口为允许接收 */

rx_enabled(port) = 1;

/* 申请发送中断 */

ret = request_irq(TX_IRQ(port), gprs_uart_tx_chars, 0, portname, port);

if (ret)

{

printk(KERN_ERR "cannot get irq %d\n", TX_IRQ(port));

rx_enabled(port) = 0;

free_irq(RX_IRQ(port), port);

goto err;

}

/* 设置串口为允许发送 */

tx_enabled(port) = 1;

err:

spin_unlock_irqrestore(&port->lock, flags);

return ret;

}

/* 关闭串口,在关闭驱动的设备文件时会调用该函数,释放串口中断 */

static void gprs_uart_shutdown(struct uart_port *port)

{

rx_enabled(port) = 0; /* 设置串口为不允许接收 */

free_irq(RX_IRQ(port), port); /* 释放接收中断 */

tx_enabled(port) = 0; /* 设置串口为不允许发送 */

free_irq(TX_IRQ(port), port); /* 释放发送中断 */

}

/* 设置串口参数 */

static void gprs_uart_set_termios(struct uart_port *port,

struct ktermios *termios,

struct ktermios *old)

{

unsigned long flags;

unsigned int baud, quot;

unsigned int ulcon, ufcon = 0;

/* 不支持moden控制信号线

* HUPCL: 关闭时挂断moden

* CMSPAR: mark or space (stick) parity

* CLOCAL: 忽略任何moden控制线

*/

termios->c_cflag &= ~(HUPCL | CMSPAR);

termios->c_cflag |= CLOCAL;

/* 获取用户设置的串口波特率,并计算分频数(串口波特率除数quot) */

baud = uart_get_baud_rate(port, termios, old, 0, 115200*8);

if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)

quot = port->custom_divisor;

else

quot = port->uartclk / baud / 16 - 1;

/* 设置数据字长 */

switch (termios->c_cflag & CSIZE)

{

case CS5:

ulcon = S3C2410_LCON_CS5;

break;

case CS6:

ulcon = S3C2410_LCON_CS6;

break;

case CS7:

ulcon = S3C2410_LCON_CS7;

break;

case CS8:

default:

ulcon = S3C2410_LCON_CS8;

break;

}

/* 是否要求设置两个停止位(CSTOPB) */

if (termios->c_cflag & CSTOPB)

ulcon |= S3C2410_LCON_STOPB;

/* 是否使用奇偶检验 */

if (termios->c_cflag & PARENB)

{

if (termios->c_cflag & PARODD) /* 奇校验 */

ulcon |= S3C2410_LCON_PODD;

else /* 偶校验 */

ulcon |= S3C2410_LCON_PEVEN;

}

else /* 无校验 */

{

ulcon |= S3C2410_LCON_PNONE;

}

if (port->fifosize > 1)

ufcon |= S3C2410_UFCON_FIFOMODE | S3C2410_UFCON_RXTRIG8;

spin_lock_irqsave(&port->lock, flags);

/* 设置FIFO控制寄存器、线控制寄存器和波特率除数寄存器 */

wr_regl(port, S3C2410_UFCON, ufcon);

wr_regl(port, S3C2410_ULCON, ulcon);

wr_regl(port, S3C2410_UBRDIV, quot);

/* 更新串口FIFO的超时时限 */

uart_update_timeout(port, termios->c_cflag, baud);

/* 设置我们感兴趣的Rx error */

port->read_status_mask = S3C2410_UERSTAT_OVERRUN;

if (termios->c_iflag & INPCK)

port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;

/* 设置我们忽略的Rx error */

port->ignore_status_mask = 0;

if (termios->c_iflag & IGNPAR)

port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN;

if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)

port->ignore_status_mask |= S3C2410_UERSTAT_FRAME;

/* 若未设置CREAD(使用接收器),则忽略所有Rx error*/

if ((termios->c_cflag & CREAD) == 0)

port->ignore_status_mask |= RXSTAT_DUMMY_READ;

spin_unlock_irqrestore(&port->lock, flags);

}

/* 获取串口类型 */

static const char *gprs_uart_type(struct uart_port *port)

{/* 返回描述串口类型的字符串指针 */

return port->type == PORT_S3C2410 ? "gprs_uart:s3c2410_uart2" : NULL;

}

/* 申请串口一些必要的资源,如IO端口/IO内存资源,必要时还可以重新映射串口端口 */

static int gprs_uart_request_port(struct uart_port *port)

{

struct resource *res;

const char *name = to_platform_device(port->dev)->name;

/* request_mem_region请求分配IO内存,从开始port->mapbase,大小MAP_SIZE

* port->mapbase保存当前串口的寄存器基地址(物理)

* uart2: 0x50008000

*/

res = request_mem_region(port->mapbase, MAP_SIZE, name);

if (res == NULL)

{

printk(KERN_ERR"request_mem_region error: %p\n", res);

return -EBUSY;

}

return 0;

}

/* 释放串口已申请的IO端口/IO内存资源,必要时还需iounmap */

static void gprs_uart_release_port(struct uart_port *port)

{

/* 释放已分配IO内存 */

release_mem_region(port->mapbase, MAP_SIZE);

}

/* 执行串口所需的自动配置 */

static void gprs_uart_config_port(struct uart_port *port, int flags)

{

int retval;

/* 请求串口 */

retval = gprs_uart_request_port(port);

/* 设置串口类型 */

if (flags & UART_CONFIG_TYPE && retval == 0)

port->type = PORT_S3C2410;

}

/* The UART operations structure */

static struct uart_ops gprs_uart_ops = {

.start_tx = gprs_uart_start_tx, /* Start transmitting */

.stop_tx = gprs_uart_stop_tx, /* Stop transmission */

.stop_rx = gprs_uart_stop_rx, /* Stop reception */

.enable_ms = gprs_uart_enable_ms, /* Enable modem status signals */

.tx_empty = gprs_uart_tx_empty, /* Transmitter busy? */

.get_mctrl = gprs_uart_get_mctrl, /* Get modem control */

.set_mctrl = gprs_uart_set_mctrl, /* Set modem control */

.break_ctl = gprs_uart_break_ctl, /* Set break signal */

.startup = gprs_uart_startup, /* App opens GPRS_UART */

.shutdown = gprs_uart_shutdown, /* App closes GPRS_UART */

.set_termios = gprs_uart_set_termios, /* Set termios */

.type = gprs_uart_type, /* Get UART type */

.request_port = gprs_uart_request_port, /* Claim resources associated with a GPRS_UART port */

.release_port = gprs_uart_release_port, /* Release resources associated with a GPRS_UART port */

.config_port = gprs_uart_config_port, /* Configure when driver adds a GPRS_UART port */

};

/* Uart driver for GPRS_UART */

static struct uart_driver gprs_uart_driver = {

.owner = THIS_MODULE, /* Owner */

.driver_name = DEV_NAME, /* Driver name */

.dev_name = DEV_NAME, /* Device node name */

.major = GPRS_UART_MAJOR, /* Major number */

.minor = GPRS_UART_MINOR, /* Minor number start */

.nr = 1, /* Number of UART ports */

};

/* Uart port for GPRS_UART port */

static struct uart_port gprs_uart_port = {

.irq = IRQ_S3CUART_RX2, /* IRQ */

.fifosize = GPRS_UART_FIFO_SIZE, /* Size of the FIFO */

.iotype = UPIO_MEM, /* IO memory */

.flags = UPF_BOOT_AUTOCONF, /* UART port flag */

.ops = &gprs_uart_ops, /* UART operations */

.line = 0, /* UART port number */

.lock = __SPIN_LOCK_UNLOCKED(gprs_uart_port.lock),

};

/* 初始化指定串口端口 */

static int gprs_uart_init_port(struct uart_port *port, struct platform_device *platdev)

{

unsigned long flags;

unsigned int gphcon;

if (platdev == NULL)

return -ENODEV;

port->dev = &platdev->dev;

/* 设置串口波特率时钟频率 */

port->uartclk = clk_get_rate(clk_get(&platdev->dev, "pclk"));

/* 设置串口的寄存器基地址(物理): 0x50008000 */

port->mapbase = S3C2410_PA_UART2;

/* 设置当前串口的寄存器基地址(虚拟): 0xF5008000 */

port->membase = S3C24XX_VA_UART + (S3C2410_PA_UART2 - S3C24XX_PA_UART);

spin_lock_irqsave(&port->lock, flags);

wr_regl(port, S3C2410_UCON, S3C2410_UCON_DEFAULT);

wr_regl(port, S3C2410_ULCON, S3C2410_LCON_CS8 | S3C2410_LCON_PNONE);

wr_regl(port, S3C2410_UFCON, S3C2410_UFCON_FIFOMODE

| S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_RESETBOTH);

/* 将I/O port H的gph6和gph7设置为TXD2和RXD2 */

gphcon = readl(S3C2410_GPHCON);

gphcon &= ~((0x5) << 12);

writel(gphcon, S3C2410_GPHCON);

spin_unlock_irqrestore(&port->lock, flags);

return 0;

}

/* Platform driver probe */

static int __init gprs_uart_probe(struct platform_device *dev)

{

int ret;

/* 初始化串口 */

ret = gprs_uart_init_port(&gprs_uart_port, dev);

if (ret < 0)

{

printk(KERN_ERR"gprs_uart_probe: gprs_uart_init_port error: %d\n", ret);

return ret;

}

/* 添加串口 */

ret = uart_add_one_port(&gprs_uart_driver, &gprs_uart_port);

if (ret < 0)

{

printk(KERN_ERR"gprs_uart_probe: uart_add_one_port error: %d\n", ret);

return ret;

}

/* 将串口uart_port结构体保存在platform_device->dev->driver_data中 */

platform_set_drvdata(dev, &gprs_uart_port);

return 0;

}

/* Called when the platform driver is unregistered */

static int gprs_uart_remove(struct platform_device *dev)

{

platform_set_drvdata(dev, NULL);

/* 移除串口 */

uart_remove_one_port(&gprs_uart_driver, &gprs_uart_port);

return 0;

}

/* Suspend power management event */

static int gprs_uart_suspend(struct platform_device *dev, pm_message_t state)

{

uart_suspend_port(&gprs_uart_driver, &gprs_uart_port);

return 0;

}

/* Resume after a previous suspend */

static int gprs_uart_resume(struct platform_device *dev)

{

uart_resume_port(&gprs_uart_driver, &gprs_uart_port);

return 0;

}

/* Platform driver for GPRS_UART */

static struct platform_driver gprs_plat_driver = {

.probe = gprs_uart_probe, /* Probe method */

.remove = __exit_p(gprs_uart_remove), /* Detach method */

.suspend = gprs_uart_suspend, /* Power suspend */

.resume = gprs_uart_resume, /* Resume after a suspend */

.driver = {

.owner = THIS_MODULE,

.name = DEV_NAME, /* Driver name */

},

};

/* Platform device for GPRS_UART */

struct platform_device *gprs_plat_device;

static int __init gprs_init_module(void)

{

int retval;

/* Register uart_driver for GPRS_UART */

retval = uart_register_driver(&gprs_uart_driver);

if (0 != retval)

{

printk(KERN_ERR "gprs_init_module: can't register the GPRS_UART driver %d\n",retval);

return retval;

}

/* Register platform device for GPRS_UART. Usually called

during architecture-specific setup */

gprs_plat_device = platform_device_register_simple(DEV_NAME, 0, NULL, 0);

if (IS_ERR(gprs_plat_device))

{

retval = PTR_ERR(gprs_plat_device);

printk(KERN_ERR "gprs_init_module: can't register platform device %d\n", retval);

goto fail_reg_plat_dev;

}

/* Announce a matching driver for the platform

devices registered above */

retval = platform_driver_register(&gprs_plat_driver);

if (0 != retval)

{

printk(KERN_ERR "gprs_init_module: can't register platform driver %d\n", retval);

goto fail_reg_plat_drv;

}

return 0; /* succeed */

fail_reg_plat_drv:

platform_device_unregister(gprs_plat_device);

fail_reg_plat_dev:

uart_unregister_driver(&gprs_uart_driver);

return retval;

}

static void __exit gprs_exit_module(void)

{

/* The order of unregistration is important. Unregistering the

UART driver before the platform driver will crash the system */

/* Unregister the platform driver */

platform_driver_unregister(&gprs_plat_driver);

/* Unregister the platform devices */

platform_device_unregister(gprs_plat_device);

/* Unregister the GPRS_UART driver */

uart_unregister_driver(&gprs_uart_driver);

}

module_init(gprs_init_module);

module_exit(gprs_exit_module);

MODULE_AUTHOR("lingd");

MODULE_LICENSE("Dual BSD/GPL");

linux 串口驱动 理解,linux 串口驱动 理解相关推荐

  1. 关于linux UART驱动和tty架构的理解

    关于linux UART驱动和tty架构的理解 最近要开发一个驱动程序,需要用到串口和SPI接口.平台的串口驱动程序本身在开发板中已经被实现了,也可以就这样直接使用,但是这样分开使用的结果就是在串口和 ...

  2. linux设备usb节点和硬件接口,所谓设备驱动即驱使硬件设备行动,带你深入理解linux的设备驱动......

    原标题:所谓设备驱动即驱使硬件设备行动,带你深入理解linux的设备驱动... 设备驱动最通俗的解释就是"驱使硬件设备行动".操作系统是通过各种驱动程序来驾驭硬件设备的,它为用户屏 ...

  3. 从串口驱动到Linux驱动模型,想转Linux的必会!

    关注.星标公众号,直达精彩内容 ID:技术让梦想更伟大 整理:李肖遥 本文通过对Linux下串口驱动的分析.由最上层的C库.到操作系统系统调用层的封装.再到tty子系统的核心.再到一系列线路规程.再到 ...

  4. 嵌入式驱动解析:从串口驱动到Linux驱动模型

    本文通过对Linux下串口驱动的分析.由最上层的C库.到操作系统系统调用层的封装.再到tty子系统的核心.再到一系列线路规程.再到最底层的硬件操作. 对Linux中的tty子系统进行简要的说明.从理论 ...

  5. 从串口驱动到Linux驱动模型

    大学的时候,帮朋友写的操作系统调研的作业,最近整理过去的文档时候偶然发现,遂作为博客发出来. 从串口驱动到Linux的tty子系统驱动模型简要分析 基于ARM920T核心 Samsung的S3C244 ...

  6. 从需求的角度去理解Linux系列:总线、设备和驱动

    <从需求的角度去理解Linux系列:总线.设备和驱动>是一篇有关如何学习嵌入式Linux系统的方法论文章,也是从需求的角度去理解Linux系统软件的开篇.这是作者精心撰写的经验总结,希望嵌 ...

  7. linux驱动向不同串口发数据,Linux串口(serial、uart)驱动程序设计

    一.核心数据结构 串口驱动有3个核心数据结构,它们都定义在 1.uart_driver uart_driver包含了串口设备名.串口驱动名.主次设备号.串口控制台(可选)等信息,还封装了tty_dri ...

  8. linux设备驱动,tty串口编程 如何查看linux下串口是否可用?串口名称等

    如何查看linux下串口是否可用?串口名称等? http://zhidao.baidu.com/question/419148559.html 查看串口是否可用,可以对串口发送数据比如对com1口,e ...

  9. linux 深入理解I2C内核驱动

    系列文章 I.MX6ULL 手册查找使用方法 实战点亮LED(寄存器版) I.MX6ULL 手册查找使用方法 实战点亮LED(固件库版本) linux 字符设备驱动实战 linux LED设备驱动文件 ...

最新文章

  1. 封闭、缺少代码审查,硕士刚毕业的“老”工程师揭露机器学习残酷现状!
  2. 关于python语言的浮点数类型_Python 浮点数数据类型详解(float)[学习 Python 必备基础知识][看此一篇就够了]...
  3. Sql Server 字符串操作总结
  4. C语言将字符串转换成对应的数字(十进制、十六进制)【转】
  5. 下列关于python的说法中、正确的是-以下关于 Python版本的说法中,哪个是正确的?...
  6. 微信小程序onLoad与onShow的区别
  7. Vista新特征(Features)(英文)
  8. 不改变原数组的一些方法
  9. python内置类型详细解释
  10. 教你用Python轻松批量获取快手无水印视频以及详细信息!(侵权删)
  11. opencv自带实例下载
  12. CSS的Border属性 属性 边框 可以 定义 宽度 颜色 CSS solid 类型 文本
  13. 关于es6 async函数中reject状态的promise处理
  14. 袁腾飞老师的幽默历史课
  15. Git 学习日记-01
  16. Unity GoogleVR Cardboard 开发
  17. 在安装Windows时手动创建分区
  18. python英文词云代码_Python 词云 【中/英】小白简单入门教程
  19. SVN提交错误:系统找不到指定路径
  20. java 断言 assert 初步使用:断言开启、断言使用

热门文章

  1. java报价系统_基于SSM框架下的JAVA产品报价系统
  2. mysql ERROR 1018
  3. linux教程deepin,国产系统Linux Deepin 2014详细评测
  4. VC版双人PK版俄罗斯方块
  5. 12小时上线“新冠肺炎同程查询工具”,开发者这样狙击疫情
  6. ubuntu1404安装hidost
  7. linux bt 命令行,在Linux的命令行中使用BitTorrent进行下载的教程
  8. 关键链 (项目管理方法)
  9. 2021年化工自动化控制仪表考试题库及化工自动化控制仪表模拟试题
  10. 期望风险, 经验风险和结构风险