0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

tty驱动接口分析

嵌入式与Linux那些事 来源:嵌入式与Linux那些事 2023-07-14 12:21 次阅读

前言

tty这个名称源于电传打字节的简称,在linux表示各种终端,终端通常都跟硬件相对应。比如对应于输入设备键盘鼠标,输出设备显示器的控制终端和串口终端。也有对应于不存在设备的pty驱动。在如此众多的终端模型之中,linux是怎么将它们统一建模的呢?这就是我们今天要讨论的问题。

tty驱动概貌

tty架构如下所示:

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

如上图所示,用户空间主要是通过系统调用与tty core交互。tty core根据用空间操作的类型再选择跟line discipline和tty driver交互。

例如,设置硬件的ioctl指令就直接交给tty_driver处理。read和write操作就会交给 line discipline处理。

Line discipline是线路规程的意思。正如它的名字一样,它表示的是这条终端”线程”的输入与输出规范设置。主要用来进行输入/输出数据的预处理。

处理之后,就会将数据交给tty driver ,它将字符转换成终端可以理解的字串。将其传给终端设备。

值得注意的是,这个架构没有为tty driver 提供read操作。也就是说tty core 和line discipline都没有办法从tty driver里直接读终端信息。这是因为tty driver对应的hardware并不一定是输入数据和输出 数据的共同负载者。

例如控制终端,输出设备是显示器,输入设备是键盘。基于这样的原理。在line discipline中有一个输入缓存区,并提供了一个名叫receive_buf()的接口函数。对应的终端设备只要调用line discipine的receiver_buf函数,将数据写入到输入缓存区就可以了。如果一个设备同时是输入设备又是输出设备。那在设备的中断处理中调用receive_buf()将数据写入即可.

tty驱动接口分析

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 子系统的 sysctl 相关设置,包括注册 sysctl 参数、创建 sysctl 目录等。

初始化 tty 设备的字符设备对象,并将其与 tty 设备操作函数 tty_fops 绑定。同时,创建一个名为 "tty" 的 tty 设备节点,并将其设备号设置为 MKDEV(TTYAUX_MAJOR, 0)。

初始化控制台设备的字符设备对象,并将其添加到字符设备系统中。同时,创建一个名为 "console" 的控制台设备节点,并将其设备号设置为 MKDEV(TTYAUX_MAJOR, 1)。该控制台设备节点还将在 sysfs 中创建一个名为 "console" 的目录,并在该目录下创建多个属性文件,用于控制控制台的一些属性。

如果内核支持虚拟终端,则初始化虚拟终端。

这里我们看到了熟悉的cdev_init(),device_create()之类的函数,这正是字符设备的创建流程。因此,我们说串口驱动也是一个字符设备驱动。

而在serial8250_init()中,会调用platform_driver_register()去注册serial8250_isa_driver,在设备树节点和serial8250_isa_driver name匹配的时候,就会进入probe流程。因此,也可以说串口驱动是总线设备驱动模型。

tty_alloc_driver

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

__tty_alloc_driver()用于分配一个 tty 驱动程序的数据结构 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结构体,并对其中的一些字段进行初始化,包括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标志位没有被设置,那么函数会分配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标志位没有被设置,那么函数会分配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;
}

/*函数会根据lines的值分配相应数量的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 驱动程序的,被 tty 驱动程序调用以将自己注册到内核中。

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

/*确认是否要内核动态分配主设备号*/
if(!driver->major){
/*函数调用alloc_chrdev_region函数来动态分配主设备号,并将分配的主设备号和次设备号保存在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{
/*已经预先分配了主设备号,函数调用register_chrdev_region函数来注册设备号*/
dev=MKDEV(driver->major,driver->minor_start);
error=register_chrdev_region(dev,driver->num,driver->name);
}
if(error< 0)
  goto err;
 /*判断是否设置了 TTY_DRIVER_DYNAMIC_ALLOC 标志位*/
 if (driver->flags&TTY_DRIVER_DYNAMIC_ALLOC){
/*需要动态分配tty设备号,函数调用tty_cdev_add函数来添加tty设备号,并将每个tty设备的字符设备注册到内核中*/
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;i< driver->num;i++){
/*需要注册固定的tty设备号,函数在循环中调用tty_register_device函数来注册每个tty设备号,并将每个tty设备注册到内核中*/
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结构体中的flags字段设置为TTY_DRIVER_INSTALLED,表示该驱动程序已经被成功注册到内核中*/
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()函数操作比较简单。就是为tty_driver创建字符设备。然后将字符设备的操作集指定为tty_fops。并且将tty_driver 挂载到tty_drivers链表中。这个链表中是以设备号为关键字找到对应的driver。

特别的。如果没有定义TTY_DRIVER_DYNAMIC_DEV。还会在sysfs中创建一个类设备。这样主要是为了udev管理设备。

tty_unregister_device

tty_unregister_device用于注销一个 tty 设备。该函数的作用是销毁设备节点和字符设备,以便于释放与该 tty 设备相关的资源,例如内存和设备文件等.

/**
*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所做工作如下:

调用 device_destroy 函数来销毁 tty 设备对应的设备节点。接受两个参数:第一个参数 tty_class 表示 tty 类,第二个参数是 tty 设备的设备号,其中 MKDEV(driver->major, driver->minor_start) + index 表示 tty 设备的设备号,driver->major 表示 tty 设备的主设备号,driver->minor_start 表示 tty 设备的次设备号的起始值,index 表示 tty 设备的索引

如果该 tty 驱动程序不是动态分配的,则调用 cdev_del 函数来注销该 tty 设备对应的字符设备。

get_tty_driver

get_tty_driver作用是在用户空间的应用程序使用 tty 设备时,获取对应的 tty 驱动程序的信息。

/**
*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 || device >=base+p->num)
continue;
*index=device-base;
returntty_driver_kref_get(p);
}
returnNULL;
}

首先使用 list_for_each_entry 循环遍历全局链表 tty_drivers,该链表中保存了所有已经注册的 tty 驱动程序。对于每个 tty 驱动程序,函数将其设备号的起始值和结束值计算出来,如果给定设备号不在这个范围内,则继续遍历下一个 tty 驱动程序。

如果给定设备号在某个 tty 驱动程序的范围内,则计算出该设备号对应的 tty 设备的索引值,并调用 tty_driver_kref_get 函数来获取该 tty 驱动程序的引用计数。函数返回该 tty 驱动程序的结构体指针,并将找到的 tty 设备的索引值保存到 index 参数中。

需要注意的是,函数在访问全局链表 tty_drivers 时,需要持有互斥锁 tty_mutex。因为多个应用程序可能同时访问同一个 tty 驱动程序,如果没有互斥锁保护,可能会导致并发问题。

tty_open

从注册的过程可以看到,所有的操作都会对应到tty_fops中。Open操作对应的操作接口是tty_open(),用于打开一个 tty 设备。函数的作用是在用户空间的应用程序使用 tty 设备时,打开对应的 tty 设备,并初始化相应的数据结构。

/**
*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结构体*/
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);
/*根据设备号来查找对应的tty驱动程序,并初始化该tty设备,将找到的tty驱动程序保存到driver变量中*/
driver=tty_lookup_driver(device,filp,&noctty,&index);
if(IS_ERR(driver)){
retval=PTR_ERR(driver);
gotoerr_unlock;
}

/*checkwhetherwe'rereopeninganexistingtty*/
/*查找对应的tty设备,并将找到的tty设备结构体指针保存到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(retval< 0) {
    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 结构体,并对其进行初始化*/
   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 设备与文件结构体相关联*/
 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);

/*调用tty设备的open函数*/
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;
}

函数所作工作如下:

在打开 tty 设备时,该函数会检查文件的标志位,如果包含 O_NOCTTY 标志,则禁止将该 tty 设备设置为控制终端。这是因为如果一个进程打开一个 tty 设备并将其设置为控制终端,其他进程就无法再将该 tty 设备设置为控制终端,这可能会导致一些问题。

如果打开当前的 tty 设备失败,则需要根据设备号来查找对应的 tty 驱动程序,并初始化该 tty 设备。在查找 tty 驱动程序时,需要调用 tty_lookup_driver 函数来查找对应的 tty 驱动程序,并将找到的 tty 驱动程序保存到 driver 变量中。如果找不到对应的 tty 驱动程序,则返回错误码。

如果找到了对应的 tty 驱动程序,则调用 tty_driver_lookup_tty 函数来查找对应的 tty 设备,并将找到的 tty 设备结构体指针保存到 tty 变量中。如果找到了该 tty 设备,则需要重新打开该 tty 设备。否则,需要初始化该 tty 设备。在初始化 tty 设备时,需要调用 tty_init_dev 函数来为该 tty 设备分配一个 tty 结构体,并对其进行初始化。

在打开 tty 设备之后,函数会调用 tty_add_file 函数将该 tty 设备与文件结构体相关联。此外,如果该 tty 设备是一个伪终端主设备,则需要将 noctty 标志设置为 1。

最后,函数会调用 tty 设备的 open 函数,如果存在的话,来进行一些特定的操作。如果 open 函数返回错误码,则需要释放该 tty 设备并返回错误码。如果 open 函数返回 -ERESTARTSYS,则需要重新打开该 tty 设备。如果有中断发生,也需要重新打开该 tty 设备。

tty_write

tty_write()作用是将用户数据写入 tty 设备,并通过线路规则(line discipline)进行处理。

线路规则是 tty 设备的一种机制,用于处理和转换从用户进程到内核和设备的数据流。在写入 tty 设备之前,需要获取该 tty 设备的线路规则,并调用其 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 数据结构的指针,表示要写入的 tty 设备。

检查传入的 tty_struct 指针是否有效,以及是否有其他进程正在访问该 tty 设备。如果出现问题,返回输入/输出错误码 -EIO。

检查 tty_struct 指针是否有效、tty 设备是否支持写操作,以及是否已经出现了输入/输出错误。如果出现问题,返回输入/输出错误码 -EIO。

检查 tty 设备是否实现了 write_room 方法,如果没有,则输出错误信息。

获取 tty 设备的线路规则(line discipline),并等待获取成功。

检查线路规则的 write 方法是否存在,如果不存在,返回输入/输出错误码 -EIO。否则,调用 do_tty_write 函数,将数据写入 tty 设备。

释放线路规则引用计数器。

返回写入操作的结果,如果写入成功,则返回写入的字节数;否则,返回相应的错误码。

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()实现终端设备文件读操作的函数 。

获取 tty_struct 结构体、inode 和 line discipline 对象的指针。

调用 tty_paranoia_check() 函数检查 tty_struct 结构体是否可用。如果检查失败,返回 -EIO。

检查 tty_struct 结构体是否为空或者 TTY_IO_ERROR 标志位已经设置。如果是,则返回 -EIO。

获取 line discipline 对象的引用,确保它不会在 tty_read() 函数执行期间被卸载。

检查 line discipline 的 read() 方法是否可用。如果可用,则调用该方法进行读取操作,并将返回的字节数保存在变量 i 中。如果不可用,返回 -EIO。

释放 line discipline 的引用。

如果读取操作成功,调用 tty_update_time() 函数更新 inode 的访问时间。

返回读取的字节数。

小结

在这一节里,只对tty的构造做一个分析,具体的比如线路规程的内容我们了解知道就好,这里不做深入分析。

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 显示器
    +关注

    关注

    21

    文章

    4971

    浏览量

    139942
  • Linux
    +关注

    关注

    87

    文章

    11296

    浏览量

    209365
  • 串口
    +关注

    关注

    14

    文章

    1551

    浏览量

    76449
  • 函数
    +关注

    关注

    3

    文章

    4329

    浏览量

    62578

原文标题:【驱动】串口驱动分析(二)-tty core

文章出处:【微信号:嵌入式与Linux那些事,微信公众号:嵌入式与Linux那些事】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    V-BY-ONE威廉希尔官方网站 优势分析

    目前接口分析:1.TTL:局限性太大,一般在小屏上面应用较多(7寸以下)2.7寸以上的屏现在的接口有LVDS,eDP,iDP,V-BY-ONE。附件讲述一下由THine公司发布的V-BY-ONE接口的威廉希尔官方网站 优势。需要详细资料的请电
    发表于 03-14 18:24

    Android系统开发之蓝牙开发案例分析

    案例,带您了解真正的蓝牙开发威廉希尔官方网站 。主要涉及:蓝牙应用接口,蓝牙Framework架构,蓝牙设备驱动,蓝牙开发技巧及数据处理等要点。1. Android的蓝牙框架分析2. Android蓝牙应用开发
    发表于 07-24 16:32

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

    FastCV主要接口分析之二 FastCV为计算机视觉(CV,computer vision)的应用开发者提供了两个主要功能:提供CV常用的函数库,其已经进行了优化且可以高效的运行在移动设备上;提供
    发表于 09-21 10:29

    FastCV主要接口分析

    FastCV主要接口分析之一 安装Hexagon SDK后,会在安装的主目录下/Hexagon_SDK/2.0/lib/fastcv/hexagon_Release_v5存在如下文件: 上图
    发表于 09-21 17:05

    浅析linux UART驱动tty架构

    关于linux UART驱动tty架构的理解
    发表于 07-03 09:55

    高清晰视频会议终端应用接口分析

    高清晰视频会议终端应用接口分析 DVI接口与HDMI接口介绍   视频通讯作为
    发表于 02-21 10:10 2217次阅读

    Xilinx FPGA DDR4接口应用分析

    本内容主要分析了基于FPGA的系统需求,赛灵思UltraScale FPGA DDR4和其他并行接口分析以及针对高性能高度灵活方案的PHY解决方案介绍。
    发表于 08-03 19:37 191次下载

    Linux中tty、pty、pts的概念区别

    /tty0的符号链接吗?A: 目前的大多数文本中都称/dev/console是到/dev/tty0的链接(包括《Linux内核源代码情景分析》),但是这样说是不确切的。根据内核文档,在2.1.71之前
    发表于 04-02 14:36 1306次阅读

    MAVLink学习之路05_ MAVLink应用编程接口分析

    MAVLink学习之路05_MAVLink应用编程接口分析
    的头像 发表于 03-07 16:21 5808次阅读

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

    日常使用手机、电脑以及其他电子产品,免不了要跟各种接口打交道。周末花了些时间查了些资料,并总结自己的实际使用经验,跟大家聊一聊我们日常使用手机、iPad、电脑、外设中常用到的接口。 讲明白各种接口
    的头像 发表于 10-30 09:57 492次阅读

    TYPE C母座引脚接口分析及优势特点的介绍

    接口分析及优势特点。 TYPE C母座引脚接口分析 Type C母座一般简称有type c、type-c等,其实这些指的都
    发表于 12-18 10:59 1.5w次阅读

    tty.js基于浏览器的终端模拟器

    ./oschina_soft/tty.js.zip
    发表于 05-26 14:54 1次下载
    <b class='flag-5'>tty</b>.js基于浏览器的终端模拟器

    SoC接口威廉希尔官方网站 之低速接口分析(下)

    JTAG协议核心是三个寄存器和TAP状态机。作为DEBUG的JTAG接口会在DAP中利用旁路寄存器将JTAG协议转换为APB协议。作为DFT的JTAG接口会直接利用数据寄存器进行边界扫描。
    的头像 发表于 04-04 16:08 1839次阅读
    SoC<b class='flag-5'>接口</b>威廉希尔官方网站
之低速<b class='flag-5'>接口分析</b>(下)

    Linux中/dev/tty、/dev/tty0和/dev/console之间的区别在哪?

    在Linux系统中,/dev/tty、/dev/tty0和/dev/console是一些特殊的设备文件,经常用于控制台和命令行界面的操作。
    的头像 发表于 06-21 09:06 3732次阅读

    串口驱动分析之serial driver

    前两节我们介绍串口驱动的框架和tty core部分。这节我们介绍和硬件紧密相关的串口驱动部分。
    的头像 发表于 09-04 14:23 422次阅读
    串口<b class='flag-5'>驱动</b><b class='flag-5'>分析</b>之serial driver