www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當前位置:首頁 > 嵌入式 > 嵌入式軟件
[導(dǎo)讀] 一、首先把tty驅(qū)動在linux中的分層結(jié)構(gòu)理清楚:自上而下分為TTY核心層、TTY線路規(guī)程、TTY驅(qū)動。二、TTY核心層與線路規(guī)程層分析用戶空間的程序直接對tty核心層進行讀寫等相

 一、首先把tty驅(qū)動在linux中的分層結(jié)構(gòu)理清楚:

自上而下分為TTY核心層、TTY線路規(guī)程、TTY驅(qū)動。

二、TTY核心層與線路規(guī)程層分析

用戶空間的程序直接對tty核心層進行讀寫等相關(guān)操作,在tty_io.c中:

int__init tty_init(void)

{

cdev_init(&tty_cdev,&tty_fops);

if(cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||

register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty")< 0)

panic("Couldn‘tregister /dev/tty drivern");

device_create(tty_class,NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");

………

以上的一段初始化代碼可以獲取以下信息:

注冊了一個字符驅(qū)動,用戶空間操作對應(yīng)到tty_fops結(jié)構(gòu)體里的函數(shù):

staticconst struct file_operations tty_fops = {

。llseek =no_llseek,

。read =tty_read,

。write =tty_write,

。poll =tty_poll,

。unlocked_ioctl =tty_ioctl,

。compat_ioctl =tty_compat_ioctl,

。open =tty_open,

。release =tty_release,

。fasync =tty_fasync,

};

對于字符設(shè)備驅(qū)動,我們知道,讀寫操作一一對應(yīng)于fops.

tty_open:

static int tty_open(struct inode *inode, struct file *filp)

{

int index;

dev_tdevice = inode->i_rdev;

structtty_driver *driver;

……

driver= get_tty_driver(device, &index);

……

tty= tty_init_dev(driver, index, 0);

……

retval= tty_add_file(tty, filp);

……

if(tty->ops->open)

retval= tty->ops->open(tty, filp);

get_tty_driver是根據(jù)設(shè)備號device,通過查找tty_drivers全局鏈表來查找tty_driver.

tty_init_dev是初始化一個tty結(jié)構(gòu)體:

tty->driver= driver;

tty->ops= driver->ops;

并建立線路規(guī)程:

ldops= tty_ldiscs[N_TTY];

ld->ops= ldops;

tty->ldisc= ld;

其實tty_ldiscs[N_TTY]在console_init中確定,該函數(shù)在內(nèi)核啟動的時候調(diào)用。

tty_register_ldisc(N_TTY,&tty_ldisc_N_TTY);

則:tty_ldiscs[N_TTY]=&tty_ldisc_N_TTY;

struct tty_ldisc_ops tty_ldisc_N_TTY = {

。magic = TTY_LDISC_MAGIC,

。name = "n_tty",

。open = n_tty_open,

。close = n_tty_close,

。flush_buffer = n_tty_flush_buffer,

。chars_in_buffer= n_tty_chars_in_buffer,

。read = n_tty_read,

。write = n_tty_write,

。ioctl = n_tty_ioctl,

。set_termios = n_tty_set_termios,

。poll = n_tty_poll,

。receive_buf = n_tty_receive_buf,

。write_wakeup = n_tty_write_wakeup

};

tty_add_file主要是將tty保存到file的私有變量private_data中。

tty->ops->open的調(diào)用,實則上就是應(yīng)用driver->ops->open.這樣,我們就從tty核心層到tty驅(qū)動層了。

tty_write:

static ssize_t tty_write(struct file *file, const char __user *buf,

size_t count, loff_t *ppos)

{

………

ld= tty_ldisc_ref_wait(tty);

if(!ld->ops->write)

ret= -EIO;

else

ret= do_tty_write(ld->ops->write, tty, file, buf, count);

………

}

從以上這個函數(shù)里,可以看到tty_write調(diào)用路線規(guī)程的write函數(shù),所以,我們來看ldisc中的write函數(shù)是怎樣的。經(jīng)過一些操作后,最終調(diào)用:

tty->ops->flush_chars(tty);

tty->ops->write(tty,b, nr);

顯然,這兩個函數(shù),都調(diào)用了tty_driver操作函數(shù),因為在之前的tty_open函數(shù)中有了tty->ops=driver-> ops這樣的操作。那么這個tty_driver是怎樣的呢,在TTY系統(tǒng)中,tty_driver是需要在驅(qū)動層注冊的。注冊的時候就初始化了ops, 也就是說,接下來的事情要看tty_driver的了。

tty_read:

static ssize_t tty_read(struct file *file, char __user *buf, size_t count,

loff_t *ppos)

{

………

ld= tty_ldisc_ref_wait(tty);

if(ld->ops->read)

i= (ld->ops->read)(tty, file, buf, count);

else

i= -EIO;

……

}

像tty_write的一樣,在tty_read里,也調(diào)用了線路規(guī)程的對應(yīng)read函數(shù)。不同的是,這個read沒有調(diào)用tty_driver里ops的read,而是這樣:

uncopied= copy_from_read_buf(tty, &b, &nr);

uncopied+= copy_from_read_buf(tty, &b, &nr);

從函數(shù)名來看copy_from_read_buf,就是從read_buf這個緩沖區(qū)拷貝數(shù)據(jù)。實際上是在tty->read_buf的末尾 tty->read_tail中讀取數(shù)據(jù)。那么read_buf中的數(shù)據(jù)是怎么來的呢?猜想,那肯定是tty_driver干的事了。

tty_ioctl:

long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

{

……

switch(cmd) {

case… …… :[!--empirenews.page--]

………

}

就是根據(jù)cmd的值進行相關(guān)操作,有對線路規(guī)程操作的,有直接通過tty_driver操作的。

三、TTY驅(qū)動層分析

接下來看,TTY驅(qū)動層是怎樣的:

TTY驅(qū)動層是根據(jù)不同的硬件操作來完成相應(yīng)的操作,這里我們以串口為例。

串口作為一個標準的設(shè)備,把共性的分離出來,就成了uart層,特性成了serial層。

主要是serial層作為一個驅(qū)動模塊加載。以8250.c為例:

static int __init serial8250_init(void)

{

………

serial8250_reg.nr= UART_NR;

ret= uart_register_driver(&serial8250_reg);

………

serial8250_register_ports(&serial8250_reg,&serial8250_isa_devs->dev);

………

#define UART_NR CONFIG_SERIAL_8250_NR_UARTS

CONFIG_SERIAL_8250_NR_UARTS是在配置內(nèi)核的時候定義的,表示支持串口的個數(shù)。

static struct uart_driver serial8250_reg = {

。owner =THIS_MODULE,

。driver_name ="serial",

。dev_name ="ttyS",

。major =TTY_MAJOR,

。minor =64,

。cons =SERIAL8250_CONSOLE,

};

在驅(qū)動層里有幾個重要的數(shù)據(jù)結(jié)構(gòu):

structuart_driver;

structuart_state ;

structuart_port;

structtty_driver;

structtty_port;

實際上,理清了這幾個結(jié)構(gòu)體的關(guān)系,也就理清了TTY驅(qū)動層。

uart_register_driver:

這個函數(shù)主要是向TTY核心層注冊一個TTY驅(qū)動:

retval= tty_register_driver(normal);

其中normal是tty_driver.

另外,還會對tty_driver和uart_driver之間進行某些賦值和指針連接。我們最關(guān)心的是,給tty_driver初始化了操作函數(shù)uart_ops,這樣,在tty核心層就可以通過uart_ops來對UART層進行操作。

serial8250_register_ports:

最重要的兩個函數(shù):serial8250_isa_init_ports和uart_add_one_port

serial8250_isa_init_ports主要的工作是初始化uart_8250_port:開啟定時器和初始化uart_port.

uart_add_one_port顧名思議,就是為uart_driver增加一個端口,在uart_driver里的state指向NR個slot, 然后,這個函數(shù)的主要工作就是為slot增加一個port.這樣,uart_driver就可以通過port對ops操作函數(shù)集進行最底層的操作。

現(xiàn)在來分析下連接部分,也就是tty_driver如何工作,如何連接tty核心層(或者ldisc層)和串口層uart_port.關(guān)于操作部分主要是uart_ops.

uart_open:

staticint uart_open(struct tty_struct *tty, struct file *filp)

{

………

retval= uart_startup(tty, state, 0);

……

}

staticint uart_startup(struct tty_struct *tty, struct uart_state *state,int init_hw)

{

……

retval= uport->ops->startup(uport);

………

調(diào)用了uart_port的操作函數(shù)ops的startup,在這個函數(shù)里作了一些串口初始化的工作,其中有申請接收數(shù)據(jù)中斷或建立超時輪詢處理。

在startup里面申請了接收數(shù)據(jù)中斷,那么這個中斷服務(wù)程序就跟讀操作密切相關(guān)了,從tty核心層的讀操作可知,接收到的數(shù)據(jù)一定是傳送到read_buf中的?,F(xiàn)在來看是中斷服務(wù)程序。

調(diào)用receive_chars來接收數(shù)據(jù),在receive_chars中,出現(xiàn)了兩個傳輸數(shù)據(jù)的函數(shù):

tty_insert_flip_char和tty_flip_buffer_push.

static inline int tty_insert_flip_char(struct tty_struct *tty,

unsigned char ch, char flag)

{

struct tty_buffer *tb = tty->buf.tail;

if(tb && tb->used < tb->size) {

tb->flag_buf_ptr[tb->used]= flag;

tb->char_buf_ptr[tb->used++]= ch;

return1;

}

return tty_insert_flip_string_flags(tty, &ch, &flag, 1);

}

當當前的tty_buffer空間不夠時調(diào)用tty_insert_flip_string_flags,在這個函數(shù)里會去查找下一個tty_buffer,并將數(shù)據(jù)放到下一個tty_buffer的char_buf_ptr里。

那么char_buf_ptr的數(shù)據(jù)怎樣與線路規(guī)程中的read_buf關(guān)聯(lián)的呢,我們看,在初始化tty_buffer的時候,也就是在tty_buffer_init函數(shù)中:

void tty_buffer_init(struct tty_struct *tty)

{

spin_lock_init(&tty->buf.lock);

tty->buf.head= NULL;

tty->buf.tail= NULL;

tty->buf.free= NULL;

tty->buf.memory_used= 0;

INIT_DELAYED_WORK(&tty->buf.work,flush_to_ldisc);

}

在函數(shù)的最后,初始化了一個工作隊列。

而這個隊列在什么時候調(diào)度呢,在驅(qū)動層里receive_chars的最后調(diào)用了tty_flip_buffer_push這個函數(shù)。

void tty_flip_buffer_push(struct tty_struct *tty)

{

unsigned long flags;spin_lock_irqsave(&tty->buf.lock, flags);if (tty->buf.tail != NULL)

tty->buf.tail->commit = tty->buf.tail->used;spin_unlock_irqrestore(&tty->buf.lock, flags);

if (tty->low_latency)

flush_to_ldisc(&tty->buf.work.work);else schedule_delayed_work(&tty->buf.work, 1);

}

那么,在push數(shù)據(jù)到tty_buffer的時候有兩種方式,一種是flush_to_ldisc,另一種就是調(diào)度tty緩沖區(qū)的工作隊列。

flush_to_ldisc是隊列調(diào)用的函數(shù):

static void flush_to_ldisc(struct work_struct *work)[!--empirenews.page--]

{

……

while((head = tty->buf.head) != NULL) {

………

count= head->commit – head->read;

………

char_buf= head->char_buf_ptr + head->read;

flag_buf= head->flag_buf_ptr + head->read;

head->read+= count;

disc->ops->receive_buf(tty,char_buf,

flag_buf,count);

………

}

……

}

這個函數(shù)主要的功能是,從tty_buffer中找到數(shù)據(jù)緩沖區(qū)char_buf_ptr,并將這個緩沖區(qū)指針傳遞給線路規(guī)程的操作函數(shù)receive_buf.再來看receive_buf:

static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char*cp,

char *fp, int count)

{

……

if(tty->real_raw) {

………

memcpy(tty->read_buf+ tty->read_head, cp, i);

………

}else{

………

switch(flags) {

caseTTY_NORMAL:

n_tty_receive_char(tty,*p);

break;

……

}

if(tty->ops->flush_chars)

tty->ops->flush_chars(tty);

………

}

………

}

從上面這段代碼可以看到,if條件成立,明顯地是拷貝數(shù)據(jù)進tty的read_buf;進入else,在正常的狀態(tài)下會調(diào)用n_tty_receive_char,然后會調(diào)用put_tty_queue,在這個函數(shù)里最終還是把數(shù)據(jù)拷貝到tty的read_buf中。

到此,tty驅(qū)動的讀操作數(shù)據(jù)鏈路基本上連通了。

uart_write:

static int uart_write(struct tty_struct *tty,

const unsigned char *buf, int count)

{

……

port= state->uart_port;

circ= &state->xmit;

……

while(1){

c= CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);

………

memcpy(circ->buf+ circ->head, buf, c);

………

}

……

uart_start(tty);

return ret;

}

上面代碼的意思是把要寫的數(shù)據(jù)拷貝到state的緩沖區(qū)里。然后調(diào)用uart_start.

static void __uart_start(struct tty_struct *tty)

{

struct uart_state *state = tty->driver_data;

struct uart_port *port = state->uart_port;

if(!uart_circ_empty(&state->xmit) && state->xmit.buf&&

!tty->stopped && !tty->hw_stopped)

port->ops->start_tx(port);

}

調(diào)用了uart_port的操作函數(shù)集的start_tx.

static void serial8250_start_tx(struct uart_port *port)

{

struct uart_8250_port *up = container_of(port, struct uart_8250_port, port);

……

transmit_chars(up);

………

}

在transmit_chars中會把state->xmit緩沖區(qū)的數(shù)據(jù)寫進串口發(fā)送數(shù)據(jù)寄存器,也就是數(shù)據(jù)到達硬件層。到此,寫操作的數(shù)據(jù)鏈路也連通。

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉