W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
最終, tiny_tty 驅(qū)動(dòng)聲明了 4 個(gè)函數(shù)指針.
open 函數(shù)被 tty 核心調(diào)用, 當(dāng)一個(gè)用戶對這個(gè) tty 驅(qū)動(dòng)被分配的設(shè)備節(jié)點(diǎn)調(diào)用 open 時(shí). tty 核心使用一個(gè)指向分配給這個(gè)設(shè)備的 tty_struct 結(jié)構(gòu)的指針調(diào)用它, 還用一個(gè)文件指針. 這個(gè) open 成員必須被一個(gè) tty 驅(qū)動(dòng)為它能正確工作而設(shè)置; 否則, -ENODEV 被返回給用戶當(dāng)調(diào)用 open 時(shí).
當(dāng)調(diào)用這個(gè) open 函數(shù), tty 驅(qū)動(dòng)被期望或者保存一些傳遞給它的 tty_struct 變量中的數(shù)據(jù), 或者保存一個(gè)可以基于端口次編號來引用的靜態(tài)數(shù)組中的數(shù)據(jù). 這是有必要的, 所以 tty 驅(qū)動(dòng)知道哪個(gè)設(shè)備在被引用當(dāng)以后的 close, write, 和其他函數(shù)被調(diào)用時(shí).
tiny_tty 驅(qū)動(dòng)保存一個(gè)指針在 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;
在這個(gè)代碼中, 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)用中. 這是一個(gè)典型的引用計(jì)數(shù), 因?yàn)橐粋€(gè) tty 驅(qū)動(dòng)的 open 和 close 函數(shù)可能對同一個(gè)設(shè)備多次調(diào)用以便多個(gè)進(jìn)程來讀寫數(shù)據(jù). 為正確處理所有的事情, 必須保持一個(gè)這個(gè)端口被打開或者關(guān)閉的次數(shù)計(jì)數(shù); 這個(gè)驅(qū)動(dòng)遞增和遞減這個(gè)計(jì)數(shù)在打開使用時(shí). 當(dāng)打開第一次被打開, 任何必要的硬件初始化和內(nèi)存分配可以做. 當(dāng)端口被最后一次關(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ù)必須返回或者一個(gè)負(fù)的錯(cuò)誤號如果發(fā)生事情阻止了成功打開, 或者一個(gè) 0 來表示成功.
close 函數(shù)指針被 tty 核心調(diào)用, 在用戶對前面使用 open 調(diào)用而創(chuàng)建的文件句柄調(diào)用 close 時(shí). 這表示設(shè)備應(yīng)當(dāng)在這次被關(guān)閉. 但是, 因?yàn)?open 函數(shù)可被多次調(diào)用, close函數(shù)也可多次調(diào)用. 因此這個(gè)函數(shù)應(yīng)當(dāng)跟蹤它被調(diào)用的次數(shù)來決定是否硬件應(yīng)當(dāng)在此次真正被關(guān)閉. tiny_tty 驅(qū)動(dòng)做這個(gè)使用下面的代碼:
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ù)來完成實(shí)際的關(guān)閉設(shè)備工作. 因此關(guān)閉邏輯不必在這里和驅(qū)動(dòng)被卸載和端口被打開時(shí)重復(fù). close 函數(shù)沒有返回值, 因?yàn)樗槐徽J(rèn)為會(huì)失敗.
write 函數(shù)被用戶在有數(shù)據(jù)發(fā)送給硬件時(shí)調(diào)用. 首先 tty 核心接收到調(diào)用, 接著它傳遞數(shù)據(jù)到 tty 驅(qū)動(dòng)的 write 函數(shù). tty 核心還告知 tty 驅(qū)動(dòng)要發(fā)送的數(shù)據(jù)大小.
有時(shí), 因?yàn)樗俣群?tty 硬件的緩沖區(qū)容量, 不是所有的寫程序要求的字符可以在調(diào)用寫函數(shù)時(shí)發(fā)送. 這個(gè)寫函數(shù)應(yīng)當(dāng)返回能夠發(fā)送給硬件的字符數(shù)( 或者在以后時(shí)間可排隊(duì)發(fā)送 ), 因此用戶程序可以檢查是否所有的數(shù)據(jù)真正寫入. 這種檢查在用戶空間非常容易完成, 比一個(gè)內(nèi)核驅(qū)動(dòng)站著睡眠直到所有的請求數(shù)據(jù)能夠被發(fā)送. 如果任何錯(cuò)誤發(fā)生在 wirte 調(diào)用期間, 一個(gè)負(fù)的錯(cuò)誤值應(yīng)當(dāng)被返回代替被寫入的字節(jié)數(shù).
write 函數(shù)可從中斷上下文和用戶上下文中被調(diào)用. 知道這一點(diǎn)是重要的, 因?yàn)?tty 驅(qū)動(dòng)不應(yīng)當(dāng)調(diào)用任何可能當(dāng)它在中斷上下文中睡眠的函數(shù). 這些包括任何可能調(diào)用調(diào)度的函數(shù), 例如普通的函數(shù) copy_from_user, kmalloc, 和 printk. 如果你確實(shí)想睡眠, 確信去首先檢查是否驅(qū)動(dòng)在中斷上下文, 通過調(diào)用 calling_in_interrupt.
這個(gè)例子 tiny tty 驅(qū)動(dòng)沒有連接到任何真實(shí)的硬件, 因此它的寫函數(shù)簡單地將要寫的什么數(shù)據(jù)記錄到內(nèi)核調(diào)試日志. 它使用下面的代碼做這個(gè):
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;
}
當(dāng) tty 子系統(tǒng)自己需要發(fā)送數(shù)據(jù)到 tty 設(shè)備之外, write 函數(shù)被調(diào)用. 如果 tty 驅(qū)動(dòng)在 tty_struct 中沒有實(shí)現(xiàn) put_char 函數(shù), 這會(huì)發(fā)生. 在這種情況下, tty 核心用一個(gè)數(shù)據(jù)大小為 1 來使用 write 函數(shù)回調(diào). 這普遍發(fā)生在 tty 核心想轉(zhuǎn)換一個(gè)新行字符為一個(gè)換行和新行字符. 這里的最大的問題是 tty 驅(qū)動(dòng)的 write 函數(shù)必須不返回 0 對于這類的調(diào)用. 這意味著驅(qū)動(dòng)必須寫那個(gè)數(shù)據(jù)的字節(jié)到設(shè)備, 因?yàn)檎{(diào)用者( tty 核心 ) 不緩沖數(shù)據(jù)和在之后的時(shí)間重試. 因?yàn)?write 函數(shù)不能知道是否它在被調(diào)用來替代 put_char, 即便只有一個(gè)字節(jié)的數(shù)據(jù)被發(fā)送, 盡力實(shí)現(xiàn) write 函數(shù)以至于它一直至少在返回前寫一個(gè)字節(jié). 許多當(dāng)前的 USB-到-串口的 tty 驅(qū)動(dòng)沒有遵照這個(gè)規(guī)則, 并且因此, 一些終端類型不能正確工作當(dāng)連接到它們時(shí).
write_room 函數(shù)被調(diào)用當(dāng) tty 核心想知道多少空間在寫緩沖中 tty 驅(qū)動(dòng)可用. 這個(gè)數(shù)字時(shí)時(shí)改變隨著字符清空寫緩沖以及調(diào)用寫函數(shù)時(shí), 添加字符到這個(gè)緩沖.
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;
}
一個(gè)工作的 tty 驅(qū)動(dòng)不需要在 tty_driver 結(jié)構(gòu)中的 chars_in_buffer 函數(shù), 但是它被推薦. 這個(gè)函數(shù)被調(diào)用當(dāng) tty 核心想知道多少字符仍然保留在 tty 驅(qū)動(dòng)的寫緩沖中要被發(fā)送. 如果驅(qū)動(dòng)能夠存儲(chǔ)字符在它發(fā)送它們到硬件之前, 它應(yīng)當(dāng)實(shí)現(xiàn)這個(gè)函數(shù)為了 tty 核心能夠知道是否所有的驅(qū)動(dòng)中的數(shù)據(jù)已經(jīng)流出.
3 個(gè) tty_driver 結(jié)構(gòu)中的函數(shù)回調(diào)可以用來刷新任何驅(qū)動(dòng)保留的數(shù)據(jù). 它們不被要求實(shí)現(xiàn), 但是推薦如果 tty 驅(qū)動(dòng)能夠緩沖數(shù)據(jù)在它發(fā)送給硬件之前. 前 2 個(gè)函數(shù)回調(diào)稱為 flush_chars 和 wait_until_sent. 這些函數(shù)被調(diào)用當(dāng) tty 核心使用 put_char 函數(shù)回調(diào)已發(fā)送了許多字符給 tty 驅(qū)動(dòng). flush_chars 函數(shù)回調(diào)被調(diào)用當(dāng) tty 核心要 tty 驅(qū)動(dòng)啟動(dòng)發(fā)送這些字符到硬件, 如果它尚未啟動(dòng). 這個(gè)函數(shù)被允許在所有的數(shù)據(jù)發(fā)送給硬件之前返回. wait_until_sent 函數(shù)回調(diào)以非常相同的發(fā)生工作; 但是它必須等待直到所有的字符在返回到 tty 核心前被發(fā)送, 或者知道超時(shí)值到時(shí). 如果這個(gè)傳遞給 wait_until_sent 函數(shù)回調(diào)的超時(shí)值設(shè)為 0, 函數(shù)應(yīng)當(dāng)?shù)却钡剿瓿蛇@個(gè)操作.
剩下的數(shù)據(jù)刷新函數(shù)回調(diào)是 flush_buffer. 它被 tty 核心調(diào)用當(dāng) tty 驅(qū)動(dòng)要刷新所有的仍然在它的寫緩沖的數(shù)據(jù). 任何保留在緩沖中的數(shù)據(jù)被丟失并且沒發(fā)送給設(shè)備.
只使用這些函數(shù), tiny_tty 驅(qū)動(dòng)可被注冊, 可打開一個(gè)設(shè)備節(jié)點(diǎn), 數(shù)據(jù)被寫入設(shè)備, 關(guān)閉設(shè)備節(jié)點(diǎn), 以驅(qū)動(dòng)注銷和從內(nèi)核中卸載. 但是 tty 核心和 tty_driver 結(jié)構(gòu)沒有提供一個(gè) read 函數(shù); 換句話說, 沒有函數(shù)調(diào)用存在來從驅(qū)動(dòng)到 tty 核心獲取數(shù)據(jù).
替代一個(gè)傳統(tǒng)的 read 函數(shù), tty 驅(qū)動(dòng)負(fù)責(zé)發(fā)送任何從硬件收到的數(shù)據(jù)到 tty 核心. tty 核心緩沖數(shù)據(jù)直到它被用戶請求. 因?yàn)?tty 核心提供的緩沖邏輯, 對每個(gè) tty 驅(qū)動(dòng)不必要實(shí)現(xiàn)它自己的緩沖邏輯. tty 核心通知 tty 驅(qū)動(dòng)當(dāng)一個(gè)用戶要驅(qū)動(dòng)停止和開始發(fā)送數(shù)據(jù), 但是如果內(nèi)部的 tty 緩沖滿, 沒有這樣的通知發(fā)生.
tty 核心緩沖由 tty 驅(qū)動(dòng)接收到的數(shù)據(jù), 在一個(gè)稱為 struct tty_flip_buffer 的結(jié)構(gòu)中. 一個(gè) flip 緩沖是一個(gè)結(jié)構(gòu)包含 2 個(gè)主要數(shù)據(jù)數(shù)組. 從 tty 設(shè)備接收到的數(shù)據(jù)被存儲(chǔ)于第一個(gè)數(shù)組. 當(dāng)這個(gè)數(shù)組滿, 任何等待數(shù)據(jù)的用戶被通知數(shù)據(jù)可以讀. 當(dāng)用戶從這個(gè)數(shù)組讀數(shù)據(jù), 任何新到的數(shù)據(jù)被存儲(chǔ)在第 2 個(gè)數(shù)組. 當(dāng)那個(gè)數(shù)組被讀空, 數(shù)據(jù)再次刷新給用戶, 并且驅(qū)動(dòng)開始填充第 1 個(gè)數(shù)組. 本質(zhì)上, 被接收的數(shù)據(jù) "flips" 從一個(gè)緩沖到另一個(gè), 期望不會(huì)溢出它們 2 個(gè). 為試圖阻止數(shù)據(jù)丟失, 一個(gè) tty 驅(qū)動(dòng)可以監(jiān)視到來的數(shù)組多大, 并且, 如果它添滿, 及時(shí)告知 tty 驅(qū)動(dòng)在這個(gè)時(shí)刻刷新緩沖, 而不是等待下一個(gè)可用的機(jī)會(huì).
struct tty_flip_buffer 結(jié)構(gòu)的細(xì)節(jié)對 tty 驅(qū)動(dòng)沒有關(guān)系, 只有一個(gè)例外, 可用的計(jì)數(shù). 這個(gè)變量包含多少字節(jié)當(dāng)前留在緩沖里可用來接收數(shù)據(jù). 如果這個(gè)值等于值 TTY_FLIPBUF_SIZE, 這個(gè) flip 緩沖需要被刷新到用戶, 使用一個(gè)對 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ū)動(dòng)接收來的要發(fā)送給用戶的字符被添加到 flip 緩沖, 使用對 tty_insert_flip_char 的調(diào)用. 這個(gè)函數(shù)的第一個(gè)參數(shù)是數(shù)據(jù)應(yīng)當(dāng)保存入的 struct tty_struct, 第 2 個(gè)參數(shù)是要保存的字符, 第 3 個(gè)參數(shù)是任何應(yīng)當(dāng)為這個(gè)字符設(shè)置的標(biāo)志. 這個(gè)標(biāo)志值應(yīng)當(dāng)設(shè)為 TTY_NORMAL 如果這個(gè)是一個(gè)正常的被接收的字符. 如果這是一個(gè)特殊類型的指示錯(cuò)誤接收數(shù)據(jù)的字符, 它應(yīng)當(dāng)設(shè)為 TTY_BREAK, TTY_PARITY, 或者 TTY_OVERRUN, 取決于錯(cuò)誤.
為了"推"數(shù)據(jù)給用戶, 進(jìn)行一個(gè)對 tty_flip_buffer_push 的調(diào)用. 這個(gè)函數(shù)應(yīng)當(dāng)也被調(diào)用如果 flip 緩沖將要溢出, 如同在這個(gè)例子中展示的. 因此無論何時(shí)數(shù)據(jù)被加到 flip 緩沖, 或者當(dāng) flip 緩沖滿, tty 驅(qū)動(dòng)必須調(diào)用 tty_flip_buffer_push. 如果 tty 驅(qū)動(dòng)可高速接收數(shù)據(jù), tty->low_latency 標(biāo)志應(yīng)當(dāng)設(shè)置, 它是對 tty_flip_buffer_pus 的調(diào)用被立刻執(zhí)行當(dāng)調(diào)用時(shí). 否則, tty_flip_buffer_push 調(diào)用會(huì)調(diào)度它自己來將數(shù)據(jù)推出緩沖, 在之后近期的一個(gè)時(shí)間點(diǎn).
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: