串口驱动程序
2015-09-07 20:35
866 查看
一. TTY
在Linux系统中,终端是一种字符设备,它有多种类型,通常使用tty来简称各种类型的终端设备。tty是Teletype的缩写,Teletype是最早出现的一种终端设备,很像电传打字机,是由Teletype公司产生的。Linux系统包含以下几类终端设备:
1、串行终端设备(/dev/ttySn):它是使用计算机串行端口连接的终端设备,也就是我们主板上的串口。
2、伪终端(/dev/pty/):它是成对的逻辑终端设备,形成双向管道,并存在成对的设备文件。如/dev/pty3和/dev/ttyp3,它们和实际的物理设备并不直接相关。
3、控制台终端(/dev/ttyn, /dev/console):在UNIX系统中,计算机显示器通常被称为控制台终端(Console)。它仿真了类型为Linux的一种终端(TERM=Linux),并且有一些设备特殊文件与之相关联:tty0、tty1、tty2等。如果当前进程有控制终端,那么/dev/tty就是当前进程的控制终端的设备特殊文件。可以简单理解为printk输出的地方。
通过查看/proc/tty/drivers文件可以获知什么类型的tty设备文件存在以及什么驱动被加载到内核。这个文件包括一个当前存在的不同tty驱动的列表,包括驱动名、默认的节点名、驱动的主编号、这个驱动使用的次编号范围以及tty驱动的类型。看下面一个例子:
$ cat /proc/tty/drivers
/dev/tty /dev/tty 5 0 system:/dev/tty
/dev/console /dev/console 5 1 system:console
/dev/ptmx /dev/ptmx 5 2 system
/dev/vc/0 /dev/vc/0 4 0 system:vtmaster
rfcomm /dev/rfcomm 216 0-255 serial
serial /dev/ttyS 4 64-111 serial
pty_slave /dev/pts 136 0-1048575 pty:slave
pty_master /dev/ptm 128 0-1048575 pty:master
unknown /dev/tty 4 1-63 console
首先来看一下tty设备的数据流通图:
![](http://blog.chinaunix.net/photo/94212_100730152614.png)
tty设备有三层:tty核心,tty线路规程,tty驱动。
我们写驱动还是只负责最底层的tty驱动。线路规程的设置也是在底层的tty驱动。
tty核心是封装好的。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202011/26/9651a42676c6613a9af48027a02ca70d)
二.数据发送与接收
发送:循环buffer ->发送fifo->发送移位寄存器
接收:接收移位寄存器->接收fifo ->Flip_buf
发送的过程是:把数据写到发送fifo中,fifo把收到的数据传给发送移位寄存器(自动的,非driver控制),然后每个时钟脉冲往串口线上写一bit数据。
接收的过程是:接收移位寄存器收到数据,发送给接收fifo,接收fifo事先设置好了触发门限,当里面的数据量超过门限时就会触发一个中断,调用驱动中的中断处理函数,把数据写到flip_buf中。
三.驱动程序
在使用串口核心层这个通用串口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驱动的主体工作。
1、uart_register_driver
2、uart_unregister_driver
3、uart_add_one_port
4、uart_remove_one_port
5、uart_write_wakeup
6、uart_suspend_port
7、uart_resume_port
8、uart_get_baud_rate
9、uart_get_divisor
10、uart_update_timeout
11、uart_match_port
12、uart_console_write
1)串口驱动结构体 uart_driver包含了串口设备名、串口驱动名、主次设备号、串口控制台(可选)等信息,还封装了tty_driver(底层串口驱动无需关心tty_driver)。
struct uart_driver {
struct module *owner; //模块所有者
const char *driver_name; //驱动名
const char *dev_name; //设备名
int major; //主设备号
int minor; //次设备号
int nr; //支持串口个数
struct console *cons;//控制台设备
struct uart_state *state; //串口状态
struct tty_driver *tty_driver; //tty设备
};
//(2)串口端口结构体 描述串口端口的I/O端口或I/O内存地址、FIFO大小、端口类型、串口时钟等信息。实际上,一个uart_port实例对应一个串口设备
struct uart_port {
spinlock_t lock;
unsigned long iobase; //io端口基地址
unsigned char __iomem *membase; //内存端口基地址
unsigned int (*serial_in)(struct uart_port *, int); //串口读函数
void (*serial_out)(struct uart_port *, int, int); //串口写方法
void (*set_termios)(struct uart_port *,struct ktermios *new,struct ktermios *old); //串口配置方法函数
void (*pm)(struct uart_port *, unsigned int state,unsigned int old);
unsigned int irq; //中断号
unsigned long irqflags; //中断标志
unsigned int uartclk; //串口时钟
unsigned int fifosize; //fifo大小
unsigned char x_char;
unsigned char regshift; //寄存器偏移值
unsigned char iotype; //io访问类型
unsigned char unused1;
unsigned int read_status_mask;
unsigned int ignore_status_mask;
struct uart_state *state; //uart_state结构体
struct uart_icount icount; //串口使用计数
struct console *cons; //console控制台
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)
unsigned long sysrq;
#endif
upf_t flags;
unsigned int mctrl;
unsigned int timeout;
unsigned int type; //串口类型
const struct uart_ops *ops; //串口操作函数集
unsigned int custom_divisor;
unsigned int line; //端口号
resource_size_t mapbase; //串口寄存器基地址(物理地址)
struct device *dev; //设备文件
unsigned char hub6;
unsigned char suspended;
unsigned char irq_wake;
unsigned char unused[2];
void *private_data;
};
//(3)操作函数集
涵盖了串口驱动可对串口设备进行的所有操作。
struct uart_ops {
unsigned int (*tx_empty)(struct uart_port *); //发送缓冲区为空
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);
void (*stop_rx)(struct uart_port *); //停止接收
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 *);
void (*set_termios)(struct uart_port *, struct ktermios *new,struct ktermios *old); //设置串口参数
void (*set_ldisc)(struct uart_port *, int new);
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 *); //释放端口
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
void (*poll_put_char)(struct uart_port *, unsigned char);
int (*poll_get_char)(struct uart_port *);
#endif
};
//(4)uart_state
struct uart_state {
struct tty_port port;
int pm_state;
struct circ_buf xmit;
struct tasklet_struct tlet;
struct uart_port *uart_port;//指向对应的串口结构
};
/*串口初始化
完善uart_driver结构s3c2410_reg的uart_state成员及tty_driver成员,并注册tty驱动*/
static int __init s3c2410uart_init(void)
{
return uart_register_driver(&s3c2410_reg);
}
使用uart_register_driver注册串口驱动。
static struct uart_driver s3c2410_reg= {
owner: THIS_MODULE,
normal_major: SERIAL_S3C2410_MAJOR,
normal_name: "ttyS%d",
callout_name: "cua%d",
normal_driver: &normal,
callout_major: CALLOUT_S3C2410_MAJOR,
callout_driver: &callout,
table: s3c2410_table,
termios: s3c2410_termios,
termios_locked: s3c2410_termios_locked,
minor: MINOR_START,
nr: UART_NR,
port: s3c2410_ports,
cons: S3C2410_CONSOLE,
};
static struct uart_port s3c2410_ports[UART_NR] = {
{
iobase: (unsigned long)(UART0_CTL_BASE),
iotype: SERIAL_IO_PORT,
irq: IRQ_RXD0,
uartclk: 130252800,
fifosize: 16,
ops: &s3c2410_pops,
type: PORT_S3C2410,
flags: ASYNC_BOOT_AUTOCONF,
},
。。。。。。 。。。。。。。 。。。。。。。
};
static struct uart_ops s3c2410_pops= {
tx_empty: s3c2410uart_tx_empty,
set_mctrl: s3c2410uart_set_mctrl,
get_mctrl: s3c2410uart_get_mctrl,
stop_tx: s3c2410uart_stop_tx,
start_tx: s3c2410uart_start_tx,
stop_rx: s3c2410uart_stop_rx,
enable_ms: s3c2410uart_enable_ms,
break_ctl: s3c2410uart_break_ctl,
startup: s3c2410uart_startup,
shutdown: s3c2410uart_shutdown,
change_speed: s3c2410uart_change_speed,
type: s3c2410uart_type,
config_port: s3c2410uart_config_port,
release_port: s3c2410uart_release_port,
request_port: s3c2410uart_request_port,
};
3.1 阻止发送函数uart_stop_tx
static void s3c2410uart_stop_tx(struct uart_port *port, u_intfrom_tty)
{
disable_irq(TX_IRQ(port));
}
停止发送的功能,其内部的函数disable_irq是停止中断的功能 ,发送数据是通过中断来完成的,关闭中断也就关闭了发送。
3.2 发送使能函数uart_start_tx
static void s3c2410uart_start_tx(struct uart_port *port, u_intnonempty,
u_int from_tty)
{
enable_irq(TX_IRQ(port));
}
与上面的过程类似,就是一个相反的过程
3.3 阻止接收函数uart_stop_rx
static void s3c2410uart_stop_rx(struct uart_port *port)
{
disable_irq(RX_IRQ(port));
}
3.4 发送缓冲空判断函数uart_tx_empty
static u_int s3c2410uart_tx_empty(struct uart_port *port)
{
return (UART_UTRSTAT(port) &UTRSTAT_TR_EMP ? 0 : TIOCSER_TEMT);
}
如果发送缓冲为空则返回0,否则返回1。
3.5 获取控制信息函数uart_get_mctrl
static u_int s3c2410uart_get_mctrl(struct uart_port *port)
{
return (TIOCM_CTS | TIOCM_DSR | TIOCM_CAR);
}
获得控制信息,TIOCM_CTS ,TIOCM_DSR 和TIOCM_CAR,这几个宏代表串口的控制信息, 分别是clear to send,data set ready和datacarrier detect(详见Serial Programming Guide for POSIX Operating Systems)
3.6 接收中断函数uart_rx_interrupt
static void s3c2410uart_rx_interrupt(int irq, void *dev_id, structpt_regs *regs)
{
struct uart_info *info = dev_id;
struct tty_struct *tty = info->tty;
unsigned int status, ch, max_count = 256;
struct uart_port *port = info->port;
status = UART_UTRSTAT(port);
while ((status & UTRSTAT_RX_RDY)&& max_count--)
{
if (tty->flip.count >=TTY_FLIPBUF_SIZE)
{
tty->flip.tqueue.routine((void *) tty);
if (tty->flip.count >=TTY_FLIPBUF_SIZE) {
printk(KERN_WARNING "TTY_DONT_FLIPset\n");
return;
}
}
ch = UART_URXH(port);
*tty->flip.char_buf_ptr = ch;
*tty->flip.flag_buf_ptr = TTY_NORMAL;
port->icount.rx++;
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
status = UART_UTRSTAT(port);
}
tty_flip_buffer_push(tty);
return;
}
功能:主要是是while大循环,首先看循环判断条件status &UTRSTAT_RX_RDY,前面有status = UART_UTRSTAT(port),查2410的datasheet, status & UTRSTAT_RX_RDY这个位是判断接收buffer内是否还有有效数据?按道理一次中断只是把接收的fifobuffer中的数据放到flipbuffer中去,接收的fifo的中断门限是4-12字节,进行一次接收往往要中断好多次,这样中断开销比较大,所以在while的循环条件中判断一下是否还有接收的有效数据,如果有,就继续在中断程序中继续接收,当然,永远都在接收中断中(如果一直有数据要接收)也不合适,所以while循环还有计数,最多循环256次。
在循环中,首先是要判断一下接收数据用的flip-buffer是不是已经满了, if (tty->flip.count >= TTY_FLIPBUF_SIZE)如果满了,就要跳到另一个buffer上去, tty->flip.tqueue.routine((void *) tty)是用来实现跳到另一个buffer上的功能,然后把收到的数据写到flip-buffer中,相应的状态,统计数据都要改,接着再来while
循环,循环结束后就要调用tty_flip_buffer_push(tty)来让用户把存在缓冲里的数据取走,接收一次都要把缓存清空。
3.7 发送中断函数uart_tx_interrupt
static void s3c2410uart_tx_interrupt(int irq, void *dev_id,
struct pt_regs *reg) {
struct uart_info *info = dev_id;
struct uart_port *port = info->port;
int count;
if (port->x_char) {
UART_UTXH(port) = port->x_char;
port->icount.tx++;
port->x_char = 0;
return;
}
if (info->xmit.head == info->xmit.tail
|| info->tty->stopped ||info->tty->hw_stopped) {
s3c2410uart_stop_tx(info->port,0);
return;
}
count = port->fifosize >> 1;
do {
UART_UTXH(port) =info->xmit.buf[info->xmit.tail];
info->xmit.tail = (info->xmit.tail +1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
if (info->xmit.head ==info->xmit.tail)
break;
} while (--count > 0);
if (CIRC_CNT(info->xmit.head,info->xmit.tail,
UART_XMIT_SIZE) < WAKEUP_CHARS)
uart_event(info, EVT_WRITE_WAKEUP);
if (info->xmit.head ==info->xmit.tail)
s3c2410uart_stop_tx(info->port,0);
}
(1) 首先查看port中的x_char是不是为0,不为0则把x_char发送出去。x_char是xon/xoff的意思,每发一个字节时在开始前先发xon信号,在结束时发xoff。
(2) 如果x_char没有被设置,再看环形缓冲区是否为空,或者info->tty->stopped 和 info->tty->hw_stopped 两个位是不是为1,如果这些条件成立的话,就停止发送。Tty->stop指示tty设备是否停止,tty->hw_stop指示tty设备的硬件是否停止了,以上两个位都可以通过ttydriver来设定,否则的话说明有数据要发送。
(3) 如果以上条件都通过了,就利用一个while循环正式发送数据了,从环形缓冲尾巴上取一个数赋给UART_UTXH(port)(发送FIFO),UART_UTXH(port) = info->xmit.buf[info->xmit.tail],这条语句就是把数据送到发送FIFO中,然后计数++,循环一共进行fifosize/2次,也就是一次只能发送8 byte。
(4)循环传送完一次后,再查看缓冲器里还剩余多少数据,如果少于WAKEUP_CHARS(256)的话,就执行uart_event(info, 0),告诉TTY核心,可以接受更多数据了。这里可以看出,tty_driver和tty_core之间的层次,tty_driver可以知道缓冲空还是满,但是它没有权力让发送数据过来,它只能是通知tty_core,让它来处理。
(5) 最后再察看一下环形寄存器,如果serial core 没有发送来更多的数据,就关闭发送。
3.8 出错中断函数uart_err_interrupt
static void s3c2410uart_err_interrupt(intirq, void *dev_id,
struct pt_regs *reg) {
struct uart_info *info = dev_id;
struct uart_port *port = info->port;
struct tty_struct *tty = info->tty;
unsigned char err = UART_UERSTAT(port) & UART_ERR_MASK;
unsigned int ch, flg;
ch = UART_URXH(port);
if (!(err & (UERSTAT_BRK | UERSTAT_FRAME |
UERSTAT_PARITY | UERSTAT_OVERRUN)))
return;
if (err & UERSTAT_BRK)
port->icount.brk++;
if (err & UERSTAT_FRAME)
port->icount.frame++;
if (err & UERSTAT_PARITY)
port->icount.parity++;
if (err & UERSTAT_OVERRUN)
port->icount.overrun++;
err &= port->read_status_mask;
if (err & UERSTAT_PARITY)
flg = TTY_PARITY;
else if (err & UERSTAT_FRAME)
flg = TTY_FRAME;
else
flg = TTY_NORMAL;
if (err & UERSTAT_OVERRUN) {
*tty->flip.char_buf_ptr = ch;
*tty->flip.flag_buf_ptr = flg;
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
if (tty->flip.count < TTY_FLIPBUF_SIZE) {
ch = 0;
flg = TTY_OVERRUN;
}
}
*tty->flip.flag_buf_ptr++ = flg;
*tty->flip.char_buf_ptr++ = ch;
tty->flip.count++;
}
#endif
首先err = UART_UERSTAT(port) & UART_ERR_MASK确定了err的值,err的值是从是从UARTError Status Register读到的,该erstate只用了四位,所以用UART_ERR_MASK把高四位掩掉,然后测试低四位中哪个位被置1了,从而判断错误种类UERSTAT_BRK/FRAME/PARITY/OVERRUN 分别代表1000/0100/0010/0001,判断出错误种类再进行相应的中断计数,然后再根据不同的err给flg设上不同的值,有
#defineTTY_NORMAL 0
#defineTTY_BREAK 1
#defineTTY_FRAME 2
#defineTTY_PARITY 3
#defineTTY_OVERRUN 4
3.9 初始化函数uart_startup
staticint s3c2410uart_startup(structuart_port *port, struct uart_info *info)
{
intret, flags;
u_intucon;
ret =request_irq(RX_IRQ(port), s3c2410uart_rx_interrupt,SA_INTERRUPT,
"serial_s3c2410_rx", info);
if(ret) goto rx_failed;
ret =request_irq(TX_IRQ(port), s3c2410uart_tx_interrupt,SA_INTERRUPT,
"serial_s3c2410_tx", info);
if(ret) goto tx_failed;
#ifdefCONFIG_USE_ERR_IRQ
ret =request_irq(ERR_IRQ(port), s3c2410uart_err_interrupt,SA_INTERRUPT,
"serial_s3c2410_err", info);
if(ret) goto err_failed;
#endif
ucon =(UCON_TX_INT_LVL | UCON_RX_INT_LVL |
UCON_TX_INT| UCON_RX_INT | UCON_RX_TIMEOUT);
#ifdefined(CONFIG_IRDA) || defined(CONFIG_IRDA_MODULE)
ULCON2|= ULCON_IR | ULCON_PAR_NONE | ULCON_WL8 | ULCON_ONE_STOP;
#endif
save_flags(flags);
cli();
UART_UCON(port)= ucon;
sti();
restore_flags(flags);
return0;
#ifdefCONFIG_USE_ERR_IRQ
err_failed:
free_irq(TX_IRQ(port),info);
#endif
tx_failed:
free_irq(RX_IRQ(port),info);
rx_failed:
returnret;
}
如果使用了函数open(ttyS0),那么最后调用的实现open功能的就是这个函数,它打开ttyS0。
1:利用request_irq()申请发送,接收,错误三个中断,如果失败,就要释放调已经申请的全部资源
2:设置UARTControl Register
3.10 函数uart_change_speed
staticvoid s3c2410uart_change_speed(structuart_port *port, u_int cflag, u_int iflag, u_int quot)
{
u_intulcon, ufcon;
intflags;
ufcon= UART_UFCON(port);
switch(cflag & CSIZE) {
caseCS5: ulcon = ULCON_WL5; break;
caseCS6: ulcon = ULCON_WL6; break;
caseCS7: ulcon = ULCON_WL7; break;
default:ulcon = ULCON_WL8; break;
}
if(cflag & CSTOPB)
ulcon|= ULCON_STOP;
if(cflag & PARENB) {
if(!(cflag & PARODD))
ulcon|= ULCON_PAR_EVEN;
}
if(port->fifosize > 1)
ufcon|= UFCON_FIFO_EN;
port->read_status_mask= UERSTAT_OVERRUN;
if(iflag & INPCK)
port->read_status_mask|= UERSTAT_PARITY | UERSTAT_FRAME;
port->ignore_status_mask= 0;
if(iflag & IGNPAR)
port->ignore_status_mask|= UERSTAT_FRAME | UERSTAT_PARITY;
if(iflag & IGNBRK) {
if(iflag & IGNPAR)
port->ignore_status_mask|= UERSTAT_OVERRUN;
}
quot-= 1;
save_flags(flags);
cli();
UART_UFCON(port)= ufcon;
UART_ULCON(port)= (UART_ULCON(port) & ~(ULCON_PAR | ULCON_WL)) | ulcon;
UART_UBRDIV(port)= quot;
sti();
restore_flags(flags);
}
1:
UBRDIVn=(int)(CLK/(bps*16))-1
quot=(CLK/ (baudrate x 16) ) (CLK为PCLK或UCLK,baudrate的单位是bps
(1):首先看一下cflag的cs位,同CS5/6/7比较,然后设置ulcon,接下来的几个if也是将ulcon根据cflag的设置进行一下设置,设置了停止位,校验位。
(2):如果port中设置了fifosize,就把UFCON(物理地址0x50000008)的第0位设为1。
4.控制台
4.1 注册控制台
void __init s3c2410_console_init(void)
{
register_console(&s3c2410_cons);
}
static struct console s3c2410_cons = {
name: "ttyS",
write: s3c2410_console_write,
device: s3c2410_console_device,
wait_key: s3c2410_console_wait_key,
setup: s3c2410_console_setup,
flags: CON_PRINTBUFFER,
index: -1,
};
4.2 函数console_write
staticvoid s3c2410_console_write(structconsole *co, const char *s, u_int count)
{
int i;
structuart_port *port = s3c2410_ports+ co->index;
for (i= 0; i < count; i++) {
while(!(UART_UTRSTAT(port) & UTRSTAT_TX_EMP));
UART_UTXH(port)= s[i];
if(s[i] == '\n') {
while(!(UART_UTRSTAT(port) & UTRSTAT_TX_EMP));
UART_UTXH(port)= '\r';
}
}
}
通过串口往外发送数据
for循环count次,每次发送一个字符,当发送缓冲寄存器为空时,就往里写一个字符,如果写的数据是回车加换行,就要再写一个换行符
4.3 函数console_waitkey
staticint s3c2410_console_wait_key(structconsole *co)
{
int c;
structuart_port *port = s3c2410_ports+ co->index;
while(!(UART_UTRSTAT(port) & UTRSTAT_RX_RDY));
c =UART_URXH(port);
returnc;
}
该函数在while循环中等待接收数据,一旦接收缓冲器中有有效数据,该函数立即返回,返回值为接收到的一个字符
4.4 函数console_device
statickdev_t s3c2410_console_device(structconsole *co)
{
returnMKDEV(SERIAL_S3C2410_MAJOR,MINOR_START + co->index);
}
通过主,次设备号返回kdev_t结构
4.5 设置函数console_setup
staticint __init s3c2410_console_setup(structconsole *co, char *options)
{
structuart_port *port;
intbaud = 115200;
intbits = 8;
intparity = 'n';
intflow = 'n';
port =uart_get_console(s3c2410_ports,UART_NR, co);
if(options)
uart_parse_options(options,&baud, &parity, &bits, &flow);
returnuart_set_options(port, co, baud, parity, bits, flow);
}
这个函数就是设置控制台(console)的状态,里面主要有三个函数
(1)uart_get_console (struct uart_port*ports, int nr, struct console *co)
该函数检查是否co->index是一个无效的index number,返回指向该index number 对应的uart_portstruct的指针
(2)uart_parse_options (options, &baud,&parity, &bits, &flow)
如果option被设置了,那么就要调用该函数,该函数解析options,options来自上一层函数的参数,它是一个字符串,应该包含baudrate,parity,bits,flow这些信息。
(3)uart_set_options(port, co, baud, parity, bits, flow)
针对以上给定的信息,对console的cflag进行设置.还要调用一下ops中的change_speed对baud rate进行实际的设置(物理),成功地话return 0
在Linux系统中,终端是一种字符设备,它有多种类型,通常使用tty来简称各种类型的终端设备。tty是Teletype的缩写,Teletype是最早出现的一种终端设备,很像电传打字机,是由Teletype公司产生的。Linux系统包含以下几类终端设备:
1、串行终端设备(/dev/ttySn):它是使用计算机串行端口连接的终端设备,也就是我们主板上的串口。
2、伪终端(/dev/pty/):它是成对的逻辑终端设备,形成双向管道,并存在成对的设备文件。如/dev/pty3和/dev/ttyp3,它们和实际的物理设备并不直接相关。
3、控制台终端(/dev/ttyn, /dev/console):在UNIX系统中,计算机显示器通常被称为控制台终端(Console)。它仿真了类型为Linux的一种终端(TERM=Linux),并且有一些设备特殊文件与之相关联:tty0、tty1、tty2等。如果当前进程有控制终端,那么/dev/tty就是当前进程的控制终端的设备特殊文件。可以简单理解为printk输出的地方。
通过查看/proc/tty/drivers文件可以获知什么类型的tty设备文件存在以及什么驱动被加载到内核。这个文件包括一个当前存在的不同tty驱动的列表,包括驱动名、默认的节点名、驱动的主编号、这个驱动使用的次编号范围以及tty驱动的类型。看下面一个例子:
$ cat /proc/tty/drivers
/dev/tty /dev/tty 5 0 system:/dev/tty
/dev/console /dev/console 5 1 system:console
/dev/ptmx /dev/ptmx 5 2 system
/dev/vc/0 /dev/vc/0 4 0 system:vtmaster
rfcomm /dev/rfcomm 216 0-255 serial
serial /dev/ttyS 4 64-111 serial
pty_slave /dev/pts 136 0-1048575 pty:slave
pty_master /dev/ptm 128 0-1048575 pty:master
unknown /dev/tty 4 1-63 console
首先来看一下tty设备的数据流通图:
![](http://blog.chinaunix.net/photo/94212_100730152614.png)
tty设备有三层:tty核心,tty线路规程,tty驱动。
我们写驱动还是只负责最底层的tty驱动。线路规程的设置也是在底层的tty驱动。
tty核心是封装好的。
二.数据发送与接收
发送:循环buffer ->发送fifo->发送移位寄存器
接收:接收移位寄存器->接收fifo ->Flip_buf
发送的过程是:把数据写到发送fifo中,fifo把收到的数据传给发送移位寄存器(自动的,非driver控制),然后每个时钟脉冲往串口线上写一bit数据。
接收的过程是:接收移位寄存器收到数据,发送给接收fifo,接收fifo事先设置好了触发门限,当里面的数据量超过门限时就会触发一个中断,调用驱动中的中断处理函数,把数据写到flip_buf中。
三.驱动程序
在使用串口核心层这个通用串口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驱动的主体工作。
1、uart_register_driver
/* 功能: uart_register_driver用于将串口驱动uart_driver注册到内核(串口核心层)中,通常在模块初始化函数调用该函数。 * 参数 drv:要注册的uart_driver * 返回值: 成功,返回0;否则返回错误码 */ int uart_register_driver(struct uart_driver *drv) |
/* 功能: uart_unregister_driver用于注销我们已注册的uart_driver,通常在模块卸载函数调用该函数 * 参数 drv:要注销的uart_driver * 返回值: 成功,返回0;否则返回错误码 */ void uart_unregister_driver(struct uart_driver *drv) |
/* 功能: uart_add_one_port用于为串口驱动添加一个串口端口,通常在探测到设备后(驱动的设备probe方法)调用该函数 * 参数 drv:串口驱动 * port:要添加的串口端口 * 返回值: 成功,返回0;否则返回错误码 */ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) |
/* 功能: uart_remove_one_port用于删除一个已添加到串口驱动中的串口端口,通常在驱动卸载时调用该函数 * 参数 drv: 串口驱动 * port: 要删除的串口端口 * 返回值: 成功,返回0;否则返回错误码 */ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) |
/* 功能: uart_write_wakeup唤醒上层因向串口端口写数据而阻塞的进程,通常在串口发送中断处理函数中调用该函数 * 参数 port:需要唤醒写阻塞进程的串口端口 */ void uart_write_wakeup(struct uart_port *port) |
/* 功能: uart_suspend_port用于挂起特定的串口端口 * 参数 drv: 要挂起的串口端口所属的串口驱动 * port:要挂起的串口端口 * 返回值: 成功返回0;否则返回错误码 */ int uart_suspend_port(struct uart_driver *drv, struct uart_port *port) |
/* 功能: uart_resume_port用于恢复某一已挂起的串口 * 参数 drv: 要恢复的串口端口所属的串口驱动 * port:要恢复的串口端口 * 返回值: 成功返回0;否则返回错误码 */ int uart_resume_port(struct uart_driver *drv, struct uart_port *port) |
/* 功能: 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) |
/* 功能: uart_get_divisor用于计算某一波特率的串口时钟分频数(串口波特率除数) * 参数 port:要计算时钟分频数的串口端口 * baud:期望的波特率 *返回值: 串口时钟分频数 */ unsigned int uart_get_divisor(struct uart_port *port, unsigned int baud) |
/* 功能: uart_update_timeout用于更新(设置)串口FIFO超时时间 * 参数 port: 要更新超时时间的串口端口 * cflag:termios结构体的cflag值 * baud: 串口的波特率 */ void uart_update_timeout(struct uart_port *port, unsigned int cflag, unsigned int baud) |
/* 功能:uart_match_port用于判断两串口端口是否为同一端口 * 参数 port1、port2:要判断的串口端口 * 返回值:不同返回0;否则返回非0 */ int uart_match_port(struct uart_port *port1, struct uart_port *port2) |
/* 功能: 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)) |
1)串口驱动结构体 uart_driver包含了串口设备名、串口驱动名、主次设备号、串口控制台(可选)等信息,还封装了tty_driver(底层串口驱动无需关心tty_driver)。
struct uart_driver {
struct module *owner; //模块所有者
const char *driver_name; //驱动名
const char *dev_name; //设备名
int major; //主设备号
int minor; //次设备号
int nr; //支持串口个数
struct console *cons;//控制台设备
struct uart_state *state; //串口状态
struct tty_driver *tty_driver; //tty设备
};
//(2)串口端口结构体 描述串口端口的I/O端口或I/O内存地址、FIFO大小、端口类型、串口时钟等信息。实际上,一个uart_port实例对应一个串口设备
struct uart_port {
spinlock_t lock;
unsigned long iobase; //io端口基地址
unsigned char __iomem *membase; //内存端口基地址
unsigned int (*serial_in)(struct uart_port *, int); //串口读函数
void (*serial_out)(struct uart_port *, int, int); //串口写方法
void (*set_termios)(struct uart_port *,struct ktermios *new,struct ktermios *old); //串口配置方法函数
void (*pm)(struct uart_port *, unsigned int state,unsigned int old);
unsigned int irq; //中断号
unsigned long irqflags; //中断标志
unsigned int uartclk; //串口时钟
unsigned int fifosize; //fifo大小
unsigned char x_char;
unsigned char regshift; //寄存器偏移值
unsigned char iotype; //io访问类型
unsigned char unused1;
unsigned int read_status_mask;
unsigned int ignore_status_mask;
struct uart_state *state; //uart_state结构体
struct uart_icount icount; //串口使用计数
struct console *cons; //console控制台
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)
unsigned long sysrq;
#endif
upf_t flags;
unsigned int mctrl;
unsigned int timeout;
unsigned int type; //串口类型
const struct uart_ops *ops; //串口操作函数集
unsigned int custom_divisor;
unsigned int line; //端口号
resource_size_t mapbase; //串口寄存器基地址(物理地址)
struct device *dev; //设备文件
unsigned char hub6;
unsigned char suspended;
unsigned char irq_wake;
unsigned char unused[2];
void *private_data;
};
//(3)操作函数集
涵盖了串口驱动可对串口设备进行的所有操作。
struct uart_ops {
unsigned int (*tx_empty)(struct uart_port *); //发送缓冲区为空
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);
void (*stop_rx)(struct uart_port *); //停止接收
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 *);
void (*set_termios)(struct uart_port *, struct ktermios *new,struct ktermios *old); //设置串口参数
void (*set_ldisc)(struct uart_port *, int new);
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 *); //释放端口
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
void (*poll_put_char)(struct uart_port *, unsigned char);
int (*poll_get_char)(struct uart_port *);
#endif
};
//(4)uart_state
struct uart_state {
struct tty_port port;
int pm_state;
struct circ_buf xmit;
struct tasklet_struct tlet;
struct uart_port *uart_port;//指向对应的串口结构
};
/*串口初始化
完善uart_driver结构s3c2410_reg的uart_state成员及tty_driver成员,并注册tty驱动*/
static int __init s3c2410uart_init(void)
{
return uart_register_driver(&s3c2410_reg);
}
使用uart_register_driver注册串口驱动。
static struct uart_driver s3c2410_reg= {
owner: THIS_MODULE,
normal_major: SERIAL_S3C2410_MAJOR,
normal_name: "ttyS%d",
callout_name: "cua%d",
normal_driver: &normal,
callout_major: CALLOUT_S3C2410_MAJOR,
callout_driver: &callout,
table: s3c2410_table,
termios: s3c2410_termios,
termios_locked: s3c2410_termios_locked,
minor: MINOR_START,
nr: UART_NR,
port: s3c2410_ports,
cons: S3C2410_CONSOLE,
};
static struct uart_port s3c2410_ports[UART_NR] = {
{
iobase: (unsigned long)(UART0_CTL_BASE),
iotype: SERIAL_IO_PORT,
irq: IRQ_RXD0,
uartclk: 130252800,
fifosize: 16,
ops: &s3c2410_pops,
type: PORT_S3C2410,
flags: ASYNC_BOOT_AUTOCONF,
},
。。。。。。 。。。。。。。 。。。。。。。
};
static struct uart_ops s3c2410_pops= {
tx_empty: s3c2410uart_tx_empty,
set_mctrl: s3c2410uart_set_mctrl,
get_mctrl: s3c2410uart_get_mctrl,
stop_tx: s3c2410uart_stop_tx,
start_tx: s3c2410uart_start_tx,
stop_rx: s3c2410uart_stop_rx,
enable_ms: s3c2410uart_enable_ms,
break_ctl: s3c2410uart_break_ctl,
startup: s3c2410uart_startup,
shutdown: s3c2410uart_shutdown,
change_speed: s3c2410uart_change_speed,
type: s3c2410uart_type,
config_port: s3c2410uart_config_port,
release_port: s3c2410uart_release_port,
request_port: s3c2410uart_request_port,
};
3.1 阻止发送函数uart_stop_tx
static void s3c2410uart_stop_tx(struct uart_port *port, u_intfrom_tty)
{
disable_irq(TX_IRQ(port));
}
停止发送的功能,其内部的函数disable_irq是停止中断的功能 ,发送数据是通过中断来完成的,关闭中断也就关闭了发送。
3.2 发送使能函数uart_start_tx
static void s3c2410uart_start_tx(struct uart_port *port, u_intnonempty,
u_int from_tty)
{
enable_irq(TX_IRQ(port));
}
与上面的过程类似,就是一个相反的过程
3.3 阻止接收函数uart_stop_rx
static void s3c2410uart_stop_rx(struct uart_port *port)
{
disable_irq(RX_IRQ(port));
}
3.4 发送缓冲空判断函数uart_tx_empty
static u_int s3c2410uart_tx_empty(struct uart_port *port)
{
return (UART_UTRSTAT(port) &UTRSTAT_TR_EMP ? 0 : TIOCSER_TEMT);
}
如果发送缓冲为空则返回0,否则返回1。
3.5 获取控制信息函数uart_get_mctrl
static u_int s3c2410uart_get_mctrl(struct uart_port *port)
{
return (TIOCM_CTS | TIOCM_DSR | TIOCM_CAR);
}
获得控制信息,TIOCM_CTS ,TIOCM_DSR 和TIOCM_CAR,这几个宏代表串口的控制信息, 分别是clear to send,data set ready和datacarrier detect(详见Serial Programming Guide for POSIX Operating Systems)
3.6 接收中断函数uart_rx_interrupt
static void s3c2410uart_rx_interrupt(int irq, void *dev_id, structpt_regs *regs)
{
struct uart_info *info = dev_id;
struct tty_struct *tty = info->tty;
unsigned int status, ch, max_count = 256;
struct uart_port *port = info->port;
status = UART_UTRSTAT(port);
while ((status & UTRSTAT_RX_RDY)&& max_count--)
{
if (tty->flip.count >=TTY_FLIPBUF_SIZE)
{
tty->flip.tqueue.routine((void *) tty);
if (tty->flip.count >=TTY_FLIPBUF_SIZE) {
printk(KERN_WARNING "TTY_DONT_FLIPset\n");
return;
}
}
ch = UART_URXH(port);
*tty->flip.char_buf_ptr = ch;
*tty->flip.flag_buf_ptr = TTY_NORMAL;
port->icount.rx++;
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
status = UART_UTRSTAT(port);
}
tty_flip_buffer_push(tty);
return;
}
功能:主要是是while大循环,首先看循环判断条件status &UTRSTAT_RX_RDY,前面有status = UART_UTRSTAT(port),查2410的datasheet, status & UTRSTAT_RX_RDY这个位是判断接收buffer内是否还有有效数据?按道理一次中断只是把接收的fifobuffer中的数据放到flipbuffer中去,接收的fifo的中断门限是4-12字节,进行一次接收往往要中断好多次,这样中断开销比较大,所以在while的循环条件中判断一下是否还有接收的有效数据,如果有,就继续在中断程序中继续接收,当然,永远都在接收中断中(如果一直有数据要接收)也不合适,所以while循环还有计数,最多循环256次。
在循环中,首先是要判断一下接收数据用的flip-buffer是不是已经满了, if (tty->flip.count >= TTY_FLIPBUF_SIZE)如果满了,就要跳到另一个buffer上去, tty->flip.tqueue.routine((void *) tty)是用来实现跳到另一个buffer上的功能,然后把收到的数据写到flip-buffer中,相应的状态,统计数据都要改,接着再来while
循环,循环结束后就要调用tty_flip_buffer_push(tty)来让用户把存在缓冲里的数据取走,接收一次都要把缓存清空。
3.7 发送中断函数uart_tx_interrupt
static void s3c2410uart_tx_interrupt(int irq, void *dev_id,
struct pt_regs *reg) {
struct uart_info *info = dev_id;
struct uart_port *port = info->port;
int count;
if (port->x_char) {
UART_UTXH(port) = port->x_char;
port->icount.tx++;
port->x_char = 0;
return;
}
if (info->xmit.head == info->xmit.tail
|| info->tty->stopped ||info->tty->hw_stopped) {
s3c2410uart_stop_tx(info->port,0);
return;
}
count = port->fifosize >> 1;
do {
UART_UTXH(port) =info->xmit.buf[info->xmit.tail];
info->xmit.tail = (info->xmit.tail +1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
if (info->xmit.head ==info->xmit.tail)
break;
} while (--count > 0);
if (CIRC_CNT(info->xmit.head,info->xmit.tail,
UART_XMIT_SIZE) < WAKEUP_CHARS)
uart_event(info, EVT_WRITE_WAKEUP);
if (info->xmit.head ==info->xmit.tail)
s3c2410uart_stop_tx(info->port,0);
}
(1) 首先查看port中的x_char是不是为0,不为0则把x_char发送出去。x_char是xon/xoff的意思,每发一个字节时在开始前先发xon信号,在结束时发xoff。
(2) 如果x_char没有被设置,再看环形缓冲区是否为空,或者info->tty->stopped 和 info->tty->hw_stopped 两个位是不是为1,如果这些条件成立的话,就停止发送。Tty->stop指示tty设备是否停止,tty->hw_stop指示tty设备的硬件是否停止了,以上两个位都可以通过ttydriver来设定,否则的话说明有数据要发送。
(3) 如果以上条件都通过了,就利用一个while循环正式发送数据了,从环形缓冲尾巴上取一个数赋给UART_UTXH(port)(发送FIFO),UART_UTXH(port) = info->xmit.buf[info->xmit.tail],这条语句就是把数据送到发送FIFO中,然后计数++,循环一共进行fifosize/2次,也就是一次只能发送8 byte。
(4)循环传送完一次后,再查看缓冲器里还剩余多少数据,如果少于WAKEUP_CHARS(256)的话,就执行uart_event(info, 0),告诉TTY核心,可以接受更多数据了。这里可以看出,tty_driver和tty_core之间的层次,tty_driver可以知道缓冲空还是满,但是它没有权力让发送数据过来,它只能是通知tty_core,让它来处理。
(5) 最后再察看一下环形寄存器,如果serial core 没有发送来更多的数据,就关闭发送。
3.8 出错中断函数uart_err_interrupt
static void s3c2410uart_err_interrupt(intirq, void *dev_id,
struct pt_regs *reg) {
struct uart_info *info = dev_id;
struct uart_port *port = info->port;
struct tty_struct *tty = info->tty;
unsigned char err = UART_UERSTAT(port) & UART_ERR_MASK;
unsigned int ch, flg;
ch = UART_URXH(port);
if (!(err & (UERSTAT_BRK | UERSTAT_FRAME |
UERSTAT_PARITY | UERSTAT_OVERRUN)))
return;
if (err & UERSTAT_BRK)
port->icount.brk++;
if (err & UERSTAT_FRAME)
port->icount.frame++;
if (err & UERSTAT_PARITY)
port->icount.parity++;
if (err & UERSTAT_OVERRUN)
port->icount.overrun++;
err &= port->read_status_mask;
if (err & UERSTAT_PARITY)
flg = TTY_PARITY;
else if (err & UERSTAT_FRAME)
flg = TTY_FRAME;
else
flg = TTY_NORMAL;
if (err & UERSTAT_OVERRUN) {
*tty->flip.char_buf_ptr = ch;
*tty->flip.flag_buf_ptr = flg;
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
if (tty->flip.count < TTY_FLIPBUF_SIZE) {
ch = 0;
flg = TTY_OVERRUN;
}
}
*tty->flip.flag_buf_ptr++ = flg;
*tty->flip.char_buf_ptr++ = ch;
tty->flip.count++;
}
#endif
首先err = UART_UERSTAT(port) & UART_ERR_MASK确定了err的值,err的值是从是从UARTError Status Register读到的,该erstate只用了四位,所以用UART_ERR_MASK把高四位掩掉,然后测试低四位中哪个位被置1了,从而判断错误种类UERSTAT_BRK/FRAME/PARITY/OVERRUN 分别代表1000/0100/0010/0001,判断出错误种类再进行相应的中断计数,然后再根据不同的err给flg设上不同的值,有
#defineTTY_NORMAL 0
#defineTTY_BREAK 1
#defineTTY_FRAME 2
#defineTTY_PARITY 3
#defineTTY_OVERRUN 4
3.9 初始化函数uart_startup
staticint s3c2410uart_startup(structuart_port *port, struct uart_info *info)
{
intret, flags;
u_intucon;
ret =request_irq(RX_IRQ(port), s3c2410uart_rx_interrupt,SA_INTERRUPT,
"serial_s3c2410_rx", info);
if(ret) goto rx_failed;
ret =request_irq(TX_IRQ(port), s3c2410uart_tx_interrupt,SA_INTERRUPT,
"serial_s3c2410_tx", info);
if(ret) goto tx_failed;
#ifdefCONFIG_USE_ERR_IRQ
ret =request_irq(ERR_IRQ(port), s3c2410uart_err_interrupt,SA_INTERRUPT,
"serial_s3c2410_err", info);
if(ret) goto err_failed;
#endif
ucon =(UCON_TX_INT_LVL | UCON_RX_INT_LVL |
UCON_TX_INT| UCON_RX_INT | UCON_RX_TIMEOUT);
#ifdefined(CONFIG_IRDA) || defined(CONFIG_IRDA_MODULE)
ULCON2|= ULCON_IR | ULCON_PAR_NONE | ULCON_WL8 | ULCON_ONE_STOP;
#endif
save_flags(flags);
cli();
UART_UCON(port)= ucon;
sti();
restore_flags(flags);
return0;
#ifdefCONFIG_USE_ERR_IRQ
err_failed:
free_irq(TX_IRQ(port),info);
#endif
tx_failed:
free_irq(RX_IRQ(port),info);
rx_failed:
returnret;
}
如果使用了函数open(ttyS0),那么最后调用的实现open功能的就是这个函数,它打开ttyS0。
1:利用request_irq()申请发送,接收,错误三个中断,如果失败,就要释放调已经申请的全部资源
2:设置UARTControl Register
3.10 函数uart_change_speed
staticvoid s3c2410uart_change_speed(structuart_port *port, u_int cflag, u_int iflag, u_int quot)
{
u_intulcon, ufcon;
intflags;
ufcon= UART_UFCON(port);
switch(cflag & CSIZE) {
caseCS5: ulcon = ULCON_WL5; break;
caseCS6: ulcon = ULCON_WL6; break;
caseCS7: ulcon = ULCON_WL7; break;
default:ulcon = ULCON_WL8; break;
}
if(cflag & CSTOPB)
ulcon|= ULCON_STOP;
if(cflag & PARENB) {
if(!(cflag & PARODD))
ulcon|= ULCON_PAR_EVEN;
}
if(port->fifosize > 1)
ufcon|= UFCON_FIFO_EN;
port->read_status_mask= UERSTAT_OVERRUN;
if(iflag & INPCK)
port->read_status_mask|= UERSTAT_PARITY | UERSTAT_FRAME;
port->ignore_status_mask= 0;
if(iflag & IGNPAR)
port->ignore_status_mask|= UERSTAT_FRAME | UERSTAT_PARITY;
if(iflag & IGNBRK) {
if(iflag & IGNPAR)
port->ignore_status_mask|= UERSTAT_OVERRUN;
}
quot-= 1;
save_flags(flags);
cli();
UART_UFCON(port)= ufcon;
UART_ULCON(port)= (UART_ULCON(port) & ~(ULCON_PAR | ULCON_WL)) | ulcon;
UART_UBRDIV(port)= quot;
sti();
restore_flags(flags);
}
1:
UBRDIVn=(int)(CLK/(bps*16))-1
quot=(CLK/ (baudrate x 16) ) (CLK为PCLK或UCLK,baudrate的单位是bps
(1):首先看一下cflag的cs位,同CS5/6/7比较,然后设置ulcon,接下来的几个if也是将ulcon根据cflag的设置进行一下设置,设置了停止位,校验位。
(2):如果port中设置了fifosize,就把UFCON(物理地址0x50000008)的第0位设为1。
4.控制台
4.1 注册控制台
void __init s3c2410_console_init(void)
{
register_console(&s3c2410_cons);
}
static struct console s3c2410_cons = {
name: "ttyS",
write: s3c2410_console_write,
device: s3c2410_console_device,
wait_key: s3c2410_console_wait_key,
setup: s3c2410_console_setup,
flags: CON_PRINTBUFFER,
index: -1,
};
4.2 函数console_write
staticvoid s3c2410_console_write(structconsole *co, const char *s, u_int count)
{
int i;
structuart_port *port = s3c2410_ports+ co->index;
for (i= 0; i < count; i++) {
while(!(UART_UTRSTAT(port) & UTRSTAT_TX_EMP));
UART_UTXH(port)= s[i];
if(s[i] == '\n') {
while(!(UART_UTRSTAT(port) & UTRSTAT_TX_EMP));
UART_UTXH(port)= '\r';
}
}
}
通过串口往外发送数据
for循环count次,每次发送一个字符,当发送缓冲寄存器为空时,就往里写一个字符,如果写的数据是回车加换行,就要再写一个换行符
4.3 函数console_waitkey
staticint s3c2410_console_wait_key(structconsole *co)
{
int c;
structuart_port *port = s3c2410_ports+ co->index;
while(!(UART_UTRSTAT(port) & UTRSTAT_RX_RDY));
c =UART_URXH(port);
returnc;
}
该函数在while循环中等待接收数据,一旦接收缓冲器中有有效数据,该函数立即返回,返回值为接收到的一个字符
4.4 函数console_device
statickdev_t s3c2410_console_device(structconsole *co)
{
returnMKDEV(SERIAL_S3C2410_MAJOR,MINOR_START + co->index);
}
通过主,次设备号返回kdev_t结构
4.5 设置函数console_setup
staticint __init s3c2410_console_setup(structconsole *co, char *options)
{
structuart_port *port;
intbaud = 115200;
intbits = 8;
intparity = 'n';
intflow = 'n';
port =uart_get_console(s3c2410_ports,UART_NR, co);
if(options)
uart_parse_options(options,&baud, &parity, &bits, &flow);
returnuart_set_options(port, co, baud, parity, bits, flow);
}
这个函数就是设置控制台(console)的状态,里面主要有三个函数
(1)uart_get_console (struct uart_port*ports, int nr, struct console *co)
该函数检查是否co->index是一个无效的index number,返回指向该index number 对应的uart_portstruct的指针
(2)uart_parse_options (options, &baud,&parity, &bits, &flow)
如果option被设置了,那么就要调用该函数,该函数解析options,options来自上一层函数的参数,它是一个字符串,应该包含baudrate,parity,bits,flow这些信息。
(3)uart_set_options(port, co, baud, parity, bits, flow)
针对以上给定的信息,对console的cflag进行设置.还要调用一下ops中的change_speed对baud rate进行实际的设置(物理),成功地话return 0
相关文章推荐
- 比较详细的WinXP故障恢复控制台完全手册第1/2页
- mysql 控制台操作
- PowerShell实现在控制台中插入绿色的打勾符号
- C#控制台模拟电梯工作原理
- C#使用控制台列出当前所有可用的打印机列表
- C#下载网页并在控制台输出的方法
- C++实现基于控制台界面的吃豆子游戏
- c#入门之枚举和结构体使用详解(控制台接收字符串以相反的方向输出)
- 原创的C语言控制台小游戏
- js调试系列 初识控制台
- C#隐藏控制台键盘输入的方法
- Python实现控制台输入密码的方法
- yii框架通过控制台命令创建定时任务示例
- Java从控制台接受输入字符的简单方法
- java控制台输入示例分享
- java控制台输出百分比进度条示例
- python在控制台输出进度条的方法
- 用VC++6.0的控制台实现2048小游戏的程序
- C语言控制台版2048小游戏
- 控制台显示java冒泡排序流程示例