您的位置:首页 > 运维架构 > Linux

Linux串口驱动分析read

2015-01-16 13:44 344 查看
/*串口read函数分析
* 当应用程序调用read系统调用时,会调用tty_fops中的tty_read
* 接下来分析tty_read函数
*
* 其中最重要的就是ld->ops->read(tty,file,buf,count);
* 也就是调用线路规程中read函数
*/

static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
int i;
struct inode *inode = file->f_path.dentry->d_inode;
struct tty_struct *tty = file_tty(file);
struct tty_ldisc *ld;

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;
}

/* 线路规程的ops是: tty_ldisc_N_TTY
* read =   = n_tty_read,
* buf代表的是用户空间传下来的buf, 将来需要我们把数据写到buf中去
*/
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;

/*其实n_tty_read就是调用copy_from_read_buf将tty->read_buf中的数据送到用户传下来的buf中。 前面都是一些合法的判断
*/
uncopied = copy_from_read_buf(tty, &b, &nr);
uncopied += copy_from_read_buf(tty, &b, &nr);
}

/*** 其实从copy_from_read_buf中可以很明显的看见*/
static int copy_from_read_buf(struct tty_struct *tty,unsigned char __user **b,size_t *nr)
{
/*很明显的可以看见copy_to_user函数。数据是从tty->read_buf中拷贝到b中去的。
* 那么tty->read中的数据那又是从那里来的?
*/
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;
/* Turn single EOF into zero-length read */
if (L_EXTPROC(tty) && tty->icanon && n == 1) {
if (!tty->read_cnt && (*b)[n-1] == EOF_CHAR(tty))
n--;
}
spin_unlock_irqrestore(&tty->read_lock, flags);
*b += n;
*nr -= n;
}
return retval;
}

/*接下来分析tty->read_buf中的数据是从那里来的?
* 首先: 数据当然是从硬件里read出来的。
* 那么当我们的串口有数据的话,当然就调用我们以前注册的rx中断函数了。
*/

static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id)
{
struct s3c24xx_uart_port *ourport = dev_id;
struct uart_port *port = &ourport->port;
struct tty_struct *tty = port->state->port.tty;
unsigned int ufcon, ch, flag, ufstat, uerstat;
int max_count = 64;

while (max_count-- > 0) {
/*读取UFCON串口配置寄存器*/
ufcon = rd_regl(port, S3C2410_UFCON);
/*读取 UFSTAT串口状态寄存器。*/
ufstat = rd_regl(port, S3C2410_UFSTAT);

/*根据读出的ufstat判断UFSTAT中rx的fifo是否为0*/
if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
break;

/*读取UERSTAT错误状态寄存器*/
uerstat = rd_regl(port, S3C2410_UERSTAT);
/*读取URXH寄存器*/
ch = rd_regb(port, S3C2410_URXH);

/*进行流量控制*/
if (port->flags & UPF_CONS_FLOW) {
int txe = s3c24xx_serial_txempty_nofifo(port);

if (rx_enabled(port)) {
if (!txe) {
rx_enabled(port) = 0;
continue;
}
} else {
if (txe) {
ufcon |= S3C2410_UFCON_RESETRX;
wr_regl(port, S3C2410_UFCON, ufcon);
rx_enabled(port) = 1;
goto out;
}
continue;
}
}

/* insert the character into the buffer */

flag = TTY_NORMAL;
port->icount.rx++;

if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) {
dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n",
ch, uerstat);

/* check for break */
if (uerstat & S3C2410_UERSTAT_BREAK) {
dbg("break!\n");
port->icount.brk++;
if (uart_handle_break(port))
goto ignore_char;
}

if (uerstat & S3C2410_UERSTAT_FRAME)
port->icount.frame++;
if (uerstat & S3C2410_UERSTAT_OVERRUN)
port->icount.overrun++;

uerstat &= port->read_status_mask;

if (uerstat & S3C2410_UERSTAT_BREAK)
flag = TTY_BREAK;
else if (uerstat & S3C2410_UERSTAT_PARITY)
flag = TTY_PARITY;
else if (uerstat & (S3C2410_UERSTAT_FRAME |
S3C2410_UERSTAT_OVERRUN))
flag = TTY_FRAME;
}

if (uart_handle_sysrq_char(port, ch))
goto ignore_char;

/*插入ch也就是数据到tty->tty_bufhead中去。 当whiel大循环完后, 整个64字节数据都存放到tty->tty_bufhead中去*/
uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN,
ch, flag);
}

/*这是才将整个数据送tty->read_buf中去*/
tty_flip_buffer_push(tty);
}
/* 将串口产生的数据送进tty->buf.tail中去。 */
static inline int tty_insert_flip_char(struct tty_struct *tty,unsigned char ch, char flag)
{
struct tty_buffer *tb = tty->buf.tail;
if (tb && tb->used < tb->size) {
tb->flag_buf_ptr[tb->used] = flag;		/*用于存放flag,也就是状态位*/
tb->char_buf_ptr[tb->used++] = ch;		/*用于存放真正的数据*/
return 1;
}
return tty_insert_flip_string_flags(tty, &ch, &flag, 1);
}

static void flush_to_ldisc(struct work_struct *work)
{
char_buf = head->char_buf_ptr + head->read;		/*char_buf用于存放真实数据*/
flag_buf = head->flag_buf_ptr + head->read;   /*flag_buf用于存放flag标志*/
head->read += count;
spin_unlock_irqrestore(&tty->buf.lock, flags);
disc->ops->receive_buf(tty, char_buf,			/*调用tty_ldisc_N_TTY中的recive_buf函数*/
flag_buf, count);
}

static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,char *fp, int count)
{

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_buf中去*/
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1); /*其实read_buf是一个环形缓冲区。 每次写进数据都会调正read_head的位置。 同时改变read_cnt*/
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);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux