Chinaunix首页 | 论坛 | 博客
  • 博客访问: 154000
  • 博文数量: 72
  • 博客积分: 3680
  • 博客等级: 中校
  • 技术积分: 1051
  • 用 户 组: 普通用户
  • 注册时间: 2010-08-22 13:51
文章分类
文章存档

2010年(72)

我的朋友

分类: LINUX

2010-09-24 09:02:20

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_charxon/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_drivertty_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 ,判断出错误种类再进行相应的中断计数,然后再根据不同的errflg设上不同的值,

#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) ) (CLKPCLKUCLK,baudrate的单位是bps

 

(1):首先看一下cflagcs位,同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)

针对以上给定的信息,consolecflag进行设置.还要调用一下ops中的change_speedbaud rate进行实际的设置(物理),成功地话return 0


阅读(1364) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~