全部博文(72)
2010年(72)
分类: LINUX
2010-09-24 09:02:20
3.7 发送中断函数uart_tx_interrupt
static void s
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) {
s
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)
s
}
(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 s
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 s
{
int ret, flags;
u_int ucon;
ret = request_irq(RX_IRQ(port), s
"serial_s
if (ret) goto rx_failed;
ret = request_irq(TX_IRQ(port), s
"serial_s
if (ret) goto tx_failed;
#ifdef CONFIG_USE_ERR_IRQ
ret = request_irq(ERR_IRQ(port), s
"serial_s
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 s
{
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位,同CS
(2):如果port中设置了fifosize,就把UFCON(物理地址0x50000008)的第0位设为1。
4.控制台
4.1 注册控制台
void __init s
{
register_console(&s
}
static struct console s
name: "ttyS",
write: s
device: s
wait_key: s
setup: s
flags: CON_PRINTBUFFER,
index: -1,
};
4.2 函数console_write
static void s
{
int i;
struct uart_port *port = s
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 s
{
int c;
struct uart_port *port = s
while (!(UART_UTRSTAT(port) & UTRSTAT_RX_RDY));
c = UART_URXH(port);
return c;
}
该函数在while循环中等待接收数据,一旦接收缓冲器中有有效数据,该函数立即返回,返回值为接收到的一个字符
4.4 函数console_device
static kdev_t s
{
return MKDEV(SERIAL_S
}
通过主,次设备号返回kdev_t结构
4.5 设置函数console_setup
static int __init s
{
struct uart_port *port;
int baud = 115200;
int bits = 8;
int parity = 'n';
int flow = 'n';
port = uart_get_console(s
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