Akaban操作系统(5)-----UHCI的初始化,A篇

续上一篇我没写的东西

1,UHCI主控器我咋初始化的

2,为什么****(某抗日剧穿山甲震怒)疯狂出现错误的中断

那么便从1开始,这一切还得从一片文档说起,看下图

上图便是挂在PCI上的UHCI的PCI配置空间,我们只需要将其读取,之后用图上的USBBASE这个值用in和out指令去读寄存器即可,实际上UHCI的PCI配置空间只有图上的USBBASE是可写的,其余的我在虚拟机上尝试过,全部会置位会0,在BIOS初始化期间,USBBASE的值会被BIOS分配好,所以,这里不会写这个寄存器的IO地址分配,当然这个寄存器的值不完全是它的IO地址的基址,在使用前必须对其进行处理,该寄存器具体内容如下图所示

哈,intel的文档和我一样不喜欢按顺序写,硬生生把这张图拖到了好几面之后,虽然它是英文的,但是根据有道(小道)的截屏翻译,我大致推测(因为有道的翻译实在牛头不对马嘴),但是我的推测应该是没错的(虽然前两篇就推测错了),在真机上是可以正常运行的

31-16位保留,必须为0

15-5位IO的基地址,可写,但是也是io基地值的15-5位,这意味着分配的io基地值4-0位必须为0

4-1位保留,必须为0

0位,和所有使用IO地址的设备一样,该位置位为1,表示使用的是IO空间

但如上文所述,该寄存器已经被BIOS提前分配好了,这里不会写如何分配(Pawloader注重于硬件开发,而不是算法优化),在kernal开发时会写PCI设备的热拔插,到时候便会写PCI的资源分配

以下代码便是pawloader对pci配置空间内的iobase处理

省去亿点代码...
	//根据UHCI design guid的第10面可知 pci_config_space的bar4存着uhci的io基地址
	//注意,这个值一般在开机时就已经被bios初始化了,但是目前不能确定efi启动后是否会为其分配io地址
	u32 bar = in_pci_conf32(dev,PCI_REG_BAR_4_);
	if(test_bit(bar,0)!=true) return false;	//如果不占用io资源,则不是该程序支持的uhci

	bar &= 0xfffffff0;	//把它的属性洗掉
	if(bar == 0)	return false;	//暂不支持对pci设备的资源分配
	hc_dev.io_base = (u16)bar; //记下io寄存器的地址

省去亿点代码...

而这个iobase的使用则需要根据design guide上所写的内容进行进一步开发

那么,io_base既然已经得到那么,便按照文档所述一个一个初始化,下图便是第一个寄存器在官方文档上的描述

文档全是英文,但是懂得都懂,不懂就有道直接截屏翻译,这里挑几个重点的翻

这个寄存器的位置一般在io_base+0(其实就是io_base)的位置,大小16bit(不确定是否需要16bit,16bit地读,保险起见,以16bit的大小读)

第2位,全局复位位,这一位复位后写回io口将会使UHCI复位,其效果与硬件上的复位键相当(不确定是否会对pci配置空间产生影响)

第0位,设备的开关位,将该位置位会让UHCI设备运行,复位回挂起UHCI设备,一般会在驱动程序完成各大IO端口寄存器的初始化后置位

所以为了这个寄存器我专门写了一个设置UHCI寄存器位的函数,如下所示

static void uhci_ioreg_op(struct hc_device *dev,u8 reg,u8 bit,bool op)
{
	u16 io_data = io_in2b(dev->io_base+reg);	//将控制寄存器读入
	if(op == BIT_SET) io_data = set_bit(io_data,bit);
	else io_data = reset_bit(io_data,bit);
	io_out2b(dev->io_base+reg,io_data);	//写回
	return;
}

上图中的hc_device定义在minefunction/PawLoader/include/akaban/usb.h中,可自行查看

reg是io地址的偏移量,在usb.h中有定义,design guide也有定义,bit指操作哪一位,op表示是置位还是复位,通过这个函数,便可以轻松地操作UHCI io寄存器中的任何一位,当然用是好用,用起来美观是美观,易读是易读,就是会拖慢运行速度,但是,没有关系,反正它只用在初始化的过程中,对性能影响不大

当然,bios既然会初始化PCI设备,自然也会使用它们,为了保证UHCI设备运行环境的干净(最初状态),pawlaoder会先将UHCI复位,如下所示

bool uhci_reset(struct hc_device *dev)
{
	//置位global reset位,使设备复位
	uhci_ioreg_op(dev,UHCI_REG_OFF_CMD,UHCI_CMD_GRESET_BIT,BIT_SET);
	//将PCI配置空间中bar4存的内容写回
	io_out4b(PCI_CONFIG_ADDR_PORT,PCI_ENABLE|(dev->bus<<16)|(dev->dev<<11)|(dev->fun<<8)|PCI_REG_BAR_4_);
	io_out4b(PCI_CONFIG_DATA_PORT,dev->io_base|1);
	//重启完毕
	return true;
}

之后是UHCI的status寄存器,其内容如下图所示

这个寄存器啊,可谓是相当的珍贵(穿山甲的害怕),只要读取这个寄存器便可以知道整个UHCI的状态,自然也出现了上一篇的内容,0x30,即第5,4位置位了,现在问题2解释清楚了

第5位置位,意味着UHCI挂起,可能是设备出现了错误,有可能是command寄存器中的rs位复位了

第4位置位,意味着UHCI中止了,这时候HCD必须将设备复位,这个错误可能是TD(后面会说)头有问题,无论是什么问题,也有可能是任何其他的问题,总之这一位置位了都不是什么好事

第3位置位,一般情况下是PCI的问题,无论是什么问题(比如PCI控制器出了问题),这一位置位了也不是什么好事

第1位置位,传输完成后TD结构体内的error位置位了,这个错误可以修复(指挂起设备),此时第0位也会置位

第0位置位,是我们最期待的,传输完成位,这一位置位时UHCI的传输任务完成,只需要我们验收成果,当然如果第1位置位,也不是好事(可能正在U盘正传着东西,但是不知道是哪个**给拔掉了)

对此专门设计了位检测函数,如下所示

static bool uhci_iobit_state(struct hc_device *dev,u8 reg,u8 bit)
{
	u16 io_data = io_in2b(dev->io_base+reg);
	return test_bit(io_data,bit);
}

这个函数各参数的含义与控制command寄存器的一致,不过少了一个参数,多了一个返回,这个返回如果是true则要求检测的位置位,如果为false则检测的位复位

这个函数不仅对uhci的status寄存器有效,对port寄存器(下文提及)也有效,这个函数仅在UHCI触发中断时检测status位

之后是UHCI的中断控制寄存器,,这个寄存器控制着UHCI的中断,都是置位使能,复位禁止,如下图所示

第2位置位,表示在传输完成后产生中断通知HCD

在上文我们把所有中断全使能了,当然这样做也误伤大雅,在这个寄存器上pawloader的操作如下所示(后来的代码有更改)

...
	uhci_ioreg_op(&hc_dev,UHCI_REG_OFF_INTR,UHCI_INTR_SPIE_BIT,BIT_SET);	
	uhci_ioreg_op(&hc_dev,UHCI_REG_OFF_INTR,UHCI_INTR_IOCE_BIT,BIT_SET);	
	uhci_ioreg_op(&hc_dev,UHCI_REG_OFF_INTR,UHCI_INTR_RIE_BIT,BIT_SET);	
	uhci_ioreg_op(&hc_dev,UHCI_REG_OFF_INTR,UHCI_INTR_CRCIE_BIT,BIT_SET);
...

没错,保持全部使能不变

之后是frame_num寄存器,其定义如下图所示

按照翻译(实际上是现场翻的),大致意思是这个寄存器作为frame_list的引索,16bit宽,并且明文规定16bit读,16bit写,这个寄存器实际上就是fram_list[n]这个链表中的中括号里的n,一般每1ms增加1

在初始化的过程中Pawlaoder会将其值写为0,如下所示

...
	io_out2b(hc_dev.io_base + UHCI_REG_OFF_FRAME_INDEX , 0 );
...

之后是frame_list基址寄存器,这个寄存器决定着TD头的位置,其内容如下所示

这个寄存器31-12位有效,对应着frame_list基地址的31-12位

但是11-0保留为0

总地来说这就是个用来填frame_list基址的寄存器,可写可读(仅32位读,不知道是否必须32位写),而且地址是在4G内存以内,4k对齐的地址,只要符合条件直接填,无需作任何处理(实际上要将4K的内存清0,之后准备TD表头,否则就****疯狂0x30),一般这个地址在linux中是内核从dma(直接内存访问)的内存池里分配来的,但是,这里Pawlaoder会用通用的内存分配函数分配地址给UHCI,毕竟这时候的Pawloader仅瓜分内存低1G的内存,而且由于特性限制,只能分配4K对齐的内存,完美符合分配条件,分配过程如下所示

...
    struct mapping_struct *tmp_map = mapping_mem(MEM_2M);	//为UHCI的FRAME LIST BASE 准备地址 
	io_out4b(hc_dev.io_base + UHCI_REG_OFF_FRAME_BASE,(u32)tmp_map->phys_ptr);	//设置帧列表	
	hc_dev.fra_list = tmp_map->line_ptr; //一般情况下开始这里会分配到0xa00000
	memset(hc_dev.fra_list,0,MEM_4K);
...

接下来是最后一个IO寄存器(实际上跳过了一个,这个设备复位后会有默认值)的介绍了(实际上是两个,但内容是一样的),在规范中如下图所示

这个寄存器存在两个分别控制着UHCI的两个口(按文档所述为两个,linux表示不确定是否会有更多,我也不知道有更多口该如何处理),既然文档说有两口那我便初始化这两口,先介绍一部分我们需要用到的位

第2位,端口使能位,这个位置位开启端口,复位关闭端口,这个位可以被某些错误状态改变,但是一但UHCI运行起来要关闭该端口回有一些延迟

第0位,电流连接状态位,如果这一位被置位,意味着这个端口上有电流通过,一般HCD会将它作为一个设备进行初始化(实际上如果只是插着个灯或风扇应该也会置位,但Pawloader可能会因为匹配不到合适的驱动程序或端口初始化错误而将该端口关闭,导致设备无法正常运转,当然下方代码没有体现),Pawloader对该寄存器的操作如下所示

...
	bool sts0 = uhci_iobit_state(&hc_dev,UHCI_REG_OFF_PORT1_STS,UHCI_PORT_DEV_PRE_BIT);	
	bool sts1 = uhci_iobit_state(&hc_dev,UHCI_REG_OFF_PORT2_STS,UHCI_PORT_DEV_PRE_BIT);	
	//适当关闭没连接设备的端口
	if(sts0 == false) uhci_ioreg_op(&hc_dev,UHCI_REG_OFF_PORT1_CMD,UHCI_PORT_ENABLE_BIT,BIT_RESET);
	if(sts1 == false) uhci_ioreg_op(&hc_dev,UHCI_REG_OFF_PORT2_CMD,UHCI_PORT_ENABLE_BIT,BIT_RESET);
	//显示端口状态
	driver_printk(PRINT_OK,DRIVER_NAME,"PORT STATE : %X %X \n",sts0,sts1);
...

既然io寄存器初始化已经搞定了,那么便开始td的初始化,当然一切UHCI的开发都不会偏离规范半点(因为规范又不是我定的),源码和TD的初始化下一篇讲,因为那玩意又够我写上个5000多字了,下一篇见。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值