linux设备驱动之8250串口驱动.docx

上传人:b****1 文档编号:15210273 上传时间:2023-07-02 格式:DOCX 页数:43 大小:30.54KB
下载 相关 举报
linux设备驱动之8250串口驱动.docx_第1页
第1页 / 共43页
linux设备驱动之8250串口驱动.docx_第2页
第2页 / 共43页
linux设备驱动之8250串口驱动.docx_第3页
第3页 / 共43页
linux设备驱动之8250串口驱动.docx_第4页
第4页 / 共43页
linux设备驱动之8250串口驱动.docx_第5页
第5页 / 共43页
linux设备驱动之8250串口驱动.docx_第6页
第6页 / 共43页
linux设备驱动之8250串口驱动.docx_第7页
第7页 / 共43页
linux设备驱动之8250串口驱动.docx_第8页
第8页 / 共43页
linux设备驱动之8250串口驱动.docx_第9页
第9页 / 共43页
linux设备驱动之8250串口驱动.docx_第10页
第10页 / 共43页
linux设备驱动之8250串口驱动.docx_第11页
第11页 / 共43页
linux设备驱动之8250串口驱动.docx_第12页
第12页 / 共43页
linux设备驱动之8250串口驱动.docx_第13页
第13页 / 共43页
linux设备驱动之8250串口驱动.docx_第14页
第14页 / 共43页
linux设备驱动之8250串口驱动.docx_第15页
第15页 / 共43页
linux设备驱动之8250串口驱动.docx_第16页
第16页 / 共43页
linux设备驱动之8250串口驱动.docx_第17页
第17页 / 共43页
linux设备驱动之8250串口驱动.docx_第18页
第18页 / 共43页
linux设备驱动之8250串口驱动.docx_第19页
第19页 / 共43页
linux设备驱动之8250串口驱动.docx_第20页
第20页 / 共43页
亲,该文档总共43页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

linux设备驱动之8250串口驱动.docx

《linux设备驱动之8250串口驱动.docx》由会员分享,可在线阅读,更多相关《linux设备驱动之8250串口驱动.docx(43页珍藏版)》请在冰点文库上搜索。

linux设备驱动之8250串口驱动.docx

linux设备驱动之8250串口驱动

linux设备驱动之8250串口驱动

一:

前言

前一段时间自己实践了一下8250芯片串口驱动的编写。

今天就在此基础上分析一下linuxkernel自带的串口驱动。

毕竟只有对比专业的驱动代码才能更好的进步,同以往一样,基于linixkernel2.6.25.相应驱动代码位于:

linux-2.6.25/drivers/serial/8250.c。

二:

8250串口驱动初始化

相应的初始化函数为serial8250_init().代码如下:

staticint__initserial8250_init(void)

{

intret,i;

if(nr_uarts>UART_NR)

nr_uarts=UART_NR;

printk(KERN_INFO"Serial:

8250/16550driver$Revision:

1.90$"

"%dports,IRQsharing%sabled\n",nr_uarts,

share_irqs?

"en":

"dis");

for(i=0;i

spin_lock_init(&irq_lists[i].lock);

ret=uart_register_driver(&serial8250_reg);

if(ret)

gotoout;

serial8250_isa_devs=platform_device_alloc("serial8250",

PLAT8250_DEV_LEGACY);

if(!

serial8250_isa_devs){

ret=-ENOMEM;

gotounreg_uart_drv;

}

ret=platform_device_add(serial8250_isa_devs);

if(ret)

gotoput_dev;

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

ret=platform_driver_register(&serial8250_isa_driver);

if(ret==0)

gotoout;

platform_device_del(serial8250_isa_devs);

put_dev:

platform_device_put(serial8250_isa_devs);

unreg_uart_drv:

uart_unregister_driver(&serial8250_reg);

out:

returnret;

}

这段代码涉及到的知识要求,如platform,uart等我们在之前都已经做过详细的分析。

这里不再重复。

在代码中UART_NR:

表示串口的个数。

这个参数在编译内核的时候可以自己配置,默认为32。

我们按照代码中的流程一步一步进行研究。

1:

注册uart_driver.

对应uart-driver的结构为serial8250_reg.定义如下:

staticstructuart_driverserial8250_reg={

.owner=THIS_MODULE,

.driver_name="serial",

.dev_name="ttyS",

.major=TTY_MAJOR,

.minor=64,

.nr=UART_NR,

.cons=SERIAL8250_CONSOLE,

};

TTY_MAJOR定义如下:

#defineTTY_MAJOR4

从上面可以看出。

串口对应的设备节点为/dev/ttyS0~/dev/ttyS0(UART_NR).设备节点号为(4。

64)起始的UART_NR个节点..

2:

初始化并注册platform_device

相关代码如下:

serial8250_isa_devs=platform_device_alloc("serial8250",PAT8250_DEV_LEGACY);

platform_device_add(serial8250_isa_devs);

可以看出。

serial8250_isa_devs.->name为serial8250。

这个参数是在匹配platform_device和platform_driver使用的.

3:

为uart-driver添加port.

相关代码如下:

serial8250_register_ports(&serial8250_reg,&serial8250_isa_devs->dev)

跟进这个函数看一下:

staticvoid__init

serial8250_register_ports(structuart_driver*drv,structdevice*dev)

{

inti;

serial8250_isa_init_ports();

for(i=0;i

structuart_8250_port*up=&serial8250_ports[i];

up->port.dev=dev;

uart_add_one_port(drv,&up->port);

}

}

在这里函数里,初始化了port.然后将挂添加到uart-driver中。

我们还注意到。

生成的deivce节点,在sysfs中是位于platform_deivce对应目录的下面.

serial8250_isa_init_ports()代码如下所示:

staticvoid__initserial8250_isa_init_ports(void)

{

structuart_8250_port*up;

staticintfirst=1;

inti;

if(!

first)

return;

first=0;

for(i=0;i

structuart_8250_port*up=&serial8250_ports[i];

up->port.line=i;

spin_lock_init(&up->port.lock);

init_timer(&up->timer);

up->timer.function=serial8250_timeout;

/*

*ALPHA_KLUDGE_MCRneedstobekilled.

*/

up->mcr_mask=~ALPHA_KLUDGE_MCR;

up->mcr_force=ALPHA_KLUDGE_MCR;

up->port.ops=&serial8250_pops;

}

for(i=0,up=serial8250_ports;

i

i++,up++){

up->port.iobase=old_serial_port[i].port;

up->port.irq=irq_canonicalize(old_serial_port[i].irq);

up->port.uartclk=old_serial_port[i].baud_base*16;

up->port.flags=old_serial_port[i].flags;

up->port.hub6=old_serial_port[i].hub6;

up->port.membase=old_serial_port[i].iomem_base;

up->port.iotype=old_serial_port[i].io_type;

up->port.regshift=old_serial_port[i].iomem_reg_shift;

if(share_irqs)

up->port.flags|=UPF_SHARE_IRQ;

}

}

在这里,我们关注一下注要成员的初始化。

Uart_port的各项操作位于serial8250_pops中.iobaseirq等成员是从old_serial_por这个结构中得来的,这个结构如下所示:

staticconststructold_serial_portold_serial_port[]={

SERIAL_PORT_DFNS/*definedinasm/serial.h*/

}

#defineSERIAL_PORT_DFNS

/*UARTCLKPORTIRQFLAGS*/

{0,BASE_BAUD,0x3F8,4,STD_COM_FLAGS},/*ttyS0*/

{0,BASE_BAUD,0x2F8,3,STD_COM_FLAGS},/*ttyS1*/

{0,BASE_BAUD,0x3E8,4,STD_COM_FLAGS},/*ttyS2*/

{0,BASE_BAUD,0x2E8,3,STD_COM4_FLAGS},/*ttyS3*/

从上面看到。

前两项对应了com1com2的各项参数。

如寄存器首始地址,Irq号等。

后面两项不太清楚。

在上面的代码中,我们看到了uart_port各项成员的初始化。

在后面很多操作中需要用到这个成员。

我们等分析相关部份的时候,再到这个地方来看相关成员的值。

4:

注册platform_driver

相关代码如下:

platform_driver_register(&serial8250_isa_driver);

serial8250_isa_driver定义如下:

staticstructplatform_driverserial8250_isa_driver={

.probe=serial8250_probe,

.remove=__devexit_p(serial8250_remove),

.suspend=serial8250_suspend,

.resume=serial8250_resume,

.driver={

.name="serial8250",

.owner=THIS_MODULE,

},

}

为了以后把分析集中到具体的驱动部份.我们先把这个platform_driver引会的事件讲述完.

经过前面有关platform的分析我们知道.这个platform的name为”serial8250”.刚好跟前面注册的platform_device相匹配.会调用platform_driver->probe.在这里,对应的接口为:

serial8250_probe().代码如下:

staticint__devinitserial8250_probe(structplatform_device*dev)

{

structplat_serial8250_port*p=dev->dev.platform_data;

structuart_portport;

intret,i;

memset(&port,0,sizeof(structuart_port));

for(i=0;p&&p->flags!

=0;p++,i++){

port.iobase=p->iobase;

port.membase=p->membase;

port.irq=p->irq;

port.uartclk=p->uartclk;

port.regshift=p->regshift;

port.iotype=p->iotype;

port.flags=p->flags;

port.mapbase=p->mapbase;

port.hub6=p->hub6;

port.private_data=p->private_data;

port.dev=&dev->dev;

if(share_irqs)

port.flags|=UPF_SHARE_IRQ;

ret=serial8250_register_port(&port);

if(ret<0){

dev_err(&dev->dev,"unabletoregisterportatindex%d"

"(IO%lxMEM%llxIRQ%d):

%d\n",i,

p->iobase,(unsignedlonglong)p->mapbase,

p->irq,ret);

}

}

return0;

}

从上述代码可以看出.会将dev->dev.platform_data所代表的port添加到uart_driver中.这个dev->dev.platform_data究竟代表什么.我们在看到的时候再来研究它.

现在,我们把精力集中到uart_port的操作上.

三:

config_port过程

在初始化uart_port的过程中,在以下代码片段:

serial8250_isa_init_ports(void)

{

……

……

for(i=0,up=serial8250_ports;

i

i++,up++){

up->port.iobase=old_serial_port[i].port;

up->port.irq=irq_canonicalize(old_serial_port[i].irq);

up->port.uartclk=old_serial_port[i].baud_base*16;

up->port.flags=old_serial_port[i].flags;

up->port.hub6=old_serial_port[i].hub6;

up->port.membase=old_serial_port[i].iomem_base;

up->port.iotype=old_serial_port[i].io_type;

up->port.regshift=old_serial_port[i].iomem_reg_shift;

if(share_irqs)

up->port.flags|=UPF_SHARE_IRQ;

}

}

而old_serial_port又定义如下:

staticconststructold_serial_portold_serial_port[]={

SERIAL_PORT_DFNS/*definedinasm/serial.h*/

};

#defineSERIAL_PORT_DFNS

/*UARTCLKPORTIRQFLAGS*/

{0,BASE_BAUD,0x3F8,4,STD_COM_FLAGS},/*ttyS0*/

{0,BASE_BAUD,0x2F8,3,STD_COM_FLAGS},/*ttyS1*/

{0,BASE_BAUD,0x3E8,4,STD_COM_FLAGS},/*ttyS2*/

{0,BASE_BAUD,0x2E8,3,STD_COM4_FLAGS},/*ttyS3*/

由此可见.port->flags被定义成了STD_COM_FLAGS,定义如下:

#ifdefCONFIG_SERIAL_DETECT_IRQ

#defineSTD_COM_FLAGS(ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST|ASYNC_AUTO_IRQ)

#defineSTD_COM4_FLAGS(ASYNC_BOOT_AUTOCONF|ASYNC_AUTO_IRQ)

#else

#defineSTD_COM_FLAGS(ASYNC_BOOT_AUTOCONF|ASYNC_SKIP_TEST)

#defineSTD_COM4_FLAGSASYNC_BOOT_AUTOCONF

#endif

从这里看到,不管是否自己探测IRQ,都会定义ASYNC_BOOT_AUTOCONF.这样,在uart_add_one_port()的时候.就会进入到port->config_port来配置端口.在8250中,对应的接口为:

serial8250_config_port().代码如下:

staticvoidserial8250_config_port(structuart_port*port,intflags)

{

structuart_8250_port*up=(structuart_8250_port*)port;

intprobeflags=PROBE_ANY;

intret;

/*

*Findtheregionthatwecanprobefor.Thisinturn

*tellsuswhetherwecanprobeforthetypeofport.

*/

ret=serial8250_request_std_resource(up);

if(ret<0)

return;

ret=serial8250_request_rsa_resource(up);

if(ret<0)

probeflags&=~PROBE_RSA;

if(flags&UART_CONFIG_TYPE)

autoconfig(up,probeflags);

if(up->port.type!

=PORT_UNKNOWN&&flags&UART_CONFIG_IRQ)

autoconfig_irq(up);

if(up->port.type!

=PORT_RSA&&probeflags&PROBE_RSA)

serial8250_release_rsa_resource(up);

if(up->port.type==PORT_UNKNOWN)

serial8250_release_std_resource(up);

}

serial8250_request_std_resource和serial8250_request_rsa_resource都是分配操作的端口.回顾在前面的分析中.port的相关参数会从old_serial_port中取得.而old_serial_port中又没有定义port->iotype和port->regshift.也就是说对应这两项全为0.而

#defineUPIO_PORT(0)

即表示是要操作I/O端口.

自己阅读这两个函数代表.会发现在serial8250_request_rsa_resource()中是会返回失败的.

另外,在uart_add_one_port()在进行端口匹配时,会先置flags为UART_CONFIG_TYPE.

这样,在本次操作中,if(flags&UART_CONFIG_TYPE)是会满足的.相应的就会进入autoconfig().

代码如下,这段代码比较长,分段分析如下:

staticvoidautoconfig(structuart_8250_port*up,unsignedintprobeflags)

{

unsignedcharstatus1,scratch,scratch2,scratch3;

unsignedcharsave_lcr,save_mcr;

unsignedlongflags;

if(!

up->port.iobase&&!

up->port.mapbase&&!

up->port.membase)

return;

DEBUG_AUTOCONF("ttyS%d:

autoconf(0x%04x,0x%p):

",

up->port.line,up->port.iobase,up->port.membase);

/*

*WereallydoneedglobalIRQsdisabledhere-we'regoingto

*befrobbingthechipsIRQenableregistertoseeifitexists.

*/

spin_lock_irqsave(&up->port.lock,flags);

up->capabilities=0;

up->bugs=0;

if(!

(up->port.flags&UPF_BUGGY_UART)){

/*

*Doasimpleexistencetestfirst;ifwefailthis,

*there'snopointtryinganythingelse.

*

*0x80isusedasanonsenseporttopreventagainst

*falsepositivesduetoISAbusfloat.The

*assumptionisthat0x80isanon-existentport;

*whichshouldbesafesinceinclude/asm/io.halso

*makesthisassumption.

*

*Note:

thisissafeaslongasMCRbit4isclear

*andthedeviceisin"PC"

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

当前位置:首页 > 经管营销 > 经济市场

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2