W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
最終, tiny_tty 驅(qū)動聲明了 4 個函數(shù)指針.
open 函數(shù)被 tty 核心調(diào)用, 當一個用戶對這個 tty 驅(qū)動被分配的設(shè)備節(jié)點調(diào)用 open 時. tty 核心使用一個指向分配給這個設(shè)備的 tty_struct 結(jié)構(gòu)的指針調(diào)用它, 還用一個文件指針. 這個 open 成員必須被一個 tty 驅(qū)動為它能正確工作而設(shè)置; 否則, -ENODEV 被返回給用戶當調(diào)用 open 時.
當調(diào)用這個 open 函數(shù), tty 驅(qū)動被期望或者保存一些傳遞給它的 tty_struct 變量中的數(shù)據(jù), 或者保存一個可以基于端口次編號來引用的靜態(tài)數(shù)組中的數(shù)據(jù). 這是有必要的, 所以 tty 驅(qū)動知道哪個設(shè)備在被引用當以后的 close, write, 和其他函數(shù)被調(diào)用時.
tiny_tty 驅(qū)動保存一個指針在 tty 結(jié)構(gòu)中, 如同下面代碼所見到:
static int tiny_open(struct tty_struct *tty, struct file *file)
{
struct tiny_serial *tiny;
struct timer_list *timer;
int index;
/* initialize the pointer in case something fails */
tty->driver_data = NULL;
/* get the serial object associated with this tty pointer */
index = tty->index;
tiny = tiny_table[index];
if (tiny == NULL)
{
/* first time accessing this device, let's create it */
tiny = kmalloc(sizeof(*tiny), GFP_KERNEL);
if (!tiny)
return -ENOMEM;
init_MUTEX(&tiny->sem);
tiny->open_count = 0;
tiny->timer = NULL;
tiny_table[index] = tiny;
}
down(&tiny->sem);
/* save our structure within the tty structure */
tty->driver_data = tiny;
tiny->tty = tty;
在這個代碼中, tiny_serial 結(jié)構(gòu)被保存在 tty 結(jié)構(gòu)中. 這允許 tiny_write, tiny_write_room, 和 tiny_close 函數(shù)來獲取 tiny_serial 結(jié)構(gòu)和正確操作它.
tiny_serial 結(jié)構(gòu)定義為:
struct tiny_serial
{
struct tty_struct *tty; /* pointer to the tty for this device */
int open_count; /* number of times this port has been opened */
struct semaphore sem; /* locks this structure */
struct timer_list *timer;
};
如同我們已見到的, open_count 變量初始化為 0 在第一次打開端口的 open 調(diào)用中. 這是一個典型的引用計數(shù), 因為一個 tty 驅(qū)動的 open 和 close 函數(shù)可能對同一個設(shè)備多次調(diào)用以便多個進程來讀寫數(shù)據(jù). 為正確處理所有的事情, 必須保持一個這個端口被打開或者關(guān)閉的次數(shù)計數(shù); 這個驅(qū)動遞增和遞減這個計數(shù)在打開使用時. 當打開第一次被打開, 任何必要的硬件初始化和內(nèi)存分配可以做. 當端口被最后一次關(guān)閉, 任何必要的硬件關(guān)閉和內(nèi)存清理可以做.
tiny_open 函數(shù)的剩下部分展示了如何跟蹤設(shè)備被打開的次數(shù):
++tiny->open_count;
if (tiny->open_count == 1)
{
/* this is the first time this port is opened */
/* do any hardware initialization needed here */
open 函數(shù)必須返回或者一個負的錯誤號如果發(fā)生事情阻止了成功打開, 或者一個 0 來表示成功.
close 函數(shù)指針被 tty 核心調(diào)用, 在用戶對前面使用 open 調(diào)用而創(chuàng)建的文件句柄調(diào)用 close 時. 這表示設(shè)備應(yīng)當在這次被關(guān)閉. 但是, 因為 open 函數(shù)可被多次調(diào)用, close函數(shù)也可多次調(diào)用. 因此這個函數(shù)應(yīng)當跟蹤它被調(diào)用的次數(shù)來決定是否硬件應(yīng)當在此次真正被關(guān)閉. tiny_tty 驅(qū)動做這個使用下面的代碼:
static void do_close(struct tiny_serial *tiny)
{
down(&tiny->sem);
if (!tiny->open_count)
{
/* port was never opened */
goto exit;
}
--tiny->open_count;
if (tiny->open_count <= 0)
{
/* The port is being closed by the last user. */
/* Do any hardware specific stuff here */
/* shut down our timer */
del_timer(tiny->timer);
}
exit:
up(&tiny->sem);
}
static void tiny_close(struct tty_struct *tty, struct file *file)
{
struct tiny_serial *tiny = tty->driver_data;
if (tiny)
do_close(tiny);
}
tiny_close 函數(shù)只是調(diào)用 do_close 函數(shù)來完成實際的關(guān)閉設(shè)備工作. 因此關(guān)閉邏輯不必在這里和驅(qū)動被卸載和端口被打開時重復(fù). close 函數(shù)沒有返回值, 因為它不被認為會失敗.
write 函數(shù)被用戶在有數(shù)據(jù)發(fā)送給硬件時調(diào)用. 首先 tty 核心接收到調(diào)用, 接著它傳遞數(shù)據(jù)到 tty 驅(qū)動的 write 函數(shù). tty 核心還告知 tty 驅(qū)動要發(fā)送的數(shù)據(jù)大小.
有時, 因為速度和 tty 硬件的緩沖區(qū)容量, 不是所有的寫程序要求的字符可以在調(diào)用寫函數(shù)時發(fā)送. 這個寫函數(shù)應(yīng)當返回能夠發(fā)送給硬件的字符數(shù)( 或者在以后時間可排隊發(fā)送 ), 因此用戶程序可以檢查是否所有的數(shù)據(jù)真正寫入. 這種檢查在用戶空間非常容易完成, 比一個內(nèi)核驅(qū)動站著睡眠直到所有的請求數(shù)據(jù)能夠被發(fā)送. 如果任何錯誤發(fā)生在 wirte 調(diào)用期間, 一個負的錯誤值應(yīng)當被返回代替被寫入的字節(jié)數(shù).
write 函數(shù)可從中斷上下文和用戶上下文中被調(diào)用. 知道這一點是重要的, 因為 tty 驅(qū)動不應(yīng)當調(diào)用任何可能當它在中斷上下文中睡眠的函數(shù). 這些包括任何可能調(diào)用調(diào)度的函數(shù), 例如普通的函數(shù) copy_from_user, kmalloc, 和 printk. 如果你確實想睡眠, 確信去首先檢查是否驅(qū)動在中斷上下文, 通過調(diào)用 calling_in_interrupt.
這個例子 tiny tty 驅(qū)動沒有連接到任何真實的硬件, 因此它的寫函數(shù)簡單地將要寫的什么數(shù)據(jù)記錄到內(nèi)核調(diào)試日志. 它使用下面的代碼做這個:
static int tiny_write(struct tty_struct *tty, const unsigned char *buffer, int count)
{
struct tiny_serial *tiny = tty->driver_data;
int i;
int retval = -EINVAL;
if (!tiny)
return -ENODEV;
down(&tiny->sem);
if (!tiny->open_count)
/* port was not opened */
goto exit;
/* fake sending the data out a hardware port by
* writing it to the kernel debug log.
*/
printk(KERN_DEBUG "%s - ", __FUNCTION__);
for (i = 0; i < count; ++i)
printk("%02x ", buffer[i]);
printk("\n");
exit:
up(&tiny->sem);
return retval;
}
當 tty 子系統(tǒng)自己需要發(fā)送數(shù)據(jù)到 tty 設(shè)備之外, write 函數(shù)被調(diào)用. 如果 tty 驅(qū)動在 tty_struct 中沒有實現(xiàn) put_char 函數(shù), 這會發(fā)生. 在這種情況下, tty 核心用一個數(shù)據(jù)大小為 1 來使用 write 函數(shù)回調(diào). 這普遍發(fā)生在 tty 核心想轉(zhuǎn)換一個新行字符為一個換行和新行字符. 這里的最大的問題是 tty 驅(qū)動的 write 函數(shù)必須不返回 0 對于這類的調(diào)用. 這意味著驅(qū)動必須寫那個數(shù)據(jù)的字節(jié)到設(shè)備, 因為調(diào)用者( tty 核心 ) 不緩沖數(shù)據(jù)和在之后的時間重試. 因為 write 函數(shù)不能知道是否它在被調(diào)用來替代 put_char, 即便只有一個字節(jié)的數(shù)據(jù)被發(fā)送, 盡力實現(xiàn) write 函數(shù)以至于它一直至少在返回前寫一個字節(jié). 許多當前的 USB-到-串口的 tty 驅(qū)動沒有遵照這個規(guī)則, 并且因此, 一些終端類型不能正確工作當連接到它們時.
write_room 函數(shù)被調(diào)用當 tty 核心想知道多少空間在寫緩沖中 tty 驅(qū)動可用. 這個數(shù)字時時改變隨著字符清空寫緩沖以及調(diào)用寫函數(shù)時, 添加字符到這個緩沖.
static int tiny_write_room(struct tty_struct *tty)
{
struct tiny_serial *tiny = tty->driver_data;
int room = -EINVAL;
if (!tiny)
return -ENODEV;
down(&tiny->sem);
if (!tiny->open_count)
{
/* port was not opened */
goto exit;
}
/* calculate how much room is left in the device */
room = 255;
exit:
up(&tiny->sem);
return room;
}
一個工作的 tty 驅(qū)動不需要在 tty_driver 結(jié)構(gòu)中的 chars_in_buffer 函數(shù), 但是它被推薦. 這個函數(shù)被調(diào)用當 tty 核心想知道多少字符仍然保留在 tty 驅(qū)動的寫緩沖中要被發(fā)送. 如果驅(qū)動能夠存儲字符在它發(fā)送它們到硬件之前, 它應(yīng)當實現(xiàn)這個函數(shù)為了 tty 核心能夠知道是否所有的驅(qū)動中的數(shù)據(jù)已經(jīng)流出.
3 個 tty_driver 結(jié)構(gòu)中的函數(shù)回調(diào)可以用來刷新任何驅(qū)動保留的數(shù)據(jù). 它們不被要求實現(xiàn), 但是推薦如果 tty 驅(qū)動能夠緩沖數(shù)據(jù)在它發(fā)送給硬件之前. 前 2 個函數(shù)回調(diào)稱為 flush_chars 和 wait_until_sent. 這些函數(shù)被調(diào)用當 tty 核心使用 put_char 函數(shù)回調(diào)已發(fā)送了許多字符給 tty 驅(qū)動. flush_chars 函數(shù)回調(diào)被調(diào)用當 tty 核心要 tty 驅(qū)動啟動發(fā)送這些字符到硬件, 如果它尚未啟動. 這個函數(shù)被允許在所有的數(shù)據(jù)發(fā)送給硬件之前返回. wait_until_sent 函數(shù)回調(diào)以非常相同的發(fā)生工作; 但是它必須等待直到所有的字符在返回到 tty 核心前被發(fā)送, 或者知道超時值到時. 如果這個傳遞給 wait_until_sent 函數(shù)回調(diào)的超時值設(shè)為 0, 函數(shù)應(yīng)當?shù)却钡剿瓿蛇@個操作.
剩下的數(shù)據(jù)刷新函數(shù)回調(diào)是 flush_buffer. 它被 tty 核心調(diào)用當 tty 驅(qū)動要刷新所有的仍然在它的寫緩沖的數(shù)據(jù). 任何保留在緩沖中的數(shù)據(jù)被丟失并且沒發(fā)送給設(shè)備.
只使用這些函數(shù), tiny_tty 驅(qū)動可被注冊, 可打開一個設(shè)備節(jié)點, 數(shù)據(jù)被寫入設(shè)備, 關(guān)閉設(shè)備節(jié)點, 以驅(qū)動注銷和從內(nèi)核中卸載. 但是 tty 核心和 tty_driver 結(jié)構(gòu)沒有提供一個 read 函數(shù); 換句話說, 沒有函數(shù)調(diào)用存在來從驅(qū)動到 tty 核心獲取數(shù)據(jù).
替代一個傳統(tǒng)的 read 函數(shù), tty 驅(qū)動負責發(fā)送任何從硬件收到的數(shù)據(jù)到 tty 核心. tty 核心緩沖數(shù)據(jù)直到它被用戶請求. 因為 tty 核心提供的緩沖邏輯, 對每個 tty 驅(qū)動不必要實現(xiàn)它自己的緩沖邏輯. tty 核心通知 tty 驅(qū)動當一個用戶要驅(qū)動停止和開始發(fā)送數(shù)據(jù), 但是如果內(nèi)部的 tty 緩沖滿, 沒有這樣的通知發(fā)生.
tty 核心緩沖由 tty 驅(qū)動接收到的數(shù)據(jù), 在一個稱為 struct tty_flip_buffer 的結(jié)構(gòu)中. 一個 flip 緩沖是一個結(jié)構(gòu)包含 2 個主要數(shù)據(jù)數(shù)組. 從 tty 設(shè)備接收到的數(shù)據(jù)被存儲于第一個數(shù)組. 當這個數(shù)組滿, 任何等待數(shù)據(jù)的用戶被通知數(shù)據(jù)可以讀. 當用戶從這個數(shù)組讀數(shù)據(jù), 任何新到的數(shù)據(jù)被存儲在第 2 個數(shù)組. 當那個數(shù)組被讀空, 數(shù)據(jù)再次刷新給用戶, 并且驅(qū)動開始填充第 1 個數(shù)組. 本質(zhì)上, 被接收的數(shù)據(jù) "flips" 從一個緩沖到另一個, 期望不會溢出它們 2 個. 為試圖阻止數(shù)據(jù)丟失, 一個 tty 驅(qū)動可以監(jiān)視到來的數(shù)組多大, 并且, 如果它添滿, 及時告知 tty 驅(qū)動在這個時刻刷新緩沖, 而不是等待下一個可用的機會.
struct tty_flip_buffer 結(jié)構(gòu)的細節(jié)對 tty 驅(qū)動沒有關(guān)系, 只有一個例外, 可用的計數(shù). 這個變量包含多少字節(jié)當前留在緩沖里可用來接收數(shù)據(jù). 如果這個值等于值 TTY_FLIPBUF_SIZE, 這個 flip 緩沖需要被刷新到用戶, 使用一個對 tty_flip_buffer_push 的調(diào)用. 這展示在下面的代碼:
for (i = 0; i < data_size; ++i)
{
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
tty_flip_buffer_push(tty);
tty_insert_flip_char(tty, data[i], TTY_NORMAL);
}
tty_flip_buffer_push(tty);
從 tty 驅(qū)動接收來的要發(fā)送給用戶的字符被添加到 flip 緩沖, 使用對 tty_insert_flip_char 的調(diào)用. 這個函數(shù)的第一個參數(shù)是數(shù)據(jù)應(yīng)當保存入的 struct tty_struct, 第 2 個參數(shù)是要保存的字符, 第 3 個參數(shù)是任何應(yīng)當為這個字符設(shè)置的標志. 這個標志值應(yīng)當設(shè)為 TTY_NORMAL 如果這個是一個正常的被接收的字符. 如果這是一個特殊類型的指示錯誤接收數(shù)據(jù)的字符, 它應(yīng)當設(shè)為 TTY_BREAK, TTY_PARITY, 或者 TTY_OVERRUN, 取決于錯誤.
為了"推"數(shù)據(jù)給用戶, 進行一個對 tty_flip_buffer_push 的調(diào)用. 這個函數(shù)應(yīng)當也被調(diào)用如果 flip 緩沖將要溢出, 如同在這個例子中展示的. 因此無論何時數(shù)據(jù)被加到 flip 緩沖, 或者當 flip 緩沖滿, tty 驅(qū)動必須調(diào)用 tty_flip_buffer_push. 如果 tty 驅(qū)動可高速接收數(shù)據(jù), tty->low_latency 標志應(yīng)當設(shè)置, 它是對 tty_flip_buffer_pus 的調(diào)用被立刻執(zhí)行當調(diào)用時. 否則, tty_flip_buffer_push 調(diào)用會調(diào)度它自己來將數(shù)據(jù)推出緩沖, 在之后近期的一個時間點.
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: