Linux串口驱动分析read

来源:互联网 发布:北京seo公司 编辑:程序博客网 时间:2024/05/18 00:56
/*串口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);elsei = -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);}}


0 0
原创粉丝点击