1. 程式人生 > >串列埠驅動詳細分析

串列埠驅動詳細分析

串列埠驅動(使用中斷)完整讀操作
當串列埠資料滿,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 -(硬體自己做)-> 傳送移位暫存器      

wince串列驅動分析(轉)

wince串列埠驅動分析 序列通訊介面主要是指UART(通用序列)和IRDA兩種。通常的序列連線電氣連線上有3wire和9wire兩種。3wire的接線方式下定義了傳送、接收和地三根連線。其用途就如名稱一樣分別用於傳送、接收。 通常在序列介面控制器上會有兩個FIFO用

Linux串列驅動程式(2)-串列驅動程式初始化分析

1、串列埠驅動程式結構分析 對使用者來講,能夠正常使用串列埠肯定是需要實現如下函式的: 1、串列埠裝置檔案的開啟 2、串列埠裝置檔案的初始化 3、串列埠裝置檔案的讀寫 4、串列埠裝置檔案的控制 2、串列埠驅動中重要的資料結構 首先分析一下串列埠讀寫的流程 當用戶讀寫串列埠

Linux串列驅動分析初始化

<pre name="code" class="cpp">/** * uart分析 * * 其實串列埠分析就兩個重要的檔案: S3c2440.c Samsung.c * * **/ /*1. 首先從Samsung.c的模組初始化函式看起*/ s

MTK串列驅動開發

MTK串列埠驅動開發 由於最近在工作中需要使用MTK的MT6261進行移動嵌入式裝置的開發,所以將MTK串列埠驅動開發流程貼出來分享給大家。 1.使用串列埠工具配置UART管腳,此處配置的是UART2開啟原始碼目錄下的\custom\drv\Drv_Tool\DrvGen.exe

Linux 串列驅動相關

Linux串列埠驅動相關主要涉及3個重要的結構體,uart_driver,uart_port,uart_ops。本文主要以msm8917平臺分析, 先貼dts相關程式碼 blsp1_uart2: [email protected]78b0000 { compatible

linux使用USB轉串列驅動設定

【一】、驅動相關說明: 如果直接使用串列埠線,而沒有用到USB轉串列埠裝置,就不需要安裝驅動。 如果使用了USB轉串列埠,一般情況下也不需要安裝驅動了,目前linux系統已經包含了該驅動,可以自動識別,亦可通過以下命令檢視以便確認是否支援。 檢視模組裝載的情況: 引用 lsmod |

ubuntu安裝USB轉串列驅動(PL2303)

在Ubuntu下利用minicom進行嵌入式開發時可能會用到USB轉串列埠,這時就會用到USB轉串列埠驅動,以前的Ubuntu是直接將此驅動編譯進核心,但不知道從哪個版本開始Ubuntu將其從核心去掉了,所以要用到Ubuntu的minicom時只能由我們自己安裝USB轉串列埠驅動,方法如下:

ITOP4412裸機程式設計-串列驅動

文章目錄 前言: 原理分析: 原始碼:        修改main.S        修改exynos4412.h    

WIN7 64位系統 CDC類 虛擬串列驅動無法安裝的解決辦法(2)

(1)最近用STM32使用USB——CDC類出現驅動安裝失敗的情況。 百度了一些網頁,方法很多,大多數是按照如下步驟處理: 首先,確保C:\Windows\System32\drivers\usbser.sys檔案存在; 其次,修改C:\Windows\inf\mdmcpq.inf檔

WIN7 64位系統 CDC類 虛擬串列驅動無法安裝的解決辦法

最近用STM32使用USB——CDC類出現驅動安裝失敗的情況。 百度了一些網頁,方法很多,但是我這裡按如下步驟處理: 首先,確保C:\Windows\System32\drivers\usbser.sys檔案存在; 其次,修改C:\Windows\inf\mdmcpq.inf檔案;

51微控制器入門_使用keil新建工程以及串列驅動下載和程式燒寫教程

        51微控制器是很簡單的一款微控制器,適合於新手的入門學習,但是也只能作為初學者繼續往上學習的一個墊腳石。這篇部落格我主要的目的是寫給我們學校社團的萌新閱覽的,方便他們入門51,繼續堅持學習下去。假如你有一個51微控

WIN7 CDC類 虛擬串列驅動無法安裝的解決辦法

最近用STM32做了個USB轉虛擬串列埠,但是驅動怎麼也安裝不上。因為曾經用清理工具把系統內不用的驅動清理過,所以關鍵的usbser.sys什麼的都沒有,但是下載後新增到系統內還是不行。 百度一番後,找到了解決辦法。 發一個關於cdc comms interface驅動無

USB轉串列驅動應用於macbook

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

STM32 UART串列驅動程式

文章原始地址: http://feotech.com/?p=56 示例1.通過UART1進行資料傳送 UART 1 的初始化 /** * @brief UART1 Initialise. * @param None. * @retval None. */ void UART

TX2 安裝ttyACM串列驅動

NVIDIA Jetson TX2 編譯並啟動ttyACM模組 平臺: TX2 L4T 27.1 系統:ubuntu 16.04 感測器:hokuyo UTM-30LX laser 有兩個TX2,第一個連上hokuyo鐳射時,直接就能識別ttyACM。當用另一臺TX

MFC操作串列詳細 複製程式碼(ActiveX控制元件和Windows API函式)

/******************************************************************* *******函式功能:開啟串列埠裝置連結 *******函式名稱:OpenComm *******輸入引數:無 *******輸出引數:無 ***

hisi35xx串列驅動的完善

鑑於hisi的uart3還沒有打通,ls /dev/ | grep ttyA* ,看到的只有ttyAMA0 和ttyAMA1,且使用應用程式開啟ttyAMA1裝置後,使用write函式,傳送,示波器觀察沒有波形輸出。 猜想是GPIO複用管腳沒開啟於是編寫了GPIO複用管腳驅動,載入驅動,ttyAMA1可以正

WIN10 64位版本下如何解決 PL232串列驅動安裝失敗的情況

      做研發,搭建編譯環境有時候是個講究運氣的活,運氣不好,環境就容易搭建失敗,折騰人。嵌入式開發的兄弟們離不開PL232串列埠,因為需要經常通過串列埠將除錯資訊輸出到PC端的串列埠除錯助手中。現在WINDOWS系統均已經更新到WIN10作業系統,但是PL232的驅動在

Imx8串列故障案例分析

        最近解決一故障——在imx8qxp單板按“上下鍵”或者貼上較長命令均會導致系統宕機。最後查出來是驅動移植問題。在此做下記錄,免得以後忘記,也對以後有類似串列埠問題提供一個參考。         故