串列埠驅動詳細分析
當串列埠資料滿,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 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
//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
}
解析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單板按“上下鍵”或者貼上較長命令均會導致系統宕機。最後查出來是驅動移植問題。在此做下記錄,免得以後忘記,也對以後有類似串列埠問題提供一個參考。 故