前言
tty這個名稱源于電傳打字節(jié)的簡稱,在linux表示各種終端,終端通常都跟硬件相對應。比如對應于輸入設備鍵盤鼠標,輸出設備顯示器的控制終端和串口終端。也有對應于不存在設備的pty驅(qū)動。在如此眾多的終端模型之中,linux是怎么將它們統(tǒng)一建模的呢?這就是我們今天要討論的問題。
tty驅(qū)動概貌
tty架構(gòu)如下所示:
如上圖所示,用戶空間主要是通過系統(tǒng)調(diào)用與tty core交互。tty core根據(jù)用空間操作的類型再選擇跟line discipline和tty driver交互。
例如,設置硬件的ioctl指令就直接交給tty_driver處理。read和write操作就會交給 line discipline處理。
Line discipline是線路規(guī)程的意思。正如它的名字一樣,它表示的是這條終端”線程”的輸入與輸出規(guī)范設置。主要用來進行輸入/輸出數(shù)據(jù)的預處理。
處理之后,就會將數(shù)據(jù)交給tty driver ,它將字符轉(zhuǎn)換成終端可以理解的字串。將其傳給終端設備。
值得注意的是,這個架構(gòu)沒有為tty driver 提供read操作。也就是說tty core 和line discipline都沒有辦法從tty driver里直接讀終端信息。這是因為tty driver對應的hardware并不一定是輸入數(shù)據(jù)和輸出 數(shù)據(jù)的共同負載者。
例如控制終端,輸出設備是顯示器,輸入設備是鍵盤?;谶@樣的原理。在line discipline中有一個輸入緩存區(qū),并提供了一個名叫receive_buf()的接口函數(shù)。對應的終端設備只要調(diào)用line discipine的receiver_buf函數(shù),將數(shù)據(jù)寫入到輸入緩存區(qū)就可以了。如果一個設備同時是輸入設備又是輸出設備。那在設備的中斷處理中調(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")0) ??panic("Couldn't?register?/dev/tty?driver "); ?device_create(tty_class,?NULL,?MKDEV(TTYAUX_MAJOR,?0),?NULL,?"tty"); ?cdev_init(&console_cdev,?&console_fops); ?if?(cdev_add(&console_cdev,?MKDEV(TTYAUX_MAJOR,?1),?1)?|| ?????register_chrdev_region(MKDEV(TTYAUX_MAJOR,?1),?1,?"/dev/console")?0) ??panic("Couldn't?register?/dev/console?driver "); ?consdev?=?device_create_with_groups(tty_class,?NULL, ?????????MKDEV(TTYAUX_MAJOR,?1),?NULL, ?????????cons_dev_groups,?"console"); ?if?(IS_ERR(consdev)) ??consdev?=?NULL; #ifdef?CONFIG_VT ?vty_init(&console_fops); #endif ?return?0; }
tty_init主要做了以下工作:
初始化 tty 子系統(tǒng)的 sysctl 相關(guān)設置,包括注冊 sysctl 參數(shù)、創(chuàng)建 sysctl 目錄等。
初始化 tty 設備的字符設備對象,并將其與 tty 設備操作函數(shù) tty_fops 綁定。同時,創(chuàng)建一個名為 "tty" 的 tty 設備節(jié)點,并將其設備號設置為 MKDEV(TTYAUX_MAJOR, 0)。
初始化控制臺設備的字符設備對象,并將其添加到字符設備系統(tǒng)中。同時,創(chuàng)建一個名為 "console" 的控制臺設備節(jié)點,并將其設備號設置為 MKDEV(TTYAUX_MAJOR, 1)。該控制臺設備節(jié)點還將在 sysfs 中創(chuàng)建一個名為 "console" 的目錄,并在該目錄下創(chuàng)建多個屬性文件,用于控制控制臺的一些屬性。
如果內(nèi)核支持虛擬終端,則初始化虛擬終端。
這里我們看到了熟悉的cdev_init(),device_create()之類的函數(shù),這正是字符設備的創(chuàng)建流程。因此,我們說串口驅(qū)動也是一個字符設備驅(qū)動。
而在serial8250_init()中,會調(diào)用platform_driver_register()去注冊serial8250_isa_driver,在設備樹節(jié)點和serial8250_isa_driver name匹配的時候,就會進入probe流程。因此,也可以說串口驅(qū)動是總線設備驅(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ù)會分配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ù)會分配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的值分配相應數(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)分配主設備號*/ if(!driver->major){ /*函數(shù)調(diào)用alloc_chrdev_region函數(shù)來動態(tài)分配主設備號,并將分配的主設備號和次設備號保存在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)預先分配了主設備號,函數(shù)調(diào)用register_chrdev_region函數(shù)來注冊設備號*/ dev=MKDEV(driver->major,driver->minor_start); error=register_chrdev_region(dev,driver->num,driver->name); } if(error0) ??goto?err; ?/*判斷是否設置了?TTY_DRIVER_DYNAMIC_ALLOC?標志位*/ ?if?(driver->flags&TTY_DRIVER_DYNAMIC_ALLOC){ /*需要動態(tài)分配tty設備號,函數(shù)調(diào)用tty_cdev_add函數(shù)來添加tty設備號,并將每個tty設備的字符設備注冊到內(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標志位是否設置*/ if(!(driver->flags&TTY_DRIVER_DYNAMIC_DEV)){ for(i=0;inum;i++){ /*需要注冊固定的tty設備號,函數(shù)在循環(huán)中調(diào)用tty_register_device函數(shù)來注冊每個tty設備號,并將每個tty設備注冊到內(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字段設置為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)建字符設備。然后將字符設備的操作集指定為tty_fops。并且將tty_driver 掛載到tty_drivers鏈表中。這個鏈表中是以設備號為關(guān)鍵字找到對應的driver。
特別的。如果沒有定義TTY_DRIVER_DYNAMIC_DEV。還會在sysfs中創(chuàng)建一個類設備。這樣主要是為了udev管理設備。
tty_unregister_device
tty_unregister_device用于注銷一個 tty 設備。該函數(shù)的作用是銷毀設備節(jié)點和字符設備,以便于釋放與該 tty 設備相關(guān)的資源,例如內(nèi)存和設備文件等.
/** *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 設備對應的設備節(jié)點。接受兩個參數(shù):第一個參數(shù) tty_class 表示 tty 類,第二個參數(shù)是 tty 設備的設備號,其中 MKDEV(driver->major, driver->minor_start) + index 表示 tty 設備的設備號,driver->major 表示 tty 設備的主設備號,driver->minor_start 表示 tty 設備的次設備號的起始值,index 表示 tty 設備的索引
如果該 tty 驅(qū)動程序不是動態(tài)分配的,則調(diào)用 cdev_del 函數(shù)來注銷該 tty 設備對應的字符設備。
get_tty_driver
get_tty_driver作用是在用戶空間的應用程序使用 tty 設備時,獲取對應的 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ù)將其設備號的起始值和結(jié)束值計算出來,如果給定設備號不在這個范圍內(nèi),則繼續(xù)遍歷下一個 tty 驅(qū)動程序。
如果給定設備號在某個 tty 驅(qū)動程序的范圍內(nèi),則計算出該設備號對應的 tty 設備的索引值,并調(diào)用 tty_driver_kref_get 函數(shù)來獲取該 tty 驅(qū)動程序的引用計數(shù)。函數(shù)返回該 tty 驅(qū)動程序的結(jié)構(gòu)體指針,并將找到的 tty 設備的索引值保存到 index 參數(shù)中。
需要注意的是,函數(shù)在訪問全局鏈表 tty_drivers 時,需要持有互斥鎖 tty_mutex。因為多個應用程序可能同時訪問同一個 tty 驅(qū)動程序,如果沒有互斥鎖保護,可能會導致并發(fā)問題。
tty_open
從注冊的過程可以看到,所有的操作都會對應到tty_fops中。Open操作對應的操作接口是tty_open(),用于打開一個 tty 設備。函數(shù)的作用是在用戶空間的應用程序使用 tty 設備時,打開對應的 tty 設備,并初始化相應的數(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設備設置為控制終端*/ noctty=filp->f_flags&O_NOCTTY; index=-1; retval=0; /*嘗試打開當前的tty設備*/ tty=tty_open_current_tty(device,filp); if(!tty){ mutex_lock(&tty_mutex); /*根據(jù)設備號來查找對應的tty驅(qū)動程序,并初始化該tty設備,將找到的tty驅(qū)動程序保存到driver變量中*/ driver=tty_lookup_driver(device,filp,&noctty,&index); if(IS_ERR(driver)){ retval=PTR_ERR(driver); gotoerr_unlock; } /*checkwhetherwe'rereopeninganexistingtty*/ /*查找對應的tty設備,并將找到的tty設備結(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設備,則需要重新打開該tty設備*/ 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(retval0)?{ ????tty_unlock(tty); ????tty?=?ERR_PTR(retval); ???} ??}?else?{?/*?Returns?with?the?tty_lock?held?for?now?*/ ????????????/*需要初始化該?tty?設備*/ ???tty?=?tty_init_dev(driver,?index); ????????????/*為該?tty?設備分配一個?tty?結(jié)構(gòu)體,并對其進行初始化*/ ???mutex_unlock(&tty_mutex); ??} ??tty_driver_kref_put(driver); ?} ?if?(IS_ERR(tty))?{ ??retval?=?PTR_ERR(tty); ??if?(retval?!=?-EAGAIN?||?signal_pending(current)) ???goto?err_file; ??tty_free_file(filp); ??schedule(); ??goto?retry_open; ?} ?/*將該?tty?設備與文件結(jié)構(gòu)體相關(guān)聯(lián)*/ ?tty_add_file(tty,?filp); ?check_tty_count(tty,?__func__); ????/*如果該?tty?設備是一個偽終端主設備,則需要將?noctty?標志設置為?1*/ ?if?(tty->driver->type==TTY_DRIVER_TYPE_PTY&& tty->driver->subtype==PTY_TYPE_MASTER) noctty=1; tty_debug_hangup(tty,"(ttycount=%d) ",tty->count); /*調(diào)用tty設備的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ù)會檢查文件的標志位,如果包含 O_NOCTTY 標志,則禁止將該 tty 設備設置為控制終端。這是因為如果一個進程打開一個 tty 設備并將其設置為控制終端,其他進程就無法再將該 tty 設備設置為控制終端,這可能會導致一些問題。
如果打開當前的 tty 設備失敗,則需要根據(jù)設備號來查找對應的 tty 驅(qū)動程序,并初始化該 tty 設備。在查找 tty 驅(qū)動程序時,需要調(diào)用 tty_lookup_driver 函數(shù)來查找對應的 tty 驅(qū)動程序,并將找到的 tty 驅(qū)動程序保存到 driver 變量中。如果找不到對應的 tty 驅(qū)動程序,則返回錯誤碼。
如果找到了對應的 tty 驅(qū)動程序,則調(diào)用 tty_driver_lookup_tty 函數(shù)來查找對應的 tty 設備,并將找到的 tty 設備結(jié)構(gòu)體指針保存到 tty 變量中。如果找到了該 tty 設備,則需要重新打開該 tty 設備。否則,需要初始化該 tty 設備。在初始化 tty 設備時,需要調(diào)用 tty_init_dev 函數(shù)來為該 tty 設備分配一個 tty 結(jié)構(gòu)體,并對其進行初始化。
在打開 tty 設備之后,函數(shù)會調(diào)用 tty_add_file 函數(shù)將該 tty 設備與文件結(jié)構(gòu)體相關(guān)聯(lián)。此外,如果該 tty 設備是一個偽終端主設備,則需要將 noctty 標志設置為 1。
最后,函數(shù)會調(diào)用 tty 設備的 open 函數(shù),如果存在的話,來進行一些特定的操作。如果 open 函數(shù)返回錯誤碼,則需要釋放該 tty 設備并返回錯誤碼。如果 open 函數(shù)返回 -ERESTARTSYS,則需要重新打開該 tty 設備。如果有中斷發(fā)生,也需要重新打開該 tty 設備。
tty_write
tty_write()作用是將用戶數(shù)據(jù)寫入 tty 設備,并通過線路規(guī)則(line discipline)進行處理。
線路規(guī)則是 tty 設備的一種機制,用于處理和轉(zhuǎn)換從用戶進程到內(nèi)核和設備的數(shù)據(jù)流。在寫入 tty 設備之前,需要獲取該 tty 設備的線路規(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 設備。
檢查傳入的 tty_struct 指針是否有效,以及是否有其他進程正在訪問該 tty 設備。如果出現(xiàn)問題,返回輸入/輸出錯誤碼 -EIO。
檢查 tty_struct 指針是否有效、tty 設備是否支持寫操作,以及是否已經(jīng)出現(xiàn)了輸入/輸出錯誤。如果出現(xiàn)問題,返回輸入/輸出錯誤碼 -EIO。
檢查 tty 設備是否實現(xiàn)了 write_room 方法,如果沒有,則輸出錯誤信息。
獲取 tty 設備的線路規(guī)則(line discipline),并等待獲取成功。
檢查線路規(guī)則的 write 方法是否存在,如果不存在,返回輸入/輸出錯誤碼 -EIO。否則,調(diào)用 do_tty_write 函數(shù),將數(shù)據(jù)寫入 tty 設備。
釋放線路規(guī)則引用計數(shù)器。
返回寫入操作的結(jié)果,如果寫入成功,則返回寫入的字節(jié)數(shù);否則,返回相應的錯誤碼。
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ù) 。
獲取 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)設置。如果是,則返回 -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)容我們了解知道就好,這里不做深入分析。
-
顯示器
+關(guān)注
關(guān)注
21文章
5017瀏覽量
140466 -
Linux
+關(guān)注
關(guān)注
87文章
11351瀏覽量
210507 -
串口
+關(guān)注
關(guān)注
14文章
1560瀏覽量
77144 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4346瀏覽量
63018
原文標題:【驅(qū)動】串口驅(qū)動分析(二)-tty core
文章出處:【微信號:嵌入式與Linux那些事,微信公眾號:嵌入式與Linux那些事】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
V-BY-ONE技術(shù)優(yōu)勢分析
Android系統(tǒng)開發(fā)之藍牙開發(fā)案例分析
FastCV主要接口分析(第二部分)
FastCV主要接口分析
Xilinx FPGA DDR4接口應用分析
Linux中tty、pty、pts的概念區(qū)別
常用接口分析,必須要全部掌握
TYPE C母座引腳接口分析及優(yōu)勢特點的介紹
SoC接口技術(shù)之低速接口分析(下)
![SoC<b class='flag-5'>接口</b>技術(shù)之低速<b class='flag-5'>接口分析</b>(下)](https://file1.elecfans.com/web2/M00/81/FB/wKgZomQr2j-AHpipAAClNHKrltI525.jpg)
評論