Linux 那些事儿之我是EHCI主机控制器.docx
- 文档编号:9214800
- 上传时间:2023-02-03
- 格式:DOCX
- 页数:35
- 大小:28.18KB
Linux 那些事儿之我是EHCI主机控制器.docx
《Linux 那些事儿之我是EHCI主机控制器.docx》由会员分享,可在线阅读,更多相关《Linux 那些事儿之我是EHCI主机控制器.docx(35页珍藏版)》请在冰豆网上搜索。
Linux那些事儿之我是EHCI主机控制器
Linux那些事儿之我是EHCI主机控制器
Linux那些事儿之我是EHCI主机控制器
Linux那些事儿
系列丛书
之
我是EHCI
1原文为上的《linux那些事儿之我是EHCI》,有闲情逸致的或者有批评建议的可以
到上面做客,也可以email到@
第1页共22页
Linux那些事儿之我是EHCI主机控制器
目录
目录..................................................................................................................................................2
引子..................................................................................................................................................3
接口体系...........................................................................................................................................3
套路................................................................................................................................................10
pcimatch和probe........................................................................................................................12
datastructureofehcidriveranddevice..........................................................................................14
2008年的这一场雪........................................................................................................................18
第2页共22页
Linux那些事儿之我是EHCI主机控制器
引子
转眼之间,到了2008年,先祝大家新年快乐,希望新的一年里好运连连,工资猛涨。
好久
没有写了,一个原因在于,作为一个PHD学生,难免要做一些读paper写paper的琐事,另
一个原因就是自己太懒了。
大哥甲一如既往,坚持的写作,着实让人钦佩。
此时此刻,我情
不自禁,作诗一首,北飘奇男子,江南大丈夫。
海上常常生明月,江湖就此一枝花。
不服不
行。
这里主要就linuxehcihostcontroller这部分的代码,谈谈我自己理解。
不当之处,请多指正。
参考资料,ehcispec和linux-2.6.22.1内核。
我尽量少贴代码,把问题讨论清楚。
现在开说,要进行usb传输,得有一个usbhostcontroller,usb主控制器。
它与插入系统的
USB设备进行相互操作,并负责处理USB设备与系统其它部分通信所必需的所有低层次
细节。
一个usb2.0主控器如图所示
usb2.0定义了低速(ls),全速(fs),高速(hs)传输。
EHCI仅仅支持高速传输,所以它必须还要
有一个companionHC,如(UHCI)来支持低速和全速设备,情况时这样的:
1),fs/ls设备插入到roothubport,会由companionHC(uhci/ohci)发现并管理设备;
2),fs/ls设备插入到usb2.0hub(notroothub),那么由ehci通过splittransaction和transanction
translation(tt)支持fs/ls设备。
比如,当一个usb设备插入roothubport时,先要做一件routing的事情。
所有的roothubport
默认是被EHCI占有的,所以,EHCI和插入的usb设备通信,看是不是hs设备,如果是好
说。
如果不是,EHCI就放弃这个port的占有权,让给companionHC(uhci/ohci)去管理。
接口体系
EHCI首先是一个PCI设备,我们可以lspci一下看看。
00:
1a:
7USBController:
IntelCorporationUSB2EHCIController#1(rev03)
第3页共22页
Linux那些事儿之我是EHCI主机控制器
我们与外围硬件打交道,可以把数据用in(out)指令传递给外围硬件,还可以把数据传输到cpu
和外围硬件共享的内存里面去。
这些都是计算机与硬件的接口。
(参见ldd3第9章)
那么我们的程序如何与EHCI联系,交流呢?
EHCI定义了三个接口空间。
如图
作为一个程序员,我们关心的是如何在代码中读/写这些地方的内容。
概念性的东西肯定是LDD3
写的最好,我就不赘述了。
1)pciconfigurationspace.(ldd3第12章)
由于EHCI是一个PCI设备,这里用于系统组件枚举和PCI的电源管理。
以x86为例,读取PCI总线套路是这样的。
我们要读取PCI总线上地址为add,长度为4个
字节的内容。
outl(add,0xcf8);\\先把add的out到地址为0xcf8的地方
value=inl(0xcfc);\\然后再读取0xcfc的内容
网上找到了一段程序,大家可以试验一下。
/*name:
pci.c*/
#include<stdio.h>
#include<assert.h>
#include<sys/io.h>
#defineIO_PORTS11/*ioport<=0x3ff*/
#defineIO_PORTS22/*ioport>0x3ff&&ioport<0xffff*/
#defineIO_PERMOFF0
#defineIO_PERMON1
#defineIO_PERMON23
#defineRW_DELAY10000/*delay100000microsecondsforreadingandwritin
gI/Oports.*/
#ifndefBOOL
第4页共22页
Linux那些事儿之我是EHCI主机控制器
typedefunsignedcharBOOL;
#endif
#ifndefBYTE
typedefunsignedcharBYTE;
#endif
#ifndefDWORD
typedefunsignedlongDWORD;
#endif
#ifndefINT
typedefunsignedintINT;
#endif
#ifndefULONG
typedefunsignedlongULONG;
#endif
#ifndefWORD
typedefunsignedshortWORD;
#endif
/*
**Function:
WritethevalueofthespecifiedI/Oportbygivingthelengthandth
e
**startingaddress.
**Parameter:
PortAddr:
theportaddress
**PortVal:
thevaluetoset
**size:
size=1forreading1byte,2forword,4fordoublewords
**Return:
1returnedifsuccess,or0returned
*/
BOOLSetPortVal(WORDPortAddr,DWORDPortVal,BYTEsize)
{
BOOLRet=0;
INTtmpRet=0;
ULONGnumperm=1;
INTprivilege=0;
assert(PortAddr>0);
if(PortAddr<=0x3ff)
{
tmpRet=ioperm((ULONG)PortAddr,numperm,IO_PERMON);
privilege=IO_PORTS1;
}
第5页共22页
Linux那些事儿之我是EHCI主机控制器
elseif(PortAddr>0x3ff)
{
tmpRet=iopl(IO_PERMON2);
privilege=IO_PORTS2;
}
else
returnRet;
if(tmpRet<0)
{
fprintf(stderr,"can'tsettheioportpermissionforsetting!
");
returnRet;
}
else
{
switch(size)
{
case1:
/*writeonebytetotheport*/
outb(PortVal,PortAddr);
break;
case2:
/*writeonewordtotheport*/
outw(PortVal,PortAddr);
break;
case4:
/*writedoublewordstotheport*/
outl(PortVal,PortAddr);
break;
default:
Ret=0;
break;
}
usleep(RW_DELAY);
Ret=1;
}
if(privilege==IO_PORTS1)
ioperm((ULONG)PortAddr,numperm,IO_PERMOFF);
elseif(privilege==IO_PORTS2)
iopl(IO_PERMOFF);
returnRet;
}
/*
**Function:
ReadthevalueofthespecifiedI/Oportbygivingthelenghtandth
e
**startingaddress.
第6页共22页
Linux那些事儿之我是EHCI主机控制器
**Parameter:
PortAddr:
theportaddress
**PortVal:
valuefromport
**size:
size=1forreading1byte,2forword,4fordoublewords
**Return:
1returnedifsuccess,or0returned.
*/
BOOLGetPortVal(WORDPortAddr,DWORD*PortVal,BYTEsize)
{
BOOLRet=0;
inttmpRet=0;
unsignedlongnumperm=1;
intprivilege=0;
assert(PortAddr>0);
assert(PortVal!
=NULL);
if(PortAddr<=0x3ff)
{
tmpRet=ioperm((unsignedlong)PortAddr,numperm,IO_PERMON);
privilege=IO_PORTS1;
}
elseif(PortAddr>0x3ff)
{
tmpRet=iopl(IO_PERMON2);
privilege=IO_PORTS2;
}
else
returnRet;
if(tmpRet<0)
{
fprintf(stderr,"can'tsettheioportpermissionforreading!
");
returnRet;
}
else
{
switch(size)
{
case1:
/*readonebytefromtheport*/
*PortVal=inb(PortAddr);
break;
case2:
/*readonewordfromtheport*/
*PortVal=inw(PortAddr);
break;
case4:
/*readdoublewordsfromtheport*/
*PortVal=inl(PortAddr);
break;
default:
第7页共22页
Linux那些事儿之我是EHCI主机控制器
Ret=0;
break;
}
usleep(RW_DELAY);
Ret=1;
}
if(privilege==IO_PORTS1)
ioperm((unsignedlong)PortAddr,numperm,IO_PERMOFF);
elseif(privilege==IO_PORTS2)
iopl(IO_PERMOFF);
returnRet;
}
intmain(intargc,char*argv[])
{
WORDadd_port=0xcf8;
WORDdata_port=0xcfc;
DWORDaddr=0x80000000;
DWORDport_value;
BYTEsize=4;
intinput;
printf("Pleaseselecttheoptionnumberasfollow:
");
printf("1--bus0:
dev:
0fun:
0asaddress0x80000000");
printf("2--bus0:
dev:
1fun:
0asaddress0x80000800");
printf("3--inputyourowndefinedaddressvalue:
");
scanf("%d",&input);
switch(input)
{
case1:
addr=0x80000000;
break;
case2:
addr=0x80000800;
break;
case3:
printf("pleaseinputthe32bitsaddressinHexformat(suchas8000
7800):
");
scanf("%x",&addr);
break;
default:
printf("inputinvalidoptionnum,exitprogram.");
return-1;
}
第8页共22页
Linux那些事儿之我是EHCI主机控制器
printf("Theaddris:
%X",addr);
printf("Theadd_portis:
%X",add_port);
printf("Thedata_portis:
%X",data_port);
if(SetPortVal(add_port,addr,size))
{
if(GetPortVal(data_port,&port_value,size))
{
printf("portvalueis:
%08X",port_value);
return0;
}
}
return-1;
}
打印出来的内容与
(1)用lspci-xxx命令输出;
(2)EHCIspec2.1章的内容对照一下。
好了,现在问题是我们怎么知道PCI总线上EHCI的地址add。
lspci可以看到所有PCI设备的
地址。
首先,EHCI不管有没有驱动,它这个PCI设备在PCI总线枚举时就被探测到了,这时
候它就被分配了地址。
每个PCI外设有一个总线号,一个设备号,一个功能号标识号。
比如
00:
1a:
7,00总线号,1a设备号,7功能号。
这些个号组成了独一无二的ID。
ID和地址的转
换关系是这样的:
我们只要ID,就知道了外设的地址,然后就可以读写PCI寄存器的内容。
另外可以看看
pci_read(),pci_write()\\arch\i386\pci\common.c
的内容,这样会有更深的理解。
2)regsterspace.
这是基于内的i/o寄存器,就是i/o内存。
这里包含了CapabilityRegisters和Operational
Registers。
我们可以读取/proc/iomem看看io内存的分配情况。
我们可以看到ehci的地址
是fe226400-fe2267ff。
这段内存不可以直接读写,先要调用ioremap(或是
ioremap_nocache)影射成虚拟地址后再使用。
我写了一段程序。
大家可以试验一下。
#include<asm/io.h>
staticinthello_init(void)
{
unsignedlongport_value,mem_value;
void__iomem*add;
inti;
printk(KERN_ALERT"Hello,world");
add=ioremap(0xfe226400,0x400);
for(i=0;i<100;i++){
第9页共22页
Linux那些事儿之我是EHCI主机控制器
mem_value=ioread32(add+i*4);
printk("%08Xmemvalueis:
%08X",add+i*4,mem_value);
}
iounmap(add);
return0;
}
以上是基于ldd3中那个最简单的模块hello.ko改的。
主要是为了可以在内核空间运行。
大家
可以把打印出来的内容与ehcispec2.2对照一下。
3)ScheduleInterfaceSpace.
这里就是普通的内存。
我们直接就可以访问它。
套路
子曰:
按套路出牌。
的确,什么东西都有套路,泡妞有泡妞的套路,花前月下不如花钱日下。
打麻将有打麻将的套路,少吃少碰少放炮,多摸多杠多发财。
星际有星际的套路,linux也
有linux的套路。
刘涛姐姐的故事再一次告诉我们,年龄不是问题,身高不是距离,有cai
就行。
我们不妨看看modprobeehci-hcd之后发生了什么事情。
ehci-hcd是一个驱动程序,不知您
记不记得我在sysfs中谈论过设备模型。
有两个重要的链表挂在bus上,一个是设备device
链表,一个是驱动driver链表。
每当我们向一根bus注册一个驱动driver时,套路是这样的:
driver_register(structdevice_driver*drv)->bus_add_driver()->driver_attach()->
bus_for_each_dev(drv->bus,NULL,drv,__driver_attach);
bus_for_each_dev遍历该总线上所有的device,执行一次__driver_attach(),看能不能将驱动
关联(attach)到某个设备上去。
__driver_attach()
->driver_probe_device()
->drv->bus->match(dev,drv),//调用bus的match函数,看device和driver匹不匹
配。
如果匹配上,
继续执行really_probe()。
->really_probe()
->driver->probe()。
(如果bus->probe非空,则调用bus->probe)
而每当我们向一根bus添加一个硬件时时,套路是这样的:
device_add()
\\device_add中有很多操作kobject,注册sysfs,形成硬件hiberarchy结构的代码。
如果您忘记了,先回头去参考参考"我是sysfs"
->bus_attach_device()->device_attach()->bus_for_each_drv()
bus_for_each_drv与bus_for_each_dev类似,遍历该总线上所有的driver,执行一次
__device_attach(),看能不能将设备关联(attach)到某个已登记的驱动上去。
__device_attach()
->driver_probe_device()//后面与上面一样
第10页共22页
Linux那些事儿之我是EHCI主机控制器
总结一些,一句话,注册一个某个bus的驱动就是先把驱动自己链入到bus驱动链表中去,
在从bus的设备链表中一一寻找,看有没有自己可以关联上的设备。
找到就probe,再把二
者bind起来。
反之,添加设备道理也是一样的。
好吧,我们还是看看modprobeehci-hcd后的事情。
一切从此开始,
module_init(ehci_hcd_init);
我们把不必要的预编译代码去掉后,ehci_hcd_init如下:
staticint__initehci_hcd_init(void)
{
intretval=0;
pr_debug("%s:
blocksizes:
qh%Zdqtd%Zditd%Z
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 那些事儿之我是EHCI主机控制器 那些 事儿 我是 EHCI 主机 控制器
![提示](https://static.bdocx.com/images/bang_tan.gif)