Linux_终端控制台体系及串口驱动分析

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Linux终端控制台体系及串口驱动分析
数据通信的基本方式可分为并行通信与串行通信两种:
并行通信:利用多条数据线路将数据的各位同时传送。

它的特点是传输速度快,适用于短距离通信。

串行通信:利用一条数据线将数据一位位顺序传送。

特点是通信线路简单,利用简单的线缆就可实现通信,低成本,适用于远距离通信。

异步通信以一个字符为传输单位,通信中的两个字符间的时间间隔是不固定的,然而同一个字符中的两个相邻位之间的时间间隔是固定的。

通信协议:是指双方约定的一些规则。

在使用异步串口传送一个字符信息时,对数据格式有如下规定:规定有空闲位、起始位、资料位、奇偶校验位、停止位。

起始位:先发一个逻辑“0”信号,表示传输字符的开始
数据位:紧接在起始位之后。

数据位的个数可以是4、5、6、7、8,从最低位开始传送,靠时钟定位。

奇偶校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此校验数据传送的正确性。

停止位:它是一个字符数据的结束标志。

空闲位:处于逻辑“1”状态,表示当前线路上没有数据传送。

波特率:是衡量数据传送速率的指针。

表述每秒钟传送的二进制位数。

注:异步通信是按字符传输的,接收设备在收到起始信号之后在一个字符的传输时间内能和发送设备保持同步就能正确接收。

传送方式:单工方式、半双工方式、全双工方式
终端概述:
在Linux中,TTY(终端)是一类字符设备的统称,它包括了3种类型:控制台、串口和伪终端。

控制台:供内核使用的终端为控制台。

控制台在Linux启动时,通过命令console=…指定,如果没有指定控制台,系统把第一个注册的终端(tty)作为控制台。

1、控制台是一个虚拟的终端,它必须映射到真正的终端上。

2、控制台可以简单的理解为printk输出的地方
3、控制台是个只输出的设备,功能很简单,只能在内核中访问。

伪终端:
伪终端设备是一种特殊的终端设备,由主-从两个成对的设备构成,当打开主设备时,对应的从设备随之打开,形成连接状态。

输入到主设备的数据成为从设备的输出,输入到从设备的数据成为主设备的输出,形成双向管道。

伪终端设备通常用于远程登录服务器来建立网络和终端的关联。

当通过telnet远程登录到另一台主机时,telnet进程与远程主机的telnet服务器相连接。

telnet服务器使用某个主设备并通过对应的从设备与telnet进程相互通信。

终端体系:
从上往下:TTY core—>TTY line discipline(可以没有)—>tty driver 在Linux中,TTY体系分为:TTY核心、TTY线路规程、TTY驱动3部分。

TTY核心从用户获取要发送给TTY设备的数据,然后把数据传送给TTY线路规程(协议),它对数据进行处理后,负责把数据传递到TTY驱动程序,TTY驱动程序负责格式化数据,并通过硬件发送出去。

从硬件收到的数据向上通过TTY驱动,进入TTY线路规程,再进入TTY核心,最后被用户获取。

TTY驱动可以直接和TTY核心通讯,但通常TTY线路规程会修改在两者之间的传送的数据。

TTY驱动不能直接和线路规程通信,甚至不知道它的存在,线路规程的工作是格式化从用户或者硬件收到的数据。

这种格式常常实现为一个协议,如PPP或Bluetooth
终端体系—串口(★)
往串口发送数据时的数据流向:
/dev/ttys0(串口的设备文件)—>tty_io.c(TTY core)—>n_tty.c(tty线路规程,必须向core注册)—>处理完返回tty core—>再由tty core交给驱动程序serial.c—>驱动程序控制硬件
数据流
读操作:
TTY驱动从硬件接收到数据后,负责把数据传送到TTY核心,TTY核心将从TTY驱动收到的数据缓存到一个tty_flip_buffer类型的结构中。

该结构包含两个数据数组(一个用于读、一个用于写)。

从TTY设备接收到的数据被存储到第一个数组,当这个数组满,等待数据的用户将被通知。

当用户从这个数组读数据时,任何从TTY驱动新来的数据被存储在第2个数组。

当第二个数组存满后,数据再次提交给用户,并且驱动又开始填充第1个数组,以此交替。

驱动描述:
Linux内核使用uart_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;
};
注册驱动:
Linux为串口驱动注册了如下接口:
int uart_register_driver(struct uart_driver *drv)
端口描述:
uart_port用于描述一个UART端口(一个串口)的地址、FIFO大小、端口类型等信息。

struct uart_port
{
spinlock_t lock;/*端口锁*/
unsigned int iobase;/*IO端口基地址*/
unsigned char __iomem *membase; /*IO内存基地址*/
unsigned int irq; /*中断号*/
unsigned char fifosize; /*传输fifo大小*/
const struct uart_ops*ops;
……………………..
}
操作串口:
uart_ops定义了针对串口的一系列操作,包括发送、接收及线路设置等。

struct uart_ops
{
unsigned int(*tx_empty)(struct uart_port*);
void(*set_mctrl)(struct uart_port*, unsigned int mctrl);
unsigned int(*get_mctrl)(struct uart_port);
void (*stop_tx)(struct uart_poart*); //停止发送
void (*start_tx)(struct uart_poart*); //开始发送
void(*send_xchar)(struct uart_poart*, char ch); //发送xchar
void(*stop_rx)(struct uart_port*); //停止接收
………………………………..
}
添加串口:
串口核心层提供如下函数添加1个端口:
int uart_add_one_port(struct uart_driver*drv, struct uart_port* port)
★串口驱动设计流程:
1、定义一个uart_driver的变量,并初始化
2、使用uart_regisetr_driver来注册这个驱动
3、初始化uart_port和ops函数表
4、调用uart_add_one_poart()添加初始化好的uart_port
实例分析—mini2440串口驱动程序分析
1.发送和接收
发送:循环buffer →发送fifo→发送移位寄存器
//循环buffer(驱动实现)→发送fifo(串口芯片)过程由驱动程序完成;发送fifo→发送移位寄存器过程由硬件完成
接收:接收移位寄存器→接收fifo →Flip_buf
发送的过程是:把数据写到发送fifo中,fifo把收到的数据传给发送移位寄存器(自动的,非driver控制),然后每个时钟脉冲往串口线上写一bit数据。

接收的过程是:接收移位寄存器收到数据,发送给接收fifo,接收fifo 事先设置好了触发门限,当里面的数据量超过门限时就会触发一个中断,调用驱动中的中断处理函数,把数据写到flip_buf中。

2.寄存器
UART Line Control Register:
Word Length :数据位长度
Number of Stop Bit :停止位数
Parity Mode :奇偶校验位类型
Infra-Red Mode :UART/红外模式选择(当以UART模式工作时,需设为“0”)
UART Control Register
Receive Mode:选择接收模式。

如果是采用DMA模式的话,还需要指定说使用的DMA信道。

Transmit Mode :同上。

Send Break Signal :选择是否在传1帧资料中途发送Break信号。

Loopback Mode :选择是否将UART置于Loopback测试模式。

Rx Error Status Interrupt Enable :选择是否使能当发生接收异常时,是否产生接收错误中断。

Rx Time Out Enable :是否使能接收超时中断。

Rx Interrupt Type :选择接收中断类型。

选择0:Pulse(脉冲式/边沿式中断。

非FIFO模式时,一旦接收缓冲区中有数据,即产生一个中断;为FIFO模式时,一旦当FIFO中的资料达到一定的触发水平后,即产生一个中断)
选择1:Level(电平模式中断。

非FIFO模式时,只要接收缓冲区中有数据,即产生中断;为FIFO模式时,只有FIFO中的资料达到触发水平后,即产生中断)
Tx Interrupt Type :类同于Rx Interrupt Type
UART FIFO Conrtol Register
FIFO Enable :FIFO使能选择。

Rx FIFO Reset :选择当复位接收FIFO时是否自动清除FIFO中的内容。

Tx FIFO Reset :选择当复位发送FIFO时是否自动清除FIFO中的内容。

Rx FIFO Trigger Level :选择接收FIFO的触发水平。

Tx FIFO Trigger Level :选择发送FIFO的触发水平。

UART TX/RX Status Register
Receive buffer data ready :当接收缓冲寄存器从UART接收端口接收到有效资料时将自动置“1”。

反之为“0则表示缓冲器中没有资料。

Transmit buffer empty :当发送缓冲寄存器中为空,自动置“1”;反之表明缓冲器中正有资料等待发送。

Transmitter empty :当发送缓冲器中已经没有有效资料时,自动置“1”;反之表明尚有资料未发送。

UART FIFO Status Register
Rx FIFO Count :接收FIFO中当前存放的字节数。

Tx FIFO Count :发送FIFO中当前存放的字节数。

Rx FIFO Full :为“1“表明接收FIFO已满。

Tx FIFO Full :为“1“表明发送FIFO已满。

3.函数介绍
模块初始化函数:
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_int from_tty) {
disable_irq(TX_IRQ(port));
}
停止发送的功能,其内部的函数disable_irq是停止中断的功能,发送数据是通过中断来完成的,关闭中断也就关闭了发送。

3.2 发送使能函数uart_start_tx
static void s3c2410uart_start_tx(struct uart_port *port, u_int nonempty,
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和data carrier detect(详见Serial Programming Guide for POSIX Operating Systems)
3.6 接收中断函数uart_rx_interrupt
static void s3c2410uart_rx_interrupt(int irq, void *dev_id, struct pt_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--)
{ // status & UTRSTAT_RX_RDY判断有数据
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
{ //flipbuf已满
tty->flip.tqueue.routine((void *) tty);
//交换到另一个缓冲数组
if (tty->flip.count >= TTY_FLIPBUF_SIZE) { //再进行判断
printk(KERN_WARNING "TTY_DONT_FLIP set\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) { //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); //count=fifpsize(16字节)>>1即8字节
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(int irq, 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的值是从是从UART Error Status Register读到的,该erstate只用了四位,所以用UART_ERR_MASK把高四位掩掉,然后测试低四位中哪个位被置1了,从而判断错误种类
UERSTAT_BRK/FRAME/PARITY/OVERRUN 分别代表
1000/0100/0010/0001 ,判断出错误种类再进行相应的中断计数,然后再根据不同的err给flg设上不同的值,有
#define TTY_NORMAL 0
#define TTY_BREAK 1
#define TTY_FRAME 2
#define TTY_PARITY 3
#define TTY_OVERRUN 4
3.9 初始化函数uart_startup
static int s3c2410uart_startup(struct uart_port *port, struct uart_info *info)
{
int ret, flags;
u_int ucon;
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;
#ifdef CONFIG_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);
#if defined(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);
return 0;
#ifdef CONFIG_USE_ERR_IRQ
err_failed:
free_irq(TX_IRQ(port), info);
#endif
tx_failed:
free_irq(RX_IRQ(port), info);
rx_failed:
return ret;
}
如果使用了函数open(ttyS0),那么最后调用的实现open功能的就是这个函数,它打开ttyS0。

1:利用request_irq()申请发送,接收,错误三个中断,如果失败,就要释放调已经申请的全部资源
2:设置UART Control Register
3.10 函数uart_change_speed
static void s3c2410uart_change_speed(struct uart_port *port, u_int cflag, u_int iflag, u_int quot)
{
u_int ulcon, ufcon;
int flags;
ufcon = UART_UFCON(port);
switch (cflag & CSIZE) {
case CS5: ulcon = ULCON_WL5; break;
case CS6: ulcon = ULCON_WL6; break;
case CS7: 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
static void s3c2410_console_write(struct console *co, const char *s, u_int count)
{
int i;
struct uart_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
static int s3c2410_console_wait_key(struct console *co)
{
int c;
struct uart_port *port = s3c2410_ports + co->index;
while (!(UART_UTRSTAT(port) & UTRSTAT_RX_RDY));
c = UART_URXH(port);
return c;
}
该函数在while循环中等待接收数据,一旦接收缓冲器中有有效数据,该函数立即返回,返回值为接收到的一个字符
4.4 函数console_device
static kdev_t s3c2410_console_device(struct console *co)
{
return MKDEV(SERIAL_S3C2410_MAJOR, MINOR_START +
co->index);
}
通过主,次设备号返回kdev_t结构
4.5 设置函数console_setup
static int __init s3c2410_console_setup(struct console *co, char
*options)
{
struct uart_port *port;
int baud = 115200;
int bits = 8;
int parity = 'n';
int flow = 'n';
port = uart_get_console(s3c2410_ports, UART_NR, co);
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
return uart_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_port struct的指针
(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。

相关文档
最新文档