linux设备模型之uart驱动架构分析.docx

上传人:b****7 文档编号:9071595 上传时间:2023-02-03 格式:DOCX 页数:19 大小:84.31KB
下载 相关 举报
linux设备模型之uart驱动架构分析.docx_第1页
第1页 / 共19页
linux设备模型之uart驱动架构分析.docx_第2页
第2页 / 共19页
linux设备模型之uart驱动架构分析.docx_第3页
第3页 / 共19页
linux设备模型之uart驱动架构分析.docx_第4页
第4页 / 共19页
linux设备模型之uart驱动架构分析.docx_第5页
第5页 / 共19页
点击查看更多>>
下载资源
资源描述

linux设备模型之uart驱动架构分析.docx

《linux设备模型之uart驱动架构分析.docx》由会员分享,可在线阅读,更多相关《linux设备模型之uart驱动架构分析.docx(19页珍藏版)》请在冰豆网上搜索。

linux设备模型之uart驱动架构分析.docx

linux设备模型之uart驱动架构分析

一:

前言

接着前面的终端控制台分析,接下来分析serial的驱动.在linux中,serial也对应着终端,通常被称为串口终端.在shell上,我们看到的/dev/ttyS*就是串口终端所对应的设备节点.

在分析具体的serial驱动之前.有必要先分析uart驱动架构.uart是UniversalAsynchronousReceiverandTransmitter的缩写.翻译成中文即为”通用异步收发器”.它是串口设备驱动的封装层.

二:

uart驱动架构概貌

如下图所示:

上图中红色部份标识即为uart部份的操作.

从上图可以看到,uart设备是继tty_driver的又一层封装.实际上uart_driver就是对应tty_driver.在它的操作函数中,将操作转入uart_port.

在写操作的时候,先将数据放入一个叫做circ_buf的环形缓存区.然后uart_port从缓存区中取数据,将其写入到串口设备中.

当uart_port从serial设备接收到数据时,会将设备放入对应linediscipline的缓存区中.

这样.用户在编写串口驱动的时候,只先要注册一个uart_driver.它的主要作用是定义设备节点号.然后将对设备的各项操作封装在uart_port.驱动工程师没必要关心上层的流程,只需按硬件规范将uart_port中的接口函数完成就可以了.

三:

uart驱动中重要的数据结构及其关联

我们可以自己考虑下,基于上面的架构代码应该要怎么写.首先考虑以下几点:

1:

一个uart_driver通常会注册一段设备号.即在用户空间会看到uart_driver对应有多个设备节点.例如:

/dev/ttyS0/dev/ttyS1

每个设备节点是对应一个具体硬件的,从上面的架构来看,每个设备文件应该对应一个uart_port.

也就是说:

uart_device怎么同多个uart_port关系起来?

怎么去区分操作的是哪一个设备文件?

2:

每个uart_port对应一个circ_buf,所以uart_port必须要和这个缓存区关系起来

回忆tty驱动架构中.tty_driver有一个叫成员指向一个数组,即tty->ttys.每个设备文件对应设数组中的一项.而这个数组所代码的数据结构为tty_struct.相应的tty_struct会将tty_driver和ldisc关联起来.

那在uart驱动中,是否也可用相同的方式来处理呢?

将uart驱动常用的数据结构表示如下:

结合上面提出的疑问.可以很清楚的看懂这些结构的设计.

四:

uart_driver的注册操作

Uart_driver注册对应的函数为:

uart_register_driver()代码如下:

intuart_register_driver(structuart_driver*drv)

{

structtty_driver*normal=NULL;

inti,retval;

BUG_ON(drv->state);

/*

*Maybeweshouldbeusingaslabcacheforthis,especiallyif

*wehavealargenumberofportstohandle.

*/

drv->state=kzalloc(sizeof(structuart_state)*drv->nr,GFP_KERNEL);

retval=-ENOMEM;

if(!

drv->state)

gotoout;

normal=alloc_tty_driver(drv->nr);

if(!

normal)

gotoout;

drv->tty_driver=normal;

normal->owner=drv->owner;

normal->driver_name=drv->driver_name;

normal->name=drv->dev_name;

normal->major=drv->major;

normal->minor_start=drv->minor;

normal->type=TTY_DRIVER_TYPE_SERIAL;

normal->subtype=SERIAL_TYPE_NORMAL;

normal->init_termios=tty_std_termios;

normal->init_termios.c_cflag=B9600|CS8|CREAD|HUPCL|CLOCAL;

normal->init_termios.c_ispeed=normal->init_termios.c_ospeed=9600;

normal->flags=TTY_DRIVER_REAL_RAW|TTY_DRIVER_DYNAMIC_DEV;

normal->driver_state=drv;

tty_set_operations(normal,&uart_ops);

/*

*InitialisetheUARTstate(s).

*/

for(i=0;inr;i){

structuart_state*state=drv->statei;

state->close_delay=500;/*.5seconds*/

state->closing_wait=30000;/*30seconds*/

mutex_init(&state->mutex);

}

retval=tty_register_driver(normal);

out:

if(retval

put_tty_driver(normal);

kfree(drv->state);

}

returnretval;

}

从上面代码可以看出.uart_driver中很多数据结构其实就是tty_driver中的.将数据转换为tty_driver之后,注册tty_driver.然后初始化uart_driver->state的存储空间.

这样,就会注册uart_driver->nr个设备节点.主设备号为uart_driver->major.开始的次设备号为uart_driver->minor.

值得注意的是.在这里将tty_driver的操作集统一设为了uart_ops.其次,在tty_driver->driver_state保存了这个uart_driver.这样做是为了在用户空间对设备文件的操作时,很容易转到对应的uart_driver.

另外:

tty_driver的flags成员值为:

TTY_DRIVER_REAL_RAW|TTY_DRIVER_DYNAMIC_DEV.里面包含有TTY_DRIVER_DYNAMIC_DEV标志.结合之前对tty的分析.如果包含有这个标志,是不会在初始化的时候去注册device.也就是说在/dev/下没有动态生成结点(如果是/dev下静态创建了这个结点就另当别论了^_^).

流程图如下:

五:

uart_add_one_port()操作

在前面提到.在对uart设备文件过程中.会将操作转换到对应的port上,这个port跟uart_driver是怎么关联起来的呢?

这就是uart_add_ont_port()的主要工作了.

顾名思义,这个函数是在uart_driver增加一个port.代码如下:

intuart_add_one_port(structuart_driver*drv,structuart_port*port)

{

structuart_state*state;

intret=0;

structdevice*tty_dev;

BUG_ON(in_interrupt());

if(port->line>=drv->nr)

return-EINVAL;

state=drv->stateport->line;

mutex_lock(&port_mutex);

mutex_lock(&state->mutex);

if(state->port){

ret=-EINVAL;

gotoout;

}

state->port=port;

state->pm_state=-1;

port->cons=drv->cons;

port->info=state->info;

/*

*Ifthisportisaconsole,thenthespinlockisalready

*initialised.

*/

if(!

(uart_console(port)&&(port->cons->flags&CON_ENABLED))){

spin_lock_init(&port->lock);

lockdep_set_class(&port->lock,&port_lock_key);

}

uart_configure_port(drv,state,port);

/*

*Registertheportwhetherit'sdetectedornot.Thisallows

*setserialtobeusedtoalterthisportsparameters.

*/

tty_dev=tty_register_device(drv->tty_driver,port->line,port->dev);

if(likely(!

IS_ERR(tty_dev))){

device_can_wakeup(tty_dev)=1;

device_set_wakeup_enable(tty_dev,0);

}else

printk(KERN_ERR"Cannotregisterttydeviceonline%d\n",

port->line);

/*

*EnsureUPF_DEADisnotset.

*/

port->flags&=~UPF_DEAD;

out:

mutex_unlock(&state->mutex);

mutex_unlock(&port_mutex);

returnret;

}

首先这个函数不能在中断环境中使用.Uart_port->line就是对uart设备文件序号.它对应的也就是uart_driver->state数组中的uart_port->line项.

它主要初始化对应uart_driver->state项.接着调用uart_configure_port()进行port的自动配置.然后注册tty_device.如果用户空间运行了udev或者已经配置好了hotplug.就会在/dev下自动生成设备文件了.

操作流程图如下所示:

六:

设备节点的open操作

在用户空间执行open操作的时候,就会执行uart_ops->open.Uart_ops的定义如下:

staticconststructtty_operationsuart_ops={

.open=uart_open,

.close=uart_close,

.write=uart_write,

.put_char=uart_put_char,

.flush_chars=uart_flush_chars,

.write_room=uart_write_room,

.chars_in_buffer=uart_chars_in_buffer,

.flush_buffer=uart_flush_buffer,

.ioctl=uart_ioctl,

.throttle=uart_throttle,

.unthrottle=uart_unthrottle,

.send_xchar=uart_send_xchar,

.set_termios=uart_set_termios,

.stop=uart_stop,

.start=uart_start,

.hangup=uart_hangup,

.break_ctl=uart_break_ctl,

.wait_until_sent=uart_wait_until_sent,

#ifdefCONFIG_PROC_FS

.read_proc=uart_read_proc,

#endif

.tiocmget=uart_tiocmget,

.tiocmset=uart_tiocmset,

};

对应open的操作接口为uart_open.代码如下:

staticintuart_open(structtty_struct*tty,structfile*filp)

{

structuart_driver*drv=(structuart_driver*)tty->driver->driver_state;

structuart_state*state;

intretval,line=tty->index;

BUG_ON(!

kernel_locked());

pr_debug("uart_open(%d)called\n",line);

/*

*tty->driver->numwon'tchange,sowewon'tfailherewith

*tty->driver_datasettosomethingnon-NULL(andtherefore

*wewon'tgetcaughtbyuart_close()).

*/

retval=-ENODEV;

if(line>=tty->driver->num)

gotofail;

/*

*Wetakethesemaphoreinsideuart_gettoguaranteethatwewon't

*bere-enteredwhileallocatingtheinfostructure,orwhilewe

*requestanyIRQsthatthedrivermayneed.Thisalsohasthenice

*side-effectthatitdelaystheactionofuart_hangup,sowecan

*guaranteethatinfo->ttywillalwayscontainsomethingreasonable.

*/

state=uart_get(drv,line);

if(IS_ERR(state)){

retval=PTR_ERR(state);

gotofail;

}

/*

*Oncewesettty->driver_datahere,weareguaranteedthat

*uart_close()willdecrementthedrivermoduleusecount.

*Anyfailuresfromhereonwardsshouldnottouchthecount.

*/

tty->driver_data=state;

tty->low_latency=(state->port->flags&UPF_LOW_LATENCY)?

1:

0;

tty->alt_speed=0;

state->info->tty=tty;

/*

*Iftheportisinthemiddleofclosing,bailoutnow.

*/

if(tty_hung_up_p(filp)){

retval=-EAGAIN;

state->count--;

mutex_unlock(&state->mutex);

gotofail;

}

/*

*MakesurethedeviceisinD0state.

*/

if(state->count==1)

uart_change_pm(state,0);

/*

*Startuptheserialport.

*/

retval=uart_startup(state,0);

/*

*Ifwesucceeded,waituntiltheportisready.

*/

if(retval==0)

retval=uart_block_til_ready(filp,state);

mutex_unlock(&state->mutex);

/*

*Ifthisisthefirstopentosucceed,adjustthingstosuit.

*/

if(retval==0&&!

(state->info->flags&UIF_NORMAL_ACTIVE)){

state->info->flags|=UIF_NORMAL_ACTIVE;

uart_update_termios(state);

}

fail:

returnretval;

}

在这里函数里,继续完成操作的设备文件所对应state初始化.现在用户空间open这个设备了.即要对这个文件进行操作了.那uart_port也要开始工作了.即调用uart_startup()使其进入工作状态.当然,也需要初始化uart_port所对应的环形缓冲区circ_buf.即state->info->xmit.

特别要注意,在这里将tty->driver_data=state;这是因为以后的操作只有port相关了,不需要去了解uart_driver的相关信息.

跟踪看一下里面调用的两个重要的子函数.uart_get()和uart_startup().先分析uart_get().代码如下:

staticstructuart_state*uart_get(structuart_driver*drv,intline)

{

structuart_state*state;

intret=0;

state=drv->stateline;

if(mutex_lock_interruptible(&state->mutex)){

ret=-ERESTARTSYS;

gotoerr;

}

state->count;

if(!

state->port||state->port->flags&UPF_DEAD){

ret=-ENXIO;

gotoerr_unlock;

}

if(!

state->info){

state->info=kzalloc(sizeof(structuart_info),GFP_KERNEL);

if(state->info){

init_waitqueue_head(&state->info->open_wait);

init_waitqueue_head(&state->info->delta_msr_wait);

/*

*Linktheinfointotheotherstructures.

*/

state->port->info=state->info;

tasklet_init(&state->info->tlet,uart_tasklet_action,

(unsignedlong)state);

}else{

ret=-ENOMEM;

gotoerr_unlock;

}

}

returnstate;

err_unlock:

state->count--;

mutex_unlock(&state->mutex);

err:

returnERR_PTR(ret);

}

从代码中可以看出.这里注要是操作是初始化state->info.注意port->info就是state->info的一个副本.即port直接通过port->info可以找到它要操作的缓存区.

uart_startup()代码如下:

staticintuart_startup(structuart_state*state,intinit_hw)

{

structuart_info*info=state->info;

structuart_port*port=state->port;

unsignedlongpage;

intretval=0;

if(info->flags&UIF_INITIALIZED)

return0;

/*

*SettheTTYIOerrormarker-wewillonlyclearthis

*oncewehavesuccessfullyopenedtheport.Alsoset

*upthetty->alt_speedkludge

*/

set_bit(TTY_IO_ERROR,&info->tty->flags);

if(port->type==PORT_UNKNOWN)

return0;

/*

*Initialiseandallocatethetransmitandtemporary

*buffer.

*/

if(!

info->xmit.buf){

page=get_zeroed_page(GFP_KERNEL);

if(!

page)

return-ENOMEM;

info->xmit.buf=(unsignedchar*)page;

uart_circ_clear(&info->xmit);

}

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

if(retval==0){

if(init_hw){

/*

*Initialisethehardwareportsettings.

*/

uart_change_speed(state,NULL);

/*

*SetuptheRTSandDTRsignalsoncethe

*portisopenandreadytorespond.

*/

if(info->tty->termios->c_cflag&CBAUD)

uart_set_mctrl(port,TIOCM_RTS|TIOCM_DTR);

}

if(info->flags&UIF_CTS_FLOW){

spin_lock_irq(&port->lock);

if(!

(port->ops->get_mctrl(port)&TIOCM_CTS))

info->tty->hw_stopped=1;

spin_unl

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 解决方案 > 学习计划

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1