Linux Kernel Uart Driver Development 相关情况交代如下:
1、需要阅读文档:linux/Documentation/serial/... ,另外非常需要阅读的文档就是S5PV210 user manual。
2、参考的uart driver文件:s3c2440.c,s5pv210.c
3、底层相关源码:serial_core.h,serial.h,regs-serial.h,samsung.c,samsung.h,
4、Linux kernel version:linux-2.6.32
5、platform:S5PV210
6、本文章分析了uart 的内核驱动和简单的串口应用测试
其他串口设备相关文件:devs.c、tty_io.c
就现有的uart driver相关源码做分析,望能给读者些许帮助,如有错误或不到之处,请留言指正。
底层驱动分析
【一】samsung.c中的几个重要的数据结构:uart_ops、 uart_driver、s3c24xx_uart_port
samsung.c (@/drivers/serial/) 此文件为串口设备驱动的核心结构体所在位置,需要先研究这个文件.
#define S3C24XX_SERIAL_NAME "ttySAC"
#define S3C24XX_SERIAL_MAJOR 204
#define S3C24XX_SERIAL_MINOR 64
【1】
static struct uart_ops s3c24xx_serial_ops = {
.pm = s3c24xx_serial_pm, //电源管理
.tx_empty = s3c24xx_serial_tx_empty, //发送缓冲区
.get_mctrl = s3c24xx_serial_get_mctrl, //获取modem控制设置参数
.set_mctrl = s3c24xx_serial_set_mctrl, //设置modem控制(MCR)
.stop_tx = s3c24xx_serial_stop_tx, //停止接收字符
.start_tx = s3c24xx_serial_start_tx, //开始传输字符
.stop_rx = s3c24xx_serial_stop_rx, //停止接手
.enable_ms = s3c24xx_serial_enable_ms, //modem状态中断使能
.break_ctl = s3c24xx_serial_break_ctl, //控制break信号的传输
.startup = s3c24xx_serial_startup, //启动端口
.shutdown = s3c24xx_serial_shutdown, //关闭端口
.set_termios = s3c24xx_serial_set_termios, //改变端口参数
.type = s3c24xx_serial_type, //返回描特定端口的常量字符串指针
.release_port = s3c24xx_serial_release_port, //释放端口占用的的内存和I/O资源
.request_port = s3c24xx_serial_request_port, //申请端口所需的内存和I/O资源
.config_port = s3c24xx_serial_config_port, //执行端口所需的自动配置步骤
.verify_port = s3c24xx_serial_verify_port, //验证新的端口信息
};
URXHn和UTXHn分别为接收和发送数据寄存器;
UTRSTATn分别为发送和接收的状态;
UMCONn是UART的modem控制,设置是否使用RTS流控制,若采用流控制,可选择自动流控制(AFC)或软件控制RTS信号。
【2】
static struct uart_driver s3c24xx_uart_drv = {
.owner = THIS_MODULE,
.dev_name = "s3c2410_serial", //设备名称
.nr = CONFIG_SERIAL_SAMSUNG_UARTS, //支持最多的串口设备数,210平台共支持4个UART Port
.cons = S3C24XX_SERIAL_CONSOLE,
.driver_name = S3C24XX_SERIAL_NAME, //驱动名称
.major = S3C24XX_SERIAL_MAJOR, //主设备号
.minor = S3C24XX_SERIAL_MINOR, //次设备号
};
【3】
static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
[0] = {
.port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX0,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 0, //端口号
}
},
[1] = {
.port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX1,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 1,
}
},
#if CONFIG_SERIAL_SAMSUNG_UARTS > 2
[2] = {
.port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX2,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 2,
}
},
#endif
#if CONFIG_SERIAL_SAMSUNG_UARTS > 3
[3] = {
.port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock),
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX3,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 3,
}
}
#endif
};
【4】
分析一下s3c24xx_uart_port数据结构
struct s3c24xx_uart_port {
unsigned char rx_claimed;
unsigned char tx_claimed;
unsigned int pm_level;
unsigned long baudclk_rate;
unsigned int rx_irq;
unsigned int tx_irq;
struct s3c24xx_uart_info *info;
struct s3c24xx_uart_clksrc *clksrc;
struct clk *clk;
struct clk *baudclk;
struct uart_port port;
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
};
struct s3c24xx_uart_info {
char *name;
unsigned int type;
unsigned int fifosize;
unsigned long rx_fifomask;
unsigned long rx_fifoshift;
unsigned long rx_fifofull;
unsigned long tx_fifomask;
unsigned long tx_fifoshift;
unsigned long tx_fifofull;
/* uart port features */
unsigned int has_divslot:1;
/* clock source control */
int (*get_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);
int (*set_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);
/* uart controls */
int (*reset_port)(struct uart_port *, struct s3c2410_uartcfg *);
};
在s3c2410_uartcfg中封装了UCONn、ULCONn、UFCONn等寄存器,如下:
struct s3c2410_uartcfg {
unsigned char hwport; /* hardware port number */ //硬件端口号
unsigned char unused;
unsigned short flags;
#if !defined(CONFIG_CPU_S5PV210)
upf_t uart_flags; /* default uart flags */
#else
unsigned long uart_flags; /* default uart flags */
#endif
unsigned long ucon; /* value of ucon for port */
unsigned long ulcon; /* value of ulcon for port */
unsigned long ufcon; /* value of ufcon for port */
struct s3c24xx_uart_clksrc *clocks;
unsigned int clocks_size;
};
【二】串口驱动初始化与释放
UART_DRIVER.C
@Your Direction
【1】
驱动加载过程中会调用uart_register_driver()注册s3c24xx_uart_drv这个uart driver。同时经过
s3c2410_serial_init()
--> s3c24xx_serial_init()
--> platform_driver_register()的调用会导致s3c24xx_serial_probe()(@sumsung.c)被调用执行。
而s3c24xx_serial_probe()函数会调用s3c24xx_serial_init_port()来初始化UART端口,同时通过uart_add_one_port()添加端口。加载过程如:
static int s3c24xx_serial_init(struct platform_driver *drv, struct s3c24xx_uart_info *info)
{
printk("uart platform driver register\n");
retrurn platform_driver_register(drv);//needed identify
}
static inline int s3c2410_serial_init(void)
{
return s3c24xx_serial_init(&s5p_serial_drv,&s5p_uart_inf); //needed modify
}
static int __init PDA_ScanSerialPort_init(void)
{
int ret;
ret = s3c_gpio_scan_isr_setup();
if(!ret)
printk("Scan triger IRQ request successfully\n");
else
printk("Scan triger IRQ request failed");
//register uart dirver
ret = uart_register_driver(&s3c24xx_uart_drv);
if(ret < 0)
{
printk("failed to register Uart for Scan\n");
return -1;
}
//init serial port
s3c2410_serial_init();
//s3c24xx_serial_init(&s5p_serial_drv, *s5p_uart_inf); //needed modify
return 0;
}
【三】串口收发数据
s5pv210平台驱动uart_ops结构体的startup()成员函数s3c24xx_serial_startup()用于启动串口,申请端口的发送/接收中断,使能端口的发送和接收:
【1】
static int s3c24xx_serial_startup(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
int ret;
dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
port->mapbase, port->membase);
rx_enabled(port) = 1; //接收使能
ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0, //申请接收中断
s3c24xx_serial_portname(port), ourport);
if (ret != 0) {
printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);
return ret;
}
ourport->rx_claimed = 1;
dbg("requesting tx irq...\n");
tx_enabled(port) = 1; //使能发送中断
ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0, //申请发送中断
s3c24xx_serial_portname(port), ourport);
if (ret) {
printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);
goto err;
}
ourport->tx_claimed = 1;
dbg("s3c24xx_serial_startup ok\n");
/* the port reset code should have done the correct
* register setup for the port controls */
//端口复位代码应为端口控制配置了正确的寄存器值
return ret;
err:
s3c24xx_serial_shutdown(port);
return ret;
}
这里的s3c24xx_serial_startup 的反操作函数为 s3c24xx_serial_shutdown,其分析为如下:
static void s3c24xx_serial_shutdown(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
if (ourport->tx_claimed) {
free_irq(ourport->tx_irq, ourport);
tx_enabled(port) = 0; //置发送使能为关闭状态
ourport->tx_claimed = 0;
}
if (ourport->rx_claimed) {
free_irq(ourport->rx_irq, ourport);
ourport->rx_claimed = 0;
rx_enabled(port) = 0; //置接收使能为关闭状态
}
}
【2】
uart_ops结构体的tx_empty()成员函数s3c24xx_serial_tx_empty()用于判断发送缓冲区是否为空。
当使能FIFO模式的时候,判断UFSTATn寄存器,否则判断UFSTATn寄存器的相应位。分析如下:
static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port)
{
struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT); //寄存器
unsigned long ufcon = rd_regl(port, S3C2410_UFCON);
if (ufcon & S3C2410_UFCON_FIFOMODE) { //FIFO模式,检查UFSTATn寄存器
if ((ufstat & info->tx_fifomask) != 0 || //Tx FIFO 数据非0
(ufstat & info->tx_fifofull)) //Tx FIFO 溢出
return 0; //0:非空
return 1; //1:空
}
return s3c24xx_serial_txempty_nofifo(port);
}
这里的s3c24xx_serial_txempty_nofifo展开:
//非FIFO模式,返回UTRSTATn寄存器状态
static int s3c24xx_serial_txempty_nofifo(struct uart_port *port)
{
return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE);
}
【3】
uart_ops结构体的start_tx()成员函数s3c24xx_serial_start_tx()用于启动发送,
而stop_tx()成员函数s3c24xx_serial_stop_tx()用于停止发送;
static void s3c24xx_serial_start_tx(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
if (!tx_enabled(port)) { //如果端口发送未能使能
if (port->flags & UPF_CONS_FLOW)
s3c24xx_serial_rx_disable(port);
enable_irq(ourport->tx_irq); //使能发送中断
tx_enabled(port) = 1; //端口发送使能
}
}
static void s3c24xx_serial_stop_tx(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
if (tx_enabled(port)) { //如果端口发送使能
disable_irq_nosync(ourport->tx_irq); //清发送使能状态
tx_enabled(port) = 0; //关闭端口发送使能
if (port->flags & UPF_CONS_FLOW)
s3c24xx_serial_rx_enable(port);
}
}
【4】
uart_ops结构体的stop_rx()成员函数s3c24xx_serial_stop_rx()用于清接收中断使能;
注意:成员函数中无start_rx()成员函数
static void s3c24xx_serial_stop_rx(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
if (rx_enabled(port)) { //如果端口接收为使能
dbg("s3c24xx_serial_stop_rx: port=%p\n", port);
disable_irq_nosync(ourport->rx_irq); //清端口接收使能
rx_enabled(port) = 0; //关闭端口接收使能
}
}
【5】
与数据发送关系最密切的不是上述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缓冲。过程分析如下:
static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id)
{
struct s3c24xx_uart_port *ourport = dev_id;
struct uart_port *port = &ourport->port; //获得uart_port
struct tty_struct *tty = port->state->port.tty; //获得tty
unsigned int ufcon, ch, flag, ufstat, uerstat;
int max_count = 64;
while (max_count-- > 0) { //如果收到0个字符
ufcon = rd_regl(port, S3C2410_UFCON);
ufstat = rd_regl(port, S3C2410_UFSTAT);
if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
break;
uerstat = rd_regl(port, S3C2410_UERSTAT);
ch = rd_regb(port, S3C2410_URXH); //读出字符数据
if (port->flags & UPF_CONS_FLOW) {
int txe = s3c24xx_serial_txempty_nofifo(port);
if (rx_enabled(port)) { //如果端口为接收使能状态
if (!txe) { //如果发送缓冲区为空
rx_enabled(port) = 0; //清接收使能
continue;
}
} else { //否则端口为禁止接收
if (txe) { //如果发送缓冲区非空
ufcon |= S3C2410_UFCON_RESETRX;
wr_regl(port, S3C2410_UFCON, ufcon);
rx_enabled(port) = 1; //使能端口接收
goto out;
}
continue;
}
}
/* insert the character into the buffer */
//将接收到的数据写入到buffer中
flag = TTY_NORMAL;
port->icount.rx++;
if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) {
dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n",
ch, uerstat);
/* check for break */
if (uerstat & S3C2410_UERSTAT_BREAK) {
dbg("break!\n");
port->icount.brk++;
if (uart_handle_break(port))
goto ignore_char;
}
if (uerstat & S3C2410_UERSTAT_FRAME)
port->icount.frame++;
if (uerstat & S3C2410_UERSTAT_OVERRUN)
port->icount.overrun++;
uerstat &= port->read_status_mask;
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;
}
if (uart_handle_sysrq_char(port, ch)) //处理sysrq字符
goto ignore_char;
uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag); //插入字符到tty设备的flip缓冲
ignore_char:
continue;
}
tty_flip_buffer_push(tty); //刷新tty设备的flip缓冲
out:
return IRQ_HANDLED;
}
其中:uart_insert_char()是对串口核心层对tty_insert_flip_char()的封装,它作为内联函数被定义在serial_core.h中。
【6】
s3c24xx_serial_tx_chars()函数需要读取uart_info中的环形缓冲区中的字符,写入UTXHn寄存器,分析如下:
static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
{
struct s3c24xx_uart_port *ourport = id;
struct uart_port *port = &ourport->port;
struct circ_buf *xmit = &port->state->xmit; //得到环形缓冲区
int count = 256; //最多一次发送256个字符
if (port->x_char) { //如果定义了x_char,发送
wr_regb(port, S3C2410_UTXH, port->x_char);
port->icount.tx++;
port->x_char = 0;
goto out;
}
/* if there isnt anything more to transmit, or the uart is now stopped, disable the uart and exit */
//如果没有更多的数据需要发送,或者uart tx 停止,则停止uart 并且退出
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
s3c24xx_serial_stop_tx(port);
goto out;
}
/* try and drain the buffer... */
//尝试把环形buffer中的数据发完
while (!uart_circ_empty(xmit) && count-- > 0) {
if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
break;
wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
}
//如果环形buffer中剩余的字符少于WAKEUP_CHARS,唤醒上层
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
if (uart_circ_empty(xmit)) //如果环形buffer为空
s3c24xx_serial_stop_tx(port); //停止发送
out:
return IRQ_HANDLED;
}
其中:WAKEUP_CHARS的含义为,当发送环形buffer中的字符数少于该数时,驱动将请求上层向下传递更多的数据,
uart_write_wakeup(port)来完成这个任务。
【四】串口相关寄存器设置
【1】
uart_ops结构体的set_termios()成员函数用于改变端口的参数设置,包括波特率,字长,停止位,奇偶校验等参数,并将它接收到的
port、termios参数成员的值设置ARM处理器的uart的相关 regs(如,ULCONn、UCONn、UMCONn……)
static void s3c24xx_serial_set_termios(struct uart_port *port,
struct ktermios *termios,
struct ktermios *old)
{
struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
struct s3c24xx_uart_port *ourport = to_ourport(port);
struct s3c24xx_uart_clksrc *clksrc = NULL;
struct clk *clk = NULL;
unsigned long flags;
unsigned int baud, quot;
unsigned int ulcon;
unsigned int umcon;
unsigned int udivslot = 0;
//davied add for bt
unsigned int ucon;
//--
//不止此modem控制信号线
termios->c_cflag &= ~(HUPCL | CMSPAR);
termios->c_cflag |= CLOCAL;
//计算端口分频,以便产生对应的波特率。
//davied changed 115200*8 to 1843200*2 for bt
baud = uart_get_baud_rate(port, termios, old, 0, 1843200*2);//115200*8);
if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
quot = port->custom_divisor;
else
quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud);
// 检查是否需要改变时钟源
if (ourport->clksrc != clksrc || ourport->baudclk != clk) {
dbg("selecting clock %p\n", clk);
s3c24xx_serial_setsource(port, clksrc);
if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) {
clk_disable(ourport->baudclk);
ourport->baudclk = NULL;
}
clk_enable(clk);
ourport->clksrc = clksrc;
ourport->baudclk = clk;
ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
}
if (ourport->info->has_divslot) {
unsigned int div = ourport->baudclk_rate / baud;
udivslot = udivslot_table[div & 15];
dbg("udivslot = %04x (div %d)\n", udivslot, div & 15);
}
//设置字长
switch (termios->c_cflag & CSIZE) {
case CS5:
dbg("config: 5bits/char\n");
ulcon = S3C2410_LCON_CS5;
break;
case CS6:
dbg("config: 6bits/char\n");
ulcon = S3C2410_LCON_CS6;
break;
case CS7:
dbg("config: 7bits/char\n");
ulcon = S3C2410_LCON_CS7;
break;
case CS8:
default:
dbg("config: 8bits/char\n");
ulcon = S3C2410_LCON_CS8;
break;
}
//保留以前的LCON IR 参数
ulcon |= (cfg->ulcon & S3C2410_LCON_IRM);
if (termios->c_cflag & CSTOPB)
ulcon |= S3C2410_LCON_STOPB;
//设置是否采用RTS、CTS等流控制
umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0;
if (termios->c_cflag & PARENB) {
if (termios->c_cflag & PARODD)
ulcon |= S3C2410_LCON_PODD; //奇校验
else
ulcon |= S3C2410_LCON_PEVEN; //偶校验
} else {
ulcon |= S3C2410_LCON_PNONE; //无校验
}
spin_lock_irqsave(&port->lock, flags);
dbg("setting ulcon to %08x, brddiv to %d, udivslot %08x\n",
ulcon, quot, udivslot);
//davied add for bt
if(port->irq == IRQ_S3CUART_RX0)
{
ucon = rd_regl(port, S3C2410_UCON),
wr_regl(port, S3C2410_UCON, ucon|(1<<10));
}
//--
wr_regl(port, S3C2410_ULCON, ulcon);
wr_regl(port, S3C2410_UBRDIV, quot);
wr_regl(port, S3C2410_UMCON, umcon);
if (ourport->info->has_divslot)
wr_regl(port, S3C2443_DIVSLOT, udivslot);
dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
rd_regl(port, S3C2410_ULCON),
rd_regl(port, S3C2410_UCON),
rd_regl(port, S3C2410_UFCON));
// 更新端口超时
uart_update_timeout(port, termios->c_cflag, baud);
/*
* Which character status flags are we interested in?
*/
port->read_status_mask = S3C2410_UERSTAT_OVERRUN;
if (termios->c_iflag & INPCK)
port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;
/*
* Which character status flags should we ignore?
*/
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未设置,则忽略所有字符.
if ((termios->c_cflag & CREAD) == 0)
port->ignore_status_mask |= RXSTAT_DUMMY_READ;
spin_unlock_irqrestore(&port->lock, flags);
}
【2】
uart_ops结构体的get_mctrl()、set_mctrl()成员函数实现非常简单的modem控制信号线。get_mctrl()返回DSR一直有效,而CTS则根据UMSTATn寄存器
的内容获得,set_mctrl()当下版本为空函数。
static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port)
{
unsigned int umstat = rd_regb(port, S3C2410_UMSTAT);
if (umstat & S3C2410_UMSTAT_CTS) //如果CTS信号有效(low level)
return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
else
return TIOCM_CAR | TIOCM_DSR;
}
static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
/* todo - possibly remove AFC and do manual CTS */
}
最后TTY 驱动的主题是围绕tty_driver这个结构体的成员函数展开的,主要是应去实现其中的数据发送和接收流程以及tty设备端口接口函数。
针对串口,linux kernel实现了通用的tty_driver,所以串口驱动的主要任务就是从tty_driver转移到uart_driver上来。
UART发送接收数据分析
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
细节分析:
【1】读、写函数
rd_regl()
--> #define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))
wr_regl()
-->#define wr_regl(port, reg, val) __raw_writel(val,portaddr(port, reg))
-->#define portaddr(port, reg) ((port)->membase + (reg))
-->port --> struct uart_port
struct uart_port { /* @ linux/drivers/char/serial_core.h */
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);
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 access style */
unsigned char unused1;
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 */
//……
//……
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 */
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 unused[2];
void *private_data; /* generic platform data pointer */
};
看一下实例,你就能明白好多了,以函数s5pv210_serial_resetport()为例:
static int s5pv210_serial_resetport(struct uart_port *port, struct s3c2410_uartcfg *cfg)
{
unsigned long ucon = rd_regl(port, S3C2410_UCON);
ucon &= S5PV210_UCON_CLKMASK;
wr_regl(port, S3C2410_UCON, ucon | cfg->ucon);
wr_regl(port, S3C2410_ULCON, cfg->ulcon);
/* reset both fifos */
wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
wr_regl(port, S3C2410_UFCON, cfg->ufcon);
return 0;
}
其中:S3C2410_UCON、S3C2410_ULCON、S3C2410_UFCON等寄存器地址在头文件
linux/arch/arm/plat-samsung/include/plat/regs-serial.h中定义。
【2】经过编译后的内核下载烧写到ARM主板,运行主板,进入文件系统后敲入命令:
ls dev/ |grep tty*
可以看到,如下打印信息:
……
rfkill
vga_arbiter
snd
ttyS3
ttyS2
ttyS1
ttyS0
s3c2410_serial3
s3c2410_serial2
s3c2410_serial1
s3c2410_serial0
usbdev1.1
usb1
rtc0
graphics
block
i2c-5
i2c-4
……
可以看到有我们需要的串口驱动“s3c2410_serialx”
未完,待续~~
参考文章1
参考文章2