Linux串口驱动(5) - read详解
1. 用戶空間read的操作實(shí)現(xiàn)
static ssize_t tty_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) {int i;struct tty_struct *tty = file_tty(file);struct tty_ldisc *ld;ld = tty_ldisc_ref_wait(tty); /* 獲取tty對(duì)應(yīng)的線路規(guī)程ldisc,和tty_write是一樣的,可以回看《Linux串口驅(qū)動(dòng)(4) - write詳解》 */if (ld->ops->read)i = (ld->ops->read)(tty, file, buf, count); /* ld->ops->read即n_tty_read */elsei = -EIO;tty_ldisc_deref(ld);return i; }static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,unsigned char __user *buf, size_t nr) {struct n_tty_data *ldata = tty->disc_data;unsigned char __user *b = buf;DECLARE_WAITQUEUE(wait, current);if (!ldata->icanon) /*icanon默認(rèn)為1,icanon表示標(biāo)準(zhǔn)模式*/······add_wait_queue(&tty->read_wait, &wait);while (nr) {set_current_state(TASK_INTERRUPTIBLE);if (!input_available_p(tty, 0)) { /*沒有可讀數(shù)據(jù)時(shí)會(huì)休眠*/timeout = schedule_timeout(timeout); /*定時(shí)休眠*/continue;}__set_current_state(TASK_RUNNING);if (ldata->icanon && !L_EXTPROC(tty)) {while (nr && ldata->read_cnt) {int eol;eol = test_and_clear_bit(ldata->read_tail,ldata->read_flags);c = ldata->read_buf[ldata->read_tail]; /*從tty線路規(guī)程的緩沖區(qū)ldata->read_buf讀數(shù)據(jù)*/ldata->read_tail = ((ldata->read_tail+1) &(N_TTY_BUF_SIZE-1));ldata->read_cnt--;raw_spin_unlock_irqrestore(&ldata->read_lock, flags);if (!eol || (c != __DISABLED_CHAR)) {if (tty_put_user(tty, c, b++)) { /*將讀到的數(shù)據(jù)放入用戶buffer*/retval = -EFAULT;b--;raw_spin_lock_irqsave(&ldata->read_lock, flags);break;}nr--;}raw_spin_lock_irqsave(&ldata->read_lock, flags);}raw_spin_unlock_irqrestore(&ldata->read_lock, flags);} }remove_wait_queue(&tty->read_wait, &wait);return retval; }????????write的數(shù)據(jù)為什么是從tty線路規(guī)程的buffer里讀取,這一點(diǎn)可以回看《Linux串口驅(qū)動(dòng)(3) - open詳解》的分析線路2-3部分。
2. 總結(jié)
2.1 DMA
????????關(guān)于DMA搬運(yùn)地址的配置,在open時(shí)會(huì)同時(shí)配置發(fā)送消息時(shí)DMA搬運(yùn)的目標(biāo)地址和接收消息時(shí)DMA搬運(yùn)的源地址。因?yàn)榘l(fā)送數(shù)據(jù)時(shí)要將數(shù)據(jù)搬運(yùn)到哪個(gè)地址是確定的,但是從哪個(gè)地址搬運(yùn)數(shù)據(jù)是不確定的,接收數(shù)據(jù)時(shí)則反之。
????????不確定的那個(gè)地址,會(huì)在啟動(dòng)DMA搬運(yùn)的時(shí)候進(jìn)行配置。
2.2 write和read的不同
????????write是SOC端主動(dòng)發(fā)起的動(dòng)作,所以DMA搬運(yùn)的啟動(dòng)操作是在write函數(shù)的底層操作里調(diào)用的;而read是SOC端的被動(dòng)操作,所以串口在open的時(shí)候就要啟動(dòng)DMA搬運(yùn),將數(shù)據(jù)搬運(yùn)到tty 線路規(guī)程的一個(gè)buffer里,用戶讀的時(shí)候不用去底層讀取,去線路規(guī)程的buffer里讀取即可。
3.?DMA模式
static int start_rx_dma(struct imx_port *sport) {struct dma_chan *chan = sport->dma_chan_rx;struct dma_async_tx_descriptor *desc;desc = dmaengine_prep_dma_cyclic(chan, sport->rx_buf.dmaaddr,sport->rx_buf.buf_len, sport->rx_buf.period_len,DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);desc->callback = dma_rx_callback; //DMA完成一次搬運(yùn)后,會(huì)調(diào)用這個(gè)回調(diào)函數(shù)dmaengine_submit(desc); //將該描述符插入dmaengine驅(qū)動(dòng)的傳輸隊(duì)列dma_async_issue_pending(chan); //啟動(dòng)對(duì)應(yīng)DMA通道上的傳輸sport->dma_is_rxing = 1;return 0; }static void dma_rx_callback(void *data) {dma_rx_work(sport); }static void dma_rx_work(struct imx_port *sport) {struct tty_struct *tty = sport->port.state->port.tty;unsigned int cur_idx = sport->rx_buf.cur_idx;dma_rx_push_data(sport, tty, 0, cur_idx); / *最終串口接收到的數(shù)據(jù)都被放到了tty ldisc的一個(gè)buffer里,用戶空間讀的時(shí)候會(huì)從這個(gè)buffer里取* / }4. 非DMA模式
//如果在open階段置位了中斷使能位,所以當(dāng)RX FIFO有數(shù)據(jù)的時(shí)候會(huì)觸發(fā)中斷 static irqreturn_t imx_int(int irq, void *dev_id) {struct imx_port *sport = dev_id;unsigned int sts;unsigned int sts2;sts = readl(sport->port.membase + USR1);if ((sts & USR1_RRDY || sts & USR1_AGTIM) &&!sport->dma_is_enabled) {if (sts & USR1_AGTIM)writel(USR1_AGTIM, sport->port.membase + USR1);imx_rxint(irq, dev_id);}if (sts & USR1_TRDY &&readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN)imx_txint(irq, dev_id);return IRQ_HANDLED; }static irqreturn_t imx_rxint(int irq, void *dev_id) {struct imx_port *sport = dev_id;unsigned int rx, flg, ignored = 0;struct tty_port *port = &sport->port.state->port;unsigned long flags, temp;spin_lock_irqsave(&sport->port.lock, flags);while (readl(sport->port.membase + USR2) & USR2_RDR) {flg = TTY_NORMAL;sport->port.icount.rx++;//讀取RX FIFO中的數(shù)據(jù)rx = readl(sport->port.membase + URXD0);temp = readl(sport->port.membase + USR2);if (temp & USR2_BRCD) {writel(USR2_BRCD, sport->port.membase + USR2);if (uart_handle_break(&sport->port))continue;}if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx))continue;//將讀取的單個(gè)字節(jié)數(shù)據(jù)放到port的緩沖區(qū)中tty_insert_flip_char(port, rx, flg);}out:spin_unlock_irqrestore(&sport->port.lock, flags);//將port緩沖區(qū)中的數(shù)據(jù)全部拷貝至tty線路規(guī)程的緩沖區(qū)tty_flip_buffer_push(port); return IRQ_HANDLED; }總結(jié)
以上是生活随笔為你收集整理的Linux串口驱动(5) - read详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数学建模(一)—— 人口增长模型的确定
- 下一篇: Android Studio 统计代码行