0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

tty驅(qū)動接口分析

嵌入式與Linux那些事 ? 來源:嵌入式與Linux那些事 ? 2023-07-14 12:21 ? 次閱讀

前言

tty這個名稱源于電傳打字節(jié)的簡稱,在linux表示各種終端,終端通常都跟硬件相對應(yīng)。比如對應(yīng)于輸入設(shè)備鍵盤鼠標,輸出設(shè)備顯示器的控制終端和串口終端。也有對應(yīng)于不存在設(shè)備的pty驅(qū)動。在如此眾多的終端模型之中,linux是怎么將它們統(tǒng)一建模的呢?這就是我們今天要討論的問題。

tty驅(qū)動概貌

tty架構(gòu)如下所示:

08cc06bc-21fc-11ee-962d-dac502259ad0.png

如上圖所示,用戶空間主要是通過系統(tǒng)調(diào)用與tty core交互。tty core根據(jù)用空間操作的類型再選擇跟line discipline和tty driver交互。

例如,設(shè)置硬件的ioctl指令就直接交給tty_driver處理。read和write操作就會交給 line discipline處理。

Line discipline是線路規(guī)程的意思。正如它的名字一樣,它表示的是這條終端”線程”的輸入與輸出規(guī)范設(shè)置。主要用來進行輸入/輸出數(shù)據(jù)的預(yù)處理。

處理之后,就會將數(shù)據(jù)交給tty driver ,它將字符轉(zhuǎn)換成終端可以理解的字串。將其傳給終端設(shè)備。

值得注意的是,這個架構(gòu)沒有為tty driver 提供read操作。也就是說tty core 和line discipline都沒有辦法從tty driver里直接讀終端信息。這是因為tty driver對應(yīng)的hardware并不一定是輸入數(shù)據(jù)和輸出 數(shù)據(jù)的共同負載者。

例如控制終端,輸出設(shè)備是顯示器,輸入設(shè)備是鍵盤?;谶@樣的原理。在line discipline中有一個輸入緩存區(qū),并提供了一個名叫receive_buf()的接口函數(shù)。對應(yīng)的終端設(shè)備只要調(diào)用line discipine的receiver_buf函數(shù),將數(shù)據(jù)寫入到輸入緩存區(qū)就可以了。如果一個設(shè)備同時是輸入設(shè)備又是輸出設(shè)備。那在設(shè)備的中斷處理中調(diào)用receive_buf()將數(shù)據(jù)寫入即可.

tty驅(qū)動接口分析

tty_init()

/*
*Ok,nowwecaninitializetherestofthettydevicesandcancount
*onmemoryallocations,interruptsetc..
*/
int__inittty_init(void)
{
tty_sysctl_init();
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")

tty_init主要做了以下工作:

初始化 tty 子系統(tǒng)的 sysctl 相關(guān)設(shè)置,包括注冊 sysctl 參數(shù)、創(chuàng)建 sysctl 目錄等。

初始化 tty 設(shè)備的字符設(shè)備對象,并將其與 tty 設(shè)備操作函數(shù) tty_fops 綁定。同時,創(chuàng)建一個名為 "tty" 的 tty 設(shè)備節(jié)點,并將其設(shè)備號設(shè)置為 MKDEV(TTYAUX_MAJOR, 0)。

初始化控制臺設(shè)備的字符設(shè)備對象,并將其添加到字符設(shè)備系統(tǒng)中。同時,創(chuàng)建一個名為 "console" 的控制臺設(shè)備節(jié)點,并將其設(shè)備號設(shè)置為 MKDEV(TTYAUX_MAJOR, 1)。該控制臺設(shè)備節(jié)點還將在 sysfs 中創(chuàng)建一個名為 "console" 的目錄,并在該目錄下創(chuàng)建多個屬性文件,用于控制控制臺的一些屬性。

如果內(nèi)核支持虛擬終端,則初始化虛擬終端。

這里我們看到了熟悉的cdev_init(),device_create()之類的函數(shù),這正是字符設(shè)備的創(chuàng)建流程。因此,我們說串口驅(qū)動也是一個字符設(shè)備驅(qū)動。

而在serial8250_init()中,會調(diào)用platform_driver_register()去注冊serial8250_isa_driver,在設(shè)備樹節(jié)點和serial8250_isa_driver name匹配的時候,就會進入probe流程。因此,也可以說串口驅(qū)動是總線設(shè)備驅(qū)動模型。

tty_alloc_driver

/*UseTTY_DRIVER_*flagsbelow*/
#definetty_alloc_driver(lines,flags)
__tty_alloc_driver(lines,THIS_MODULE,flags)

__tty_alloc_driver()用于分配一個 tty 驅(qū)動程序的數(shù)據(jù)結(jié)構(gòu) struct tty_driver,并對其一些常用字段進行初始化。

/**
*__tty_alloc_driver--allocatettydriver
*@lines:countoflinesthisdrivercanhandleatmost
*@owner:modulewhichisrepsonsibleforthisdriver
*@flags:someofTTY_DRIVER_*flags,willbesetindriver->flags
*
*Thisshouldnotbecalleddirectly,someoftheprovidedmacrosshouldbe
*usedinstead.UseIS_ERRandfriendson@retval.
*/
structtty_driver*__tty_alloc_driver(unsignedintlines,structmodule*owner,
unsignedlongflags)
{
structtty_driver*driver;
unsignedintcdevs=1;
interr;

if(!lines||(flags&TTY_DRIVER_UNNUMBERED_NODE&&lines>1))
returnERR_PTR(-EINVAL);

/*分配一個structtty_driver結(jié)構(gòu)體,并對其中的一些字段進行初始化,包括num、owner、flags等*/
driver=kzalloc(sizeof(structtty_driver),GFP_KERNEL);
if(!driver)
returnERR_PTR(-ENOMEM);

kref_init(&driver->kref);
driver->magic=TTY_DRIVER_MAGIC;
driver->num=lines;
driver->owner=owner;
driver->flags=flags;

/*如果TTY_DRIVER_DEVPTS_MEM標志位沒有被設(shè)置,那么函數(shù)會分配driver->ttys和driver->termios,否則不需要分配*/
if(!(flags&TTY_DRIVER_DEVPTS_MEM)){
driver->ttys=kcalloc(lines,sizeof(*driver->ttys),
GFP_KERNEL);
driver->termios=kcalloc(lines,sizeof(*driver->termios),
GFP_KERNEL);
if(!driver->ttys||!driver->termios){
err=-ENOMEM;
gotoerr_free_all;
}
}

/*如果TTY_DRIVER_DYNAMIC_ALLOC標志位沒有被設(shè)置,那么函數(shù)會分配driver->ports,否則不需要分配*/
if(!(flags&TTY_DRIVER_DYNAMIC_ALLOC)){
driver->ports=kcalloc(lines,sizeof(*driver->ports),
GFP_KERNEL);
if(!driver->ports){
err=-ENOMEM;
gotoerr_free_all;
}
cdevs=lines;
}

/*函數(shù)會根據(jù)lines的值分配相應(yīng)數(shù)量的driver->cdevs*/
driver->cdevs=kcalloc(cdevs,sizeof(*driver->cdevs),GFP_KERNEL);
if(!driver->cdevs){
err=-ENOMEM;
gotoerr_free_all;
}

returndriver;
err_free_all:
kfree(driver->ports);
kfree(driver->ttys);
kfree(driver->termios);
kfree(driver->cdevs);
kfree(driver);
returnERR_PTR(err);
}

tty_register_driver

tty_register_driver用于注冊 tty 驅(qū)動程序的,被 tty 驅(qū)動程序調(diào)用以將自己注冊到內(nèi)核中。

/*
*Calledbyattydrivertoregisteritself.
*/
inttty_register_driver(structtty_driver*driver)
{
interror;
inti;
dev_tdev;
structdevice*d;

/*確認是否要內(nèi)核動態(tài)分配主設(shè)備號*/
if(!driver->major){
/*函數(shù)調(diào)用alloc_chrdev_region函數(shù)來動態(tài)分配主設(shè)備號,并將分配的主設(shè)備號和次設(shè)備號保存在driver->major和driver->minor_start字段中*/
error=alloc_chrdev_region(&dev,driver->minor_start,
driver->num,driver->name);
if(!error){
driver->major=MAJOR(dev);
driver->minor_start=MINOR(dev);
}
}else{
/*已經(jīng)預(yù)先分配了主設(shè)備號,函數(shù)調(diào)用register_chrdev_region函數(shù)來注冊設(shè)備號*/
dev=MKDEV(driver->major,driver->minor_start);
error=register_chrdev_region(dev,driver->num,driver->name);
}
if(errorflags&TTY_DRIVER_DYNAMIC_ALLOC){
/*需要動態(tài)分配tty設(shè)備號,函數(shù)調(diào)用tty_cdev_add函數(shù)來添加tty設(shè)備號,并將每個tty設(shè)備的字符設(shè)備注冊到內(nèi)核中*/
error=tty_cdev_add(driver,dev,0,driver->num);
if(error)
gotoerr_unreg_char;
}

mutex_lock(&tty_mutex);
/*將driver添加到鏈表tty_drivers中*/
list_add(&driver->tty_drivers,&tty_drivers);
mutex_unlock(&tty_mutex);

/*判斷TTY_DRIVER_DYNAMIC_DEV標志位是否設(shè)置*/
if(!(driver->flags&TTY_DRIVER_DYNAMIC_DEV)){
for(i=0;inum;i++){
/*需要注冊固定的tty設(shè)備號,函數(shù)在循環(huán)中調(diào)用tty_register_device函數(shù)來注冊每個tty設(shè)備號,并將每個tty設(shè)備注冊到內(nèi)核中*/
d=tty_register_device(driver,i,NULL);
if(IS_ERR(d)){
error=PTR_ERR(d);
gotoerr_unreg_devs;
}
}
}
/*注冊/proc/tty/drivers目錄中的信息*/
proc_tty_register_driver(driver);
/*將driver結(jié)構(gòu)體中的flags字段設(shè)置為TTY_DRIVER_INSTALLED,表示該驅(qū)動程序已經(jīng)被成功注冊到內(nèi)核中*/
driver->flags|=TTY_DRIVER_INSTALLED;
return0;

err_unreg_devs:
for(i--;i>=0;i--)
tty_unregister_device(driver,i);

mutex_lock(&tty_mutex);
list_del(&driver->tty_drivers);
mutex_unlock(&tty_mutex);

err_unreg_char:
unregister_chrdev_region(dev,driver->num);
err:
returnerror;
}

tty_register_driver()函數(shù)操作比較簡單。就是為tty_driver創(chuàng)建字符設(shè)備。然后將字符設(shè)備的操作集指定為tty_fops。并且將tty_driver 掛載到tty_drivers鏈表中。這個鏈表中是以設(shè)備號為關(guān)鍵字找到對應(yīng)的driver。

特別的。如果沒有定義TTY_DRIVER_DYNAMIC_DEV。還會在sysfs中創(chuàng)建一個類設(shè)備。這樣主要是為了udev管理設(shè)備。

tty_unregister_device

tty_unregister_device用于注銷一個 tty 設(shè)備。該函數(shù)的作用是銷毀設(shè)備節(jié)點和字符設(shè)備,以便于釋放與該 tty 設(shè)備相關(guān)的資源,例如內(nèi)存和設(shè)備文件等.

/**
*tty_unregister_device-unregisterattydevice
*@driver:thettydriverthatdescribesthettydevice
*@index:theindexinthettydriverforthisttydevice
*
*Ifattydeviceisregisteredwithacalltotty_register_device()then
*thisfunctionmustbecalledwhenthettydeviceisgone.
*
*Locking:??
*/

voidtty_unregister_device(structtty_driver*driver,unsignedindex)
{
device_destroy(tty_class,
MKDEV(driver->major,driver->minor_start)+index);
if(!(driver->flags&TTY_DRIVER_DYNAMIC_ALLOC)){
cdev_del(driver->cdevs[index]);
driver->cdevs[index]=NULL;
}
}

tty_unregister_device所做工作如下:

調(diào)用 device_destroy 函數(shù)來銷毀 tty 設(shè)備對應(yīng)的設(shè)備節(jié)點。接受兩個參數(shù):第一個參數(shù) tty_class 表示 tty 類,第二個參數(shù)是 tty 設(shè)備的設(shè)備號,其中 MKDEV(driver->major, driver->minor_start) + index 表示 tty 設(shè)備的設(shè)備號,driver->major 表示 tty 設(shè)備的主設(shè)備號,driver->minor_start 表示 tty 設(shè)備的次設(shè)備號的起始值,index 表示 tty 設(shè)備的索引

如果該 tty 驅(qū)動程序不是動態(tài)分配的,則調(diào)用 cdev_del 函數(shù)來注銷該 tty 設(shè)備對應(yīng)的字符設(shè)備。

get_tty_driver

get_tty_driver作用是在用戶空間的應(yīng)用程序使用 tty 設(shè)備時,獲取對應(yīng)的 tty 驅(qū)動程序的信息。

/**
*get_tty_driver-finddeviceofatty
*@dev_t:deviceidentifier
*@index:returnstheindexofthetty
*
*Thisroutinereturnsattydriverstructure,givenadevicenumber
*andalsopassesbacktheindexnumber.
*
*Locking:callermustholdtty_mutex
*/

staticstructtty_driver*get_tty_driver(dev_tdevice,int*index)
{
structtty_driver*p;

/**/
list_for_each_entry(p,&tty_drivers,tty_drivers){
dev_tbase=MKDEV(p->major,p->minor_start);
if(device=base+p->num)
continue;
*index=device-base;
returntty_driver_kref_get(p);
}
returnNULL;
}

首先使用 list_for_each_entry 循環(huán)遍歷全局鏈表 tty_drivers,該鏈表中保存了所有已經(jīng)注冊的 tty 驅(qū)動程序。對于每個 tty 驅(qū)動程序,函數(shù)將其設(shè)備號的起始值和結(jié)束值計算出來,如果給定設(shè)備號不在這個范圍內(nèi),則繼續(xù)遍歷下一個 tty 驅(qū)動程序。

如果給定設(shè)備號在某個 tty 驅(qū)動程序的范圍內(nèi),則計算出該設(shè)備號對應(yīng)的 tty 設(shè)備的索引值,并調(diào)用 tty_driver_kref_get 函數(shù)來獲取該 tty 驅(qū)動程序的引用計數(shù)。函數(shù)返回該 tty 驅(qū)動程序的結(jié)構(gòu)體指針,并將找到的 tty 設(shè)備的索引值保存到 index 參數(shù)中。

需要注意的是,函數(shù)在訪問全局鏈表 tty_drivers 時,需要持有互斥鎖 tty_mutex。因為多個應(yīng)用程序可能同時訪問同一個 tty 驅(qū)動程序,如果沒有互斥鎖保護,可能會導(dǎo)致并發(fā)問題。

tty_open

從注冊的過程可以看到,所有的操作都會對應(yīng)到tty_fops中。Open操作對應(yīng)的操作接口是tty_open(),用于打開一個 tty 設(shè)備。函數(shù)的作用是在用戶空間的應(yīng)用程序使用 tty 設(shè)備時,打開對應(yīng)的 tty 設(shè)備,并初始化相應(yīng)的數(shù)據(jù)結(jié)構(gòu)。

/**
*tty_open-openattydevice
*@inode:inodeofdevicefile
*@filp:filepointertotty
*
*tty_openandtty_releasekeepupthettycountthatcontainsthe
*numberofopensdoneonatty.Wecannotusetheinode-count,as
*differentinodesmightpointtothesametty.
*
*Open-countingisneededforptymasters,aswellasforkeeping
*trackofseriallines:DTRisdroppedwhenthelastclosehappens.
*(Thisisnotdonesolelythroughtty->count,now.-Ted1/27/92)
*
*Thetermiosstateofaptyisresetonfirstopensothat
*settingsdon'tpersistacrossreuse.
*
*Locking:tty_mutexprotectstty,tty_lookup_driverandtty_init_dev.
*tty->countshouldprotecttherest.
*->siglockprotects->signal/->sighand
*
*Note:thetty_unlock/lockcaseswithoutarefareonlysafedueto
*tty_mutex
*/

staticinttty_open(structinode*inode,structfile*filp)
{
structtty_struct*tty;
intnoctty,retval;
structtty_driver*driver=NULL;
intindex;
dev_tdevice=inode->i_rdev;
unsignedsaved_flags=filp->f_flags;

nonseekable_open(inode,filp);

retry_open:
/*分配一個tty結(jié)構(gòu)體*/
retval=tty_alloc_file(filp);
if(retval)
return-ENOMEM;

/*檢查文件的標志位,如果包含O_NOCTTY標志,則禁止將該tty設(shè)備設(shè)置為控制終端*/
noctty=filp->f_flags&O_NOCTTY;
index=-1;
retval=0;
/*嘗試打開當(dāng)前的tty設(shè)備*/
tty=tty_open_current_tty(device,filp);
if(!tty){
mutex_lock(&tty_mutex);
/*根據(jù)設(shè)備號來查找對應(yīng)的tty驅(qū)動程序,并初始化該tty設(shè)備,將找到的tty驅(qū)動程序保存到driver變量中*/
driver=tty_lookup_driver(device,filp,&noctty,&index);
if(IS_ERR(driver)){
retval=PTR_ERR(driver);
gotoerr_unlock;
}

/*checkwhetherwe'rereopeninganexistingtty*/
/*查找對應(yīng)的tty設(shè)備,并將找到的tty設(shè)備結(jié)構(gòu)體指針保存到tty變量中*/
tty=tty_driver_lookup_tty(driver,inode,index);
if(IS_ERR(tty)){
retval=PTR_ERR(tty);
gotoerr_unlock;
}

if(tty){
/*如果找到了該tty設(shè)備,則需要重新打開該tty設(shè)備*/
mutex_unlock(&tty_mutex);
retval=tty_lock_interruptible(tty);
tty_kref_put(tty);/*dropkreffromtty_driver_lookup_tty()*/
if(retval){
if(retval==-EINTR)
retval=-ERESTARTSYS;
gotoerr_unref;
}
retval=tty_reopen(tty);
if(retvaldriver->type==TTY_DRIVER_TYPE_PTY&&
tty->driver->subtype==PTY_TYPE_MASTER)
noctty=1;

tty_debug_hangup(tty,"(ttycount=%d)
",tty->count);

/*調(diào)用tty設(shè)備的open函數(shù)*/
if(tty->ops->open)
retval=tty->ops->open(tty,filp);
else
retval=-ENODEV;
filp->f_flags=saved_flags;

if(retval){
tty_debug_hangup(tty,"error%d,releasing...
",retval);

tty_unlock(tty);/*needtocalltty_releasewithoutBTM*/
tty_release(inode,filp);
if(retval!=-ERESTARTSYS)
returnretval;

if(signal_pending(current))
returnretval;

schedule();
/*
*Needtoresetf_opincaseahanguphappened.
*/
if(tty_hung_up_p(filp))
filp->f_op=&tty_fops;
gotoretry_open;
}
clear_bit(TTY_HUPPED,&tty->flags);


read_lock(&tasklist_lock);
spin_lock_irq(¤t->sighand->siglock);
if(!noctty&&
current->signal->leader&&
!current->signal->tty&&
tty->session==NULL){
/*
*Don'tletaprocessthatonlyhaswriteaccesstothetty
*obtaintheprivilegesassociatedwithhavingattyas
*controllingterminal(beingabletoreopenitwithfull
*accessthrough/dev/tty,beingabletoperformpushback).
*Manydistributionssetthegroupofallttysto"tty"and
*grantwrite-onlyaccesstoallterminalsforsetgidtty
*binaries,whichshouldnotimplyfullprivilegesonallttys.
*
*Thiscouldtheoreticallybreakoldcodethatperformsopen()
*onawrite-onlyfiledescriptor.Inthatcase,itmightbe
*necessarytoalsopermitthisif
*inode_permission(inode,MAY_READ)==0.
*/
if(filp->f_mode&FMODE_READ)
__proc_set_tty(tty);
}
spin_unlock_irq(¤t->sighand->siglock);
read_unlock(&tasklist_lock);
tty_unlock(tty);
return0;
err_unlock:
mutex_unlock(&tty_mutex);
err_unref:
/*afterlockstoavoiddeadlock*/
if(!IS_ERR_OR_NULL(driver))
tty_driver_kref_put(driver);
err_file:
tty_free_file(filp);
returnretval;
}

函數(shù)所作工作如下:

在打開 tty 設(shè)備時,該函數(shù)會檢查文件的標志位,如果包含 O_NOCTTY 標志,則禁止將該 tty 設(shè)備設(shè)置為控制終端。這是因為如果一個進程打開一個 tty 設(shè)備并將其設(shè)置為控制終端,其他進程就無法再將該 tty 設(shè)備設(shè)置為控制終端,這可能會導(dǎo)致一些問題。

如果打開當(dāng)前的 tty 設(shè)備失敗,則需要根據(jù)設(shè)備號來查找對應(yīng)的 tty 驅(qū)動程序,并初始化該 tty 設(shè)備。在查找 tty 驅(qū)動程序時,需要調(diào)用 tty_lookup_driver 函數(shù)來查找對應(yīng)的 tty 驅(qū)動程序,并將找到的 tty 驅(qū)動程序保存到 driver 變量中。如果找不到對應(yīng)的 tty 驅(qū)動程序,則返回錯誤碼。

如果找到了對應(yīng)的 tty 驅(qū)動程序,則調(diào)用 tty_driver_lookup_tty 函數(shù)來查找對應(yīng)的 tty 設(shè)備,并將找到的 tty 設(shè)備結(jié)構(gòu)體指針保存到 tty 變量中。如果找到了該 tty 設(shè)備,則需要重新打開該 tty 設(shè)備。否則,需要初始化該 tty 設(shè)備。在初始化 tty 設(shè)備時,需要調(diào)用 tty_init_dev 函數(shù)來為該 tty 設(shè)備分配一個 tty 結(jié)構(gòu)體,并對其進行初始化。

在打開 tty 設(shè)備之后,函數(shù)會調(diào)用 tty_add_file 函數(shù)將該 tty 設(shè)備與文件結(jié)構(gòu)體相關(guān)聯(lián)。此外,如果該 tty 設(shè)備是一個偽終端主設(shè)備,則需要將 noctty 標志設(shè)置為 1。

最后,函數(shù)會調(diào)用 tty 設(shè)備的 open 函數(shù),如果存在的話,來進行一些特定的操作。如果 open 函數(shù)返回錯誤碼,則需要釋放該 tty 設(shè)備并返回錯誤碼。如果 open 函數(shù)返回 -ERESTARTSYS,則需要重新打開該 tty 設(shè)備。如果有中斷發(fā)生,也需要重新打開該 tty 設(shè)備。

tty_write

tty_write()作用是將用戶數(shù)據(jù)寫入 tty 設(shè)備,并通過線路規(guī)則(line discipline)進行處理。

線路規(guī)則是 tty 設(shè)備的一種機制,用于處理和轉(zhuǎn)換從用戶進程到內(nèi)核和設(shè)備的數(shù)據(jù)流。在寫入 tty 設(shè)備之前,需要獲取該 tty 設(shè)備的線路規(guī)則,并調(diào)用其 write 方法進行處理。

/**
*tty_write-writemethodforttydevicefile
*@file:ttyfilepointer
*@buf:userdatatowrite
*@count:bytestowrite
*@ppos:unused
*
*Writedatatoattydeviceviathelinediscipline.
*
*Locking:
*Locksthelinedisciplineasrequired
*Writestothettydriverareserializedbytheatomic_write_lock
*andarethenprocessedinchunkstothedevice.Thelinediscipline
*writemethodwillnotbeinvokedinparallelforeachdevice.
*/

staticssize_ttty_write(structfile*file,constchar__user*buf,
size_tcount,loff_t*ppos)
{
structtty_struct*tty=file_tty(file);
structtty_ldisc*ld;
ssize_tret;

if(tty_paranoia_check(tty,file_inode(file),"tty_write"))
return-EIO;
if(!tty||!tty->ops->write||
(test_bit(TTY_IO_ERROR,&tty->flags)))
return-EIO;
/*Shorttermdebugtocatchbuggydrivers*/
if(tty->ops->write_room==NULL)
printk(KERN_ERR"ttydriver%slacksawrite_roommethod.
",
tty->driver->name);
ld=tty_ldisc_ref_wait(tty);
if(!ld->ops->write)
ret=-EIO;
else
ret=do_tty_write(ld->ops->write,tty,file,buf,count);
tty_ldisc_deref(ld);
returnret;
}

tty_write()所作工作如下:

首先從文件指針中獲取 tty_struct 數(shù)據(jù)結(jié)構(gòu)的指針,表示要寫入的 tty 設(shè)備。

檢查傳入的 tty_struct 指針是否有效,以及是否有其他進程正在訪問該 tty 設(shè)備。如果出現(xiàn)問題,返回輸入/輸出錯誤碼 -EIO。

檢查 tty_struct 指針是否有效、tty 設(shè)備是否支持寫操作,以及是否已經(jīng)出現(xiàn)了輸入/輸出錯誤。如果出現(xiàn)問題,返回輸入/輸出錯誤碼 -EIO。

檢查 tty 設(shè)備是否實現(xiàn)了 write_room 方法,如果沒有,則輸出錯誤信息。

獲取 tty 設(shè)備的線路規(guī)則(line discipline),并等待獲取成功。

檢查線路規(guī)則的 write 方法是否存在,如果不存在,返回輸入/輸出錯誤碼 -EIO。否則,調(diào)用 do_tty_write 函數(shù),將數(shù)據(jù)寫入 tty 設(shè)備。

釋放線路規(guī)則引用計數(shù)器。

返回寫入操作的結(jié)果,如果寫入成功,則返回寫入的字節(jié)數(shù);否則,返回相應(yīng)的錯誤碼。

tty_read

/**
*tty_read-readmethodforttydevicefiles
*@file:pointertottyfile
*@buf:userbuffer
*@count:sizeofuserbuffer
*@ppos:unused
*
*Performthereadsystemcallfunctiononthisterminaldevice.Checks
*forhungupdevicesbeforecallingthelinedisciplinemethod.
*
*Locking:
*Locksthelinedisciplineinternallywhileneeded.Multiple
*readcallsmaybeoutstandinginparallel.
*/

staticssize_ttty_read(structfile*file,char__user*buf,size_tcount,
loff_t*ppos)
{
inti;
structinode*inode=file_inode(file);
structtty_struct*tty=file_tty(file);
structtty_ldisc*ld;

if(tty_paranoia_check(tty,inode,"tty_read"))
return-EIO;
if(!tty||(test_bit(TTY_IO_ERROR,&tty->flags)))
return-EIO;

/*Wewanttowaitforthelinedisciplinetosortoutinthis
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)
tty_update_time(&inode->i_atime);

returni;
}

tty_read()實現(xiàn)終端設(shè)備文件讀操作的函數(shù) 。

獲取 tty_struct 結(jié)構(gòu)體、inode 和 line discipline 對象的指針。

調(diào)用 tty_paranoia_check() 函數(shù)檢查 tty_struct 結(jié)構(gòu)體是否可用。如果檢查失敗,返回 -EIO。

檢查 tty_struct 結(jié)構(gòu)體是否為空或者 TTY_IO_ERROR 標志位已經(jīng)設(shè)置。如果是,則返回 -EIO。

獲取 line discipline 對象的引用,確保它不會在 tty_read() 函數(shù)執(zhí)行期間被卸載。

檢查 line discipline 的 read() 方法是否可用。如果可用,則調(diào)用該方法進行讀取操作,并將返回的字節(jié)數(shù)保存在變量 i 中。如果不可用,返回 -EIO。

釋放 line discipline 的引用。

如果讀取操作成功,調(diào)用 tty_update_time() 函數(shù)更新 inode 的訪問時間。

返回讀取的字節(jié)數(shù)。

小結(jié)

在這一節(jié)里,只對tty的構(gòu)造做一個分析,具體的比如線路規(guī)程的內(nèi)容我們了解知道就好,這里不做深入分析。

審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 顯示器
    +關(guān)注

    關(guān)注

    21

    文章

    4862

    瀏覽量

    139360
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11123

    瀏覽量

    207919
  • 串口
    +關(guān)注

    關(guān)注

    14

    文章

    1533

    瀏覽量

    75465
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4237

    瀏覽量

    61969

原文標題:【驅(qū)動】串口驅(qū)動分析(二)-tty core

文章出處:【微信號:嵌入式與Linux那些事,微信公眾號:嵌入式與Linux那些事】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    V-BY-ONE技術(shù)優(yōu)勢分析

    目前接口分析:1.TTL:局限性太大,一般在小屏上面應(yīng)用較多(7寸以下)2.7寸以上的屏現(xiàn)在的接口有LVDS,eDP,iDP,V-BY-ONE。附件講述一下由THine公司發(fā)布的V-BY-ONE接口的技術(shù)優(yōu)勢。需要詳細資料的請電
    發(fā)表于 03-14 18:24

    Android系統(tǒng)開發(fā)之藍牙開發(fā)案例分析

    案例,帶您了解真正的藍牙開發(fā)技術(shù)。主要涉及:藍牙應(yīng)用接口,藍牙Framework架構(gòu),藍牙設(shè)備驅(qū)動,藍牙開發(fā)技巧及數(shù)據(jù)處理等要點。1. Android的藍牙框架分析2. Android藍牙應(yīng)用開發(fā)
    發(fā)表于 07-24 16:32

    FastCV主要接口分析(第二部分)

    FastCV主要接口分析之二 FastCV為計算機視覺(CV,computer vision)的應(yīng)用開發(fā)者提供了兩個主要功能:提供CV常用的函數(shù)庫,其已經(jīng)進行了優(yōu)化且可以高效的運行在移動設(shè)備上;提供
    發(fā)表于 09-21 10:29

    FastCV主要接口分析

    FastCV主要接口分析之一 安裝Hexagon SDK后,會在安裝的主目錄下/Hexagon_SDK/2.0/lib/fastcv/hexagon_Release_v5存在如下文件: 上圖
    發(fā)表于 09-21 17:05

    淺析linux UART驅(qū)動tty架構(gòu)

    關(guān)于linux UART驅(qū)動tty架構(gòu)的理解
    發(fā)表于 07-03 09:55

    高清晰視頻會議終端應(yīng)用接口分析

    高清晰視頻會議終端應(yīng)用接口分析 DVI接口與HDMI接口介紹   視頻通訊作為
    發(fā)表于 02-21 10:10 ?2179次閱讀

    Xilinx FPGA DDR4接口應(yīng)用分析

    本內(nèi)容主要分析了基于FPGA的系統(tǒng)需求,賽靈思UltraScale FPGA DDR4和其他并行接口分析以及針對高性能高度靈活方案的PHY解決方案介紹。
    發(fā)表于 08-03 19:37 ?191次下載

    Linux中tty、pty、pts的概念區(qū)別

    /tty0的符號鏈接嗎?A: 目前的大多數(shù)文本中都稱/dev/console是到/dev/tty0的鏈接(包括《Linux內(nèi)核源代碼情景分析》),但是這樣說是不確切的。根據(jù)內(nèi)核文檔,在2.1.71之前
    發(fā)表于 04-02 14:36 ?1260次閱讀

    MAVLink學(xué)習(xí)之路05_ MAVLink應(yīng)用編程接口分析

    MAVLink學(xué)習(xí)之路05_MAVLink應(yīng)用編程接口分析
    的頭像 發(fā)表于 03-07 16:21 ?5501次閱讀

    常用接口分析,必須要全部掌握

    日常使用手機、電腦以及其他電子產(chǎn)品,免不了要跟各種接口打交道。周末花了些時間查了些資料,并總結(jié)自己的實際使用經(jīng)驗,跟大家聊一聊我們?nèi)粘J褂檬謾C、iPad、電腦、外設(shè)中常用到的接口。 ?講明白各種接口
    的頭像 發(fā)表于 10-30 09:57 ?431次閱讀

    TYPE C母座引腳接口分析及優(yōu)勢特點的介紹

    接口分析及優(yōu)勢特點。 TYPE C母座引腳接口分析 Type C母座一般簡稱有type c、type-c等,其實這些指的都
    發(fā)表于 12-18 10:59 ?1.5w次閱讀

    tty.js基于瀏覽器的終端模擬器

    ./oschina_soft/tty.js.zip
    發(fā)表于 05-26 14:54 ?1次下載
    <b class='flag-5'>tty</b>.js基于瀏覽器的終端模擬器

    SoC接口技術(shù)之低速接口分析(下)

    JTAG協(xié)議核心是三個寄存器和TAP狀態(tài)機。作為DEBUG的JTAG接口會在DAP中利用旁路寄存器將JTAG協(xié)議轉(zhuǎn)換為APB協(xié)議。作為DFT的JTAG接口會直接利用數(shù)據(jù)寄存器進行邊界掃描。
    的頭像 發(fā)表于 04-04 16:08 ?1588次閱讀
    SoC<b class='flag-5'>接口</b>技術(shù)之低速<b class='flag-5'>接口分析</b>(下)

    Linux中/dev/tty、/dev/tty0和/dev/console之間的區(qū)別在哪?

    在Linux系統(tǒng)中,/dev/tty、/dev/tty0和/dev/console是一些特殊的設(shè)備文件,經(jīng)常用于控制臺和命令行界面的操作。
    的頭像 發(fā)表于 06-21 09:06 ?3235次閱讀

    串口驅(qū)動分析之serial driver

    前兩節(jié)我們介紹串口驅(qū)動的框架和tty core部分。這節(jié)我們介紹和硬件緊密相關(guān)的串口驅(qū)動部分。
    的頭像 發(fā)表于 09-04 14:23 ?123次閱讀
    串口<b class='flag-5'>驅(qū)動</b><b class='flag-5'>分析</b>之serial driver