串口驱动(使用中断)完整读操作
当串口数据满,fifo数据达到设定阈值时,发生接收中断。
当串口数据空时,发生发送中断。
如下:
1)发送和接收:
发送: 循环buffer -(驱动做)-> 发送 fifo -(硬件自己做)-> 发送移位寄存器
把数据写到发送fifo中。fifo把收到的数据传给发送移位寄存器(自动的,非driver控制),然后每个时钟脉冲往串口线上写一个bit数据
所以是一直是中断告知还可以发送几个。当不足时一直引发中断。
Tx FIFO trigger level选择fifo触发水平,选择什么时候引发中断,如fifo 低于 到4个或8 16 个字节等时中断
接收: 接收移位寄存器 -(硬件自己做)-> 接收fifo -(驱动做)-> flip_buf
接收移位寄存器收到数据后,发送给接收fifo,接收fifo事先设置好触发门限,当里面的数据量超过门限时,就会触发一个中断,调用驱动里的中断处理函数,把数据写到flip_buf中。
Rx FIFO trigger level选择fifo触发水平,选择什么时候引发中断,如收到4个或8 16 个字节等时中断
第一部分:
读操作:TTY驱动从硬件收到数据后,负责把数据传递到TTY 核心,TTY核心将从TTY驱动收到的数据缓存到一个tty_flip_buffer 类型的结构中。该结构包含两个数据数
组。从TTY设备接收到的数据被存储于第一个数组,当这个数组满, 等待数据的用户将被通知。当用户从这个数组读数据时, 任何从TTY驱动新来的数据将被存储在第2个数组。
当第二个数组存满后,数据再次提交给用户, 并且驱动又开始填充第1个数组,以此交替。
一次中断只是把接收的fifobuffer中的数据放到flipbuffer中去,接收的fifo的中断门限是4-12字节,进行一次接收操作往往要中断好多次,
这样中断开销比较大,所以在while的循环条件中判断一下是否还有接收的有效数据,如果有,就继续在中断程序中继续接收,
当然,永远都在接收中断中(如果一直有数据要接收)也不合适,所以while循环还有计数,最多循环64次。
在循环中,首先是要判断一下接收数据用的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)来让用户把存在缓冲里的数据取走,接收一次都要把缓存清空。
static inline void receive_chars(struct w55fa93_uart_port *up, int *status)
{
struct tty_struct *tty = up->port.state->port.tty;
struct uart_port *port = &up->port;
unsigned int ch, flag;
//int max_count = 256;
int max_count = 64;
if(*status & UART_FSR_RFE)
return;
//printk("%s get a char,FSR 0x%x\r\n",up->info->name,*status);
do {
ch = rd_regb(port, W55FA93_COM_RX);
flag = TTY_NORMAL;
up->port.icount.rx++;
if (unlikely(*status & (UART_FSR_BI | UART_FSR_PE |
UART_FSR_FE | UART_FSR_ROE))) {
/*
* For statistics only
*/
if (*status & UART_FSR_BI) {
*status &= ~(UART_FSR_FE | UART_FSR_PE);
up->port.icount.brk++;
/*
* We do the SysRQ and SAK checking
* here because otherwise the break
* may get masked by ignore_status_mask
* or read_status_mask.
*/
if (uart_handle_break(&up->port))
goto ignore_char;
} else if (*status & UART_FSR_PE)
up->port.icount.parity++;
else if (*status & UART_FSR_FE)
up->port.icount.frame++;
if (*status & UART_FSR_ROE)
up->port.icount.overrun++;
/*
* Mask off conditions which should be ignored.
*/
*status &= up->port.read_status_mask;
#ifdef CONFIG_SERIAL_W55FA93_CONSOLE
if (up->port.line == up->port.cons->index) {
/* Recover the break flag from console xmit */
*status |= up->lsr_break_flag;
up->lsr_break_flag = 0;
}
#endif
if (*status & UART_FSR_BI) {
flag = TTY_BREAK;
} else if (*status & UART_FSR_PE)
flag = TTY_PARITY;
else if (*status & UART_FSR_FE)
flag = TTY_FRAME;
}
if (uart_handle_sysrq_char(&up->port, ch))
goto ignore_char;
uart_insert_char(&up->port, *status, UART_FSR_ROE, ch, flag);
//if(up->port.irq == IRQ_UART)
// printk("%s get a char,FSR 0x%x : 0x%x\r\n",up->info->name,*status,ch);
ignore_char:
*status = rd_regl(port, W55FA93_COM_FSR);
} while ((!(*status & UART_FSR_RFE)) && (max_count-- > 0));
tty_flip_buffer_push(tty);
}
解析uart_insert_char:将数据放入tty缓存
uart_insert_char(&up->port, *status, UART_FSR_ROE, ch, flag);-->tty_insert_flip_char(tty, ch, flag)-->tty_insert_flip_string_flags(tty, &ch, &flag, 1)
int tty_insert_flip_string_flags(struct tty_struct *tty,
const unsigned char *chars, const char *flags, size_t size)
{
int copied = 0;
do {
int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE);
int space = tty_buffer_request_room(tty, goal);
struct tty_buffer *tb = tty->buf.tail;
/* If there is no space then tb may be NULL */
if (unlikely(space == 0))
break;
memcpy(tb->char_buf_ptr + tb->used, chars, space);
memcpy(tb->flag_buf_ptr + tb->used, flags, space);
tb->used += space;
copied += space;
chars += space;
flags += space;
/* There is a small chance that we need to split the data over
several buffers. If this is the case we must loop */
} while (unlikely(size > copied));
return copied;
}
总结:即已经将将数据放入到tb->char_buf_ptr缓冲区中。
解析tty_flip_buffer_push函数:
tty_flip_buffer_push(tty)-->flush_to_ldisc(&tty->buf.work.work)-->disc->ops->receive_buf(tty, char_buf,flag_buf, count)( .receive_buf = n_tty_receive_buf,)-->n_tty_receive_buf
static void flush_to_ldisc(struct work_struct *work)
{
struct tty_struct *tty =
container_of(work, struct tty_struct, buf.work.work);
unsigned long flags;
struct tty_ldisc *disc;
disc = tty_ldisc_ref(tty);
if (disc == NULL) /* !TTY_LDISC */
return;
spin_lock_irqsave(&tty->buf.lock, flags);
if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) {
.
.
.
if (count > tty->receive_room)
count = tty->receive_room;
char_buf = head->char_buf_ptr + head->read;
flag_buf = head->flag_buf_ptr + head->read;
head->read += count;
spin_unlock_irqrestore(&tty->buf.lock, flags);
disc->ops->receive_buf(tty, char_buf,
flag_buf, count);
spin_lock_irqsave(&tty->buf.lock, flags);
}
clear_bit(TTY_FLUSHING, &tty->flags);
}
/* We may have a deferred request to flush the input buffer,
if so pull the chain under the lock and empty the queue */
if (test_bit(TTY_FLUSHPENDING, &tty->flags)) {
__tty_buffer_flush(tty);
clear_bit(TTY_FLUSHPENDING, &tty->flags);
wake_up(&tty->read_wait);
}
spin_unlock_irqrestore(&tty->buf.lock, flags);
tty_ldisc_deref(disc);
}
static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
const unsigned char *p;
char *f, flags = TTY_NORMAL;
int i;
char buf[64];
unsigned long cpuflags;
if (!tty->read_buf)
return;
if (tty->real_raw) {
spin_lock_irqsave(&tty->read_lock, cpuflags);
i = min(N_TTY_BUF_SIZE - tty->read_cnt,
N_TTY_BUF_SIZE - tty->read_head);
i = min(count, i);
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
cp += i;
count -= i;
i = min(N_TTY_BUF_SIZE - tty->read_cnt,
N_TTY_BUF_SIZE - tty->read_head);
i = min(count, i);
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
spin_unlock_irqrestore(&tty->read_lock, cpuflags);
.
.
.
}
总结:即已经将将数据从tb->char_buf_ptr缓冲区中拷贝到tty->read_buf中了。
第二部分:
当用户空间开始读数据时。
tty_read-->ld->ops->read-->n_tty_read-->copy_from_read_buf(tty, &b, &nr)-->copy_to_user(*b, &tty->read_buf[tty->read_tail], n)
static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
int i;
struct tty_struct *tty;
struct inode *inode;
struct tty_ldisc *ld;
tty = (struct tty_struct *)file->private_data;
inode = file->f_path.dentry->d_inode;
if (tty_paranoia_check(tty, inode, "tty_read"))
return -EIO;
if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
/* We want to wait for the line discipline to sort out in this
situation */
ld = tty_ldisc_ref_wait(tty);
if (ld->ops->read)
i = (ld->ops->read)(tty, file, buf, count);
else
i = -EIO;
tty_ldisc_deref(ld);
if (i > 0)
inode->i_atime = current_fs_time(inode->i_sb);
return i;
}
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr)
{
unsigned char __user *b = buf;
DECLARE_WAITQUEUE(wait, current);
int c;
int minimum, time;
ssize_t retval = 0;
ssize_t size;
long timeout;
unsigned long flags;
int packet;
.
.
.
int uncopied;
/* The copy function takes the read lock and handles
locking internally for this case */
uncopied = copy_from_read_buf(tty, &b, &nr);
uncopied += copy_from_read_buf(tty, &b, &nr);
if (uncopied) {
retval = -EFAULT;
break;
}
}
.
.
.
}
static int copy_from_read_buf(struct tty_struct *tty,
unsigned char __user **b,
size_t *nr)
{
int retval;
size_t n;
unsigned long flags;
retval = 0;
spin_lock_irqsave(&tty->read_lock, flags);
n = min(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail);
n = min(*nr, n);
spin_unlock_irqrestore(&tty->read_lock, flags);
if (n) {
retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n);
n -= retval;
tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n);
spin_lock_irqsave(&tty->read_lock, flags);
tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
tty->read_cnt -= n;
spin_unlock_irqrestore(&tty->read_lock, flags);
*b += n;
*nr -= n;
}
return retval;
}
总结:最后将read_buf里面的数据传递给用户空间。而这个数据是第一部分最后放进来的数据。
当串口数据满,fifo数据达到设定阈值时,发生接收中断。
当串口数据空时,发生发送中断。
如下:
1)发送和接收:
发送: 循环buffer -(驱动做)-> 发送 fifo -(硬件自己做)-> 发送移位寄存器
把数据写到发送fifo中。fifo把收到的数据传给发送移位寄存器(自动的,非driver控制),然后每个时钟脉冲往串口线上写一个bit数据
所以是一直是中断告知还可以发送几个。当不足时一直引发中断。
Tx FIFO trigger level选择fifo触发水平,选择什么时候引发中断,如fifo 低于 到4个或8 16 个字节等时中断
接收: 接收移位寄存器 -(硬件自己做)-> 接收fifo -(驱动做)-> flip_buf
接收移位寄存器收到数据后,发送给接收fifo,接收fifo事先设置好触发门限,当里面的数据量超过门限时,就会触发一个中断,调用驱动里的中断处理函数,把数据写到flip_buf中。
Rx FIFO trigger level选择fifo触发水平,选择什么时候引发中断,如收到4个或8 16 个字节等时中断
第一部分:
读操作:TTY驱动从硬件收到数据后,负责把数据传递到TTY 核心,TTY核心将从TTY驱动收到的数据缓存到一个tty_flip_buffer 类型的结构中。该结构包含两个数据数
组。从TTY设备接收到的数据被存储于第一个数组,当这个数组满, 等待数据的用户将被通知。当用户从这个数组读数据时, 任何从TTY驱动新来的数据将被存储在第2个数组。
当第二个数组存满后,数据再次提交给用户, 并且驱动又开始填充第1个数组,以此交替。
一次中断只是把接收的fifobuffer中的数据放到flipbuffer中去,接收的fifo的中断门限是4-12字节,进行一次接收操作往往要中断好多次,
这样中断开销比较大,所以在while的循环条件中判断一下是否还有接收的有效数据,如果有,就继续在中断程序中继续接收,
当然,永远都在接收中断中(如果一直有数据要接收)也不合适,所以while循环还有计数,最多循环64次。
在循环中,首先是要判断一下接收数据用的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)来让用户把存在缓冲里的数据取走,接收一次都要把缓存清空。
static inline void receive_chars(struct w55fa93_uart_port *up, int *status)
{
struct tty_struct *tty = up->port.state->port.tty;
struct uart_port *port = &up->port;
unsigned int ch, flag;
//int max_count = 256;
int max_count = 64;
if(*status & UART_FSR_RFE)
return;
//printk("%s get a char,FSR 0x%x\r\n",up->info->name,*status);
do {
ch = rd_regb(port, W55FA93_COM_RX);
flag = TTY_NORMAL;
up->port.icount.rx++;
if (unlikely(*status & (UART_FSR_BI | UART_FSR_PE |
UART_FSR_FE | UART_FSR_ROE))) {
/*
* For statistics only
*/
if (*status & UART_FSR_BI) {
*status &= ~(UART_FSR_FE | UART_FSR_PE);
up->port.icount.brk++;
/*
* We do the SysRQ and SAK checking
* here because otherwise the break
* may get masked by ignore_status_mask
* or read_status_mask.
*/
if (uart_handle_break(&up->port))
goto ignore_char;
} else if (*status & UART_FSR_PE)
up->port.icount.parity++;
else if (*status & UART_FSR_FE)
up->port.icount.frame++;
if (*status & UART_FSR_ROE)
up->port.icount.overrun++;
/*
* Mask off conditions which should be ignored.
*/
*status &= up->port.read_status_mask;
#ifdef CONFIG_SERIAL_W55FA93_CONSOLE
if (up->port.line == up->port.cons->index) {
/* Recover the break flag from console xmit */
*status |= up->lsr_break_flag;
up->lsr_break_flag = 0;
}
#endif
if (*status & UART_FSR_BI) {
flag = TTY_BREAK;
} else if (*status & UART_FSR_PE)
flag = TTY_PARITY;
else if (*status & UART_FSR_FE)
flag = TTY_FRAME;
}
if (uart_handle_sysrq_char(&up->port, ch))
goto ignore_char;
uart_insert_char(&up->port, *status, UART_FSR_ROE, ch, flag);
//if(up->port.irq == IRQ_UART)
// printk("%s get a char,FSR 0x%x : 0x%x\r\n",up->info->name,*status,ch);
ignore_char:
*status = rd_regl(port, W55FA93_COM_FSR);
} while ((!(*status & UART_FSR_RFE)) && (max_count-- > 0));
tty_flip_buffer_push(tty);
}
解析uart_insert_char:将数据放入tty缓存
uart_insert_char(&up->port, *status, UART_FSR_ROE, ch, flag);-->tty_insert_flip_char(tty, ch, flag)-->tty_insert_flip_string_flags(tty, &ch, &flag, 1)
int tty_insert_flip_string_flags(struct tty_struct *tty,
const unsigned char *chars, const char *flags, size_t size)
{
int copied = 0;
do {
int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE);
int space = tty_buffer_request_room(tty, goal);
struct tty_buffer *tb = tty->buf.tail;
/* If there is no space then tb may be NULL */
if (unlikely(space == 0))
break;
memcpy(tb->char_buf_ptr + tb->used, chars, space);
memcpy(tb->flag_buf_ptr + tb->used, flags, space);
tb->used += space;
copied += space;
chars += space;
flags += space;
/* There is a small chance that we need to split the data over
several buffers. If this is the case we must loop */
} while (unlikely(size > copied));
return copied;
}
总结:即已经将将数据放入到tb->char_buf_ptr缓冲区中。
解析tty_flip_buffer_push函数:
tty_flip_buffer_push(tty)-->flush_to_ldisc(&tty->buf.work.work)-->disc->ops->receive_buf(tty, char_buf,flag_buf, count)( .receive_buf = n_tty_receive_buf,)-->n_tty_receive_buf
static void flush_to_ldisc(struct work_struct *work)
{
struct tty_struct *tty =
container_of(work, struct tty_struct, buf.work.work);
unsigned long flags;
struct tty_ldisc *disc;
disc = tty_ldisc_ref(tty);
if (disc == NULL) /* !TTY_LDISC */
return;
spin_lock_irqsave(&tty->buf.lock, flags);
if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) {
.
.
.
if (count > tty->receive_room)
count = tty->receive_room;
char_buf = head->char_buf_ptr + head->read;
flag_buf = head->flag_buf_ptr + head->read;
head->read += count;
spin_unlock_irqrestore(&tty->buf.lock, flags);
disc->ops->receive_buf(tty, char_buf,
flag_buf, count);
spin_lock_irqsave(&tty->buf.lock, flags);
}
clear_bit(TTY_FLUSHING, &tty->flags);
}
/* We may have a deferred request to flush the input buffer,
if so pull the chain under the lock and empty the queue */
if (test_bit(TTY_FLUSHPENDING, &tty->flags)) {
__tty_buffer_flush(tty);
clear_bit(TTY_FLUSHPENDING, &tty->flags);
wake_up(&tty->read_wait);
}
spin_unlock_irqrestore(&tty->buf.lock, flags);
tty_ldisc_deref(disc);
}
static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
const unsigned char *p;
char *f, flags = TTY_NORMAL;
int i;
char buf[64];
unsigned long cpuflags;
if (!tty->read_buf)
return;
if (tty->real_raw) {
spin_lock_irqsave(&tty->read_lock, cpuflags);
i = min(N_TTY_BUF_SIZE - tty->read_cnt,
N_TTY_BUF_SIZE - tty->read_head);
i = min(count, i);
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
cp += i;
count -= i;
i = min(N_TTY_BUF_SIZE - tty->read_cnt,
N_TTY_BUF_SIZE - tty->read_head);
i = min(count, i);
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
spin_unlock_irqrestore(&tty->read_lock, cpuflags);
.
.
.
}
总结:即已经将将数据从tb->char_buf_ptr缓冲区中拷贝到tty->read_buf中了。
第二部分:
当用户空间开始读数据时。
tty_read-->ld->ops->read-->n_tty_read-->copy_from_read_buf(tty, &b, &nr)-->copy_to_user(*b, &tty->read_buf[tty->read_tail], n)
static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
int i;
struct tty_struct *tty;
struct inode *inode;
struct tty_ldisc *ld;
tty = (struct tty_struct *)file->private_data;
inode = file->f_path.dentry->d_inode;
if (tty_paranoia_check(tty, inode, "tty_read"))
return -EIO;
if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
/* We want to wait for the line discipline to sort out in this
situation */
ld = tty_ldisc_ref_wait(tty);
if (ld->ops->read)
i = (ld->ops->read)(tty, file, buf, count);
else
i = -EIO;
tty_ldisc_deref(ld);
if (i > 0)
inode->i_atime = current_fs_time(inode->i_sb);
return i;
}
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr)
{
unsigned char __user *b = buf;
DECLARE_WAITQUEUE(wait, current);
int c;
int minimum, time;
ssize_t retval = 0;
ssize_t size;
long timeout;
unsigned long flags;
int packet;
.
.
.
int uncopied;
/* The copy function takes the read lock and handles
locking internally for this case */
uncopied = copy_from_read_buf(tty, &b, &nr);
uncopied += copy_from_read_buf(tty, &b, &nr);
if (uncopied) {
retval = -EFAULT;
break;
}
}
.
.
.
}
static int copy_from_read_buf(struct tty_struct *tty,
unsigned char __user **b,
size_t *nr)
{
int retval;
size_t n;
unsigned long flags;
retval = 0;
spin_lock_irqsave(&tty->read_lock, flags);
n = min(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail);
n = min(*nr, n);
spin_unlock_irqrestore(&tty->read_lock, flags);
if (n) {
retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n);
n -= retval;
tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n);
spin_lock_irqsave(&tty->read_lock, flags);
tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
tty->read_cnt -= n;
spin_unlock_irqrestore(&tty->read_lock, flags);
*b += n;
*nr -= n;
}
return retval;
}
总结:最后将read_buf里面的数据传递给用户空间。而这个数据是第一部分最后放进来的数据。