- UID
- 1544652
- 最后登录
- 1970-1-1
- 阅读权限
- 40
- 精华
- 主题
- 回帖
- 0
- 积分
- 1002
- PB币
-
- 威望
-
- 贡献
-
- 技术
-
- 活跃
-
|
本帖最后由 suhetao 于 2011-11-17 17:45 编辑
写在前面的话:鄙人是一个不折不扣的IT民工,整天“被沉浸”在无休无止的工作当中,这里写下的一切一切仅仅是忙里偷闲所致,如有不当,敬请原谅。
因为苹果操作系统完善的内存和磁盘管理,快捷迅速的任务响应机制,所以对这个操作系统情有独钟,至于苹果,史上最差的性价比,不敢恭维。一直都想让自己的笔记本安装上苹果操作系统,其实是因为近来在做I疯的一些开发工作,需要XCODE这一仅仅运行在MAC平台的开发工具。无奈,只能下个安装光盘镜像在4741G上安装,一切顺利,唯独睡眠唤醒问题一直很纠结。难道这是4741G的通病?通过对DSDT打EHCI补丁,依旧无效,一气之下删除了所有有关EHCI相关的ASL代码,却意外的可以睡眠了,但是却无法正常唤醒,唤醒症状是黑屏,但是各指示灯正常以及硬盘已经启动。船到桥头自然直,想起了变色龙的USB FIX选项,在org.chameleon.Boot.plist添加如下选项。
- <key>EHCIacquire</key>
- <string>Yes</string>
- <key>USBLegacyOff</key>
- <string>Yes</string>
复制代码 睡眠唤醒正常了。到底这两个神奇的选项为什么可以化腐朽为传奇,引起了鄙人的高度的重视。二话不说,下载了变色龙的源代码,稍稍分析了一下,整个变色龙项目编译以后成为一个boot的文件,因此猜测源代码中的boot.c文件类似于Main函数所在,不失所望。一切从boot函数开始。
- //==========================================================================
- // This is the entrypoint from real-mode which functions exactly as it did
- // before. Multiboot does its own runtime initialization, does some of its
- // own things, and then calls common_boot.
- void boot(int biosdev)
- {
- initialize_runtime();
- // Enable A20 gate before accessing memory above 1Mb.
- enableA20();
- common_boot(biosdev);
- }
复制代码 其中common_boot函数有以下一段
- if (ret <= 0) {
- printf("Can't find %s\n", bootFile);
- sleep(1);
-
- if (gBootFileType == kNetworkDeviceType) {
- // Return control back to PXE. Don't unload PXE base code.
- gUnloadPXEOnExit = false;
- break;
- }
- pause();
- } else {
- /* Won't return if successful. */
- ret = ExecKernel(binary);
- }
复制代码 我们想要的答案就隐藏在ExecKernel函数中, ExecKernel有以下这么一段
- ......
- usb_loop();
- ......
复制代码 usb_loop这个函数包含在变色龙源码usb.c文件中,内容如下
- // Loop through the list and call the apropriate patch function
- int usb_loop()
- {
- int retVal = 1;
- bool fix_ehci, fix_uhci, fix_usb, fix_legacy;
- fix_ehci = fix_uhci = fix_usb = fix_legacy = false;
-
- if (getBoolForKey(kUSBBusFix, &fix_usb, &bootInfo->chameleonConfig))
- {
- fix_ehci = fix_uhci = fix_legacy = fix_usb; // Disable all if none set
- }
- else
- {
- getBoolForKey(kEHCIacquire, &fix_ehci, &bootInfo->chameleonConfig);
- getBoolForKey(kUHCIreset, &fix_uhci, &bootInfo->chameleonConfig);
- getBoolForKey(kLegacyOff, &fix_legacy, &bootInfo->chameleonConfig);
- }
-
- struct pciList* current = usbList;
-
- while(current)
- {
- switch (pci_config_read8(current->pciDev->dev.addr, PCI_CLASS_PROG))
- {
- // EHCI
- case 0x20:
- if(fix_ehci) retVal &= ehci_acquire(current->pciDev);
- if(fix_legacy) retVal &= legacy_off(current->pciDev);
-
- break;
-
- // UHCI
- case 0x00:
- if (fix_uhci) retVal &= uhci_reset(current->pciDev);
- break;
- }
-
- current = current->next;
- }
- return retVal;
- }
复制代码 usb_loop函数通过getBoolForKey函数读取org.chameleon.Boot.plist文件中相关的USB FIX设定,然后遍历所有的EHCI设备,执行ehci_acquire,legacy_off这两个函数。(仅仅针对上面提到的“添加如下选项”的情况)所有的谜团已经解开,真相只有一个,先来看看legacy_off函数的源代码
- int legacy_off (pci_dt_t *pci_dev)
- {
- // Set usb legacy off modification by Signal64
- // NOTE: This *must* be called after the last file is loaded from the drive in the event that we are booting form usb.
- // NOTE2: This should be called after any getc()/getchar() call. (aka, after the Wait=y keyworkd is used)
- // AKA: Make this run immediatly before the kernel is called
- uint32_t capaddr, opaddr;
- uint8_t eecp;
- uint32_t usbcmd, usbsts, usbintr;
- uint32_t usblegsup, usblegctlsts;
-
- int isOSowned;
- int isBIOSowned;
-
- verbose("Setting Legacy USB Off on controller [%04x:%04x] at %02x:%2x.%x\n",
- pci_dev->vendor_id, pci_dev->device_id,
- pci_dev->dev.bits.bus, pci_dev->dev.bits.dev, pci_dev->dev.bits.func);
-
-
- // capaddr = Capability Registers = dev.addr + offset stored in dev.addr + 0x10 (USBBASE)
- capaddr = pci_config_read32(pci_dev->dev.addr, 0x10);
-
- // opaddr = Operational Registers = capaddr + offset (8bit CAPLENGTH in Capability Registers + offset 0)
- opaddr = capaddr + *((unsigned char*)(capaddr));
-
- // eecp = EHCI Extended Capabilities offset = capaddr HCCPARAMS bits 15:8
- eecp=*((unsigned char*)(capaddr + 9));
-
- DBG("capaddr=%x opaddr=%x eecp=%x\n", capaddr, opaddr, eecp);
-
- usbcmd = *((unsigned int*)(opaddr)); // Command Register
- usbsts = *((unsigned int*)(opaddr + 4)); // Status Register
- usbintr = *((unsigned int*)(opaddr + 8)); // Interrupt Enable Register
-
- DBG("usbcmd=%08x usbsts=%08x usbintr=%08x\n", usbcmd, usbsts, usbintr);
-
- // read PCI Config 32bit USBLEGSUP (eecp+0)
- usblegsup = pci_config_read32(pci_dev->dev.addr, eecp);
-
- // informational only
- isBIOSowned = !!((usblegsup) & (1 << (16)));
- isOSowned = !!((usblegsup) & (1 << (24)));
-
- // read PCI Config 32bit USBLEGCTLSTS (eecp+4)
- usblegctlsts = pci_config_read32(pci_dev->dev.addr, eecp + 4);
-
- DBG("usblegsup=%08x isOSowned=%d isBIOSowned=%d usblegctlsts=%08x\n", usblegsup, isOSowned, isBIOSowned, usblegctlsts);
-
- // Reset registers to Legacy OFF
- DBG("Clearing USBLEGCTLSTS\n");
- pci_config_write32(pci_dev->dev.addr, eecp + 4, 0); //usblegctlsts
-
- // if delay value is in milliseconds it doesn't appear to work.
- // setting value to anything up to 65535 does not add the expected delay here.
- delay(100);
-
- usbcmd = *((unsigned int*)(opaddr));
- usbsts = *((unsigned int*)(opaddr + 4));
- usbintr = *((unsigned int*)(opaddr + 8));
-
- DBG("usbcmd=%08x usbsts=%08x usbintr=%08x\n", usbcmd, usbsts, usbintr);
-
- DBG("Clearing Registers\n");
-
- // clear registers to default
- usbcmd = (usbcmd & 0xffffff00);
- *((unsigned int*)(opaddr)) = usbcmd;
- *((unsigned int*)(opaddr + 8)) = 0; //usbintr - clear interrupt registers
- *((unsigned int*)(opaddr + 4)) = 0x1000; //usbsts - clear status registers
- pci_config_write32(pci_dev->dev.addr, eecp, 1); //usblegsup
-
- // get the results
- usbcmd = *((unsigned int*)(opaddr));
- usbsts = *((unsigned int*)(opaddr + 4));
- usbintr = *((unsigned int*)(opaddr + 8));
-
- DBG("usbcmd=%08x usbsts=%08x usbintr=%08x\n", usbcmd, usbsts, usbintr);
-
- // read 32bit USBLEGSUP (eecp+0)
- usblegsup = pci_config_read32(pci_dev->dev.addr, eecp);
-
- // informational only
- isBIOSowned = !!((usblegsup) & (1 << (16)));
- isOSowned = !!((usblegsup) & (1 << (24)));
-
- // read 32bit USBLEGCTLSTS (eecp+4)
- usblegctlsts = pci_config_read32(pci_dev->dev.addr, eecp + 4);
-
- DBG("usblegsup=%08x isOSowned=%d isBIOSowned=%d usblegctlsts=%08x\n", usblegsup, isOSowned, isBIOSowned, usblegctlsts);
-
- verbose("Legacy USB Off Done\n");
- return 1;
- }
复制代码 这些到底是什么天书?配合一下INTEL发布的EHCI参考文档,其实都很简单。先上个图,让大家熟悉一下EHCI的组织架构
可以看到通过设备地址我们先到到达一个PCI CONFIGURATION寄存器的区域,而其中的USB BASE ADDRESS指向的就是MMIO地址,也就是CAPABILITY REGISTERS和OPERATIONAL REGISTERS.
再来几张图看看PCI CONFIGURATION寄存器以及CAPABILITY、OPERATIONAL REGISTERS的具体说明
以及
还有
其中EECP在HCCPARAMS寄存器中指定
最后是CAPABILITY、OPERATIONAL REGISTERS之间的关系。
有了这些说明图片legacy_off函数就很好理解了。首先legacy_off函数从设备地址的偏移量0x10读取USBBASE地址访问CAPABILITY REGISTER,然后CAPABILITY REGISTER偏移CAPLENGTH个长度就是OPERATIONAL REGISTER。有了所有寄存器的地址,就很好处理了。首先对USBLEGCTLSTS寄存器清零(EECP+4),相当于USBLEGCTLSTS寄存器RESET(默认全是0),延迟100毫秒等待寄存器RESET完毕,然后RESET USBCMD,通过对寄存器[0,1]两个位赋0值,通过设置USBINTR为0关闭USB中断,以及重置USB状态寄存器的USBSTS。剩下的就是读取一些信息。整个legacy_off函数的作用相当于重置USB设置。
再来看看ehci_acquire函数。代码如下
- int ehci_acquire (pci_dt_t *pci_dev)
- {
- int j, k;
- uint32_t base;
- uint8_t eecp;
- uint8_t legacy[8];
- bool isOwnershipConflict;
- bool alwaysHardBIOSReset;
- alwaysHardBIOSReset = false;
- if (!getBoolForKey(kEHCIhard, &alwaysHardBIOSReset, &bootInfo->chameleonConfig)) {
- alwaysHardBIOSReset = true;
- }
- pci_config_write16(pci_dev->dev.addr, 0x04, 0x0002);
- base = pci_config_read32(pci_dev->dev.addr, 0x10);
- verbose("EHCI controller [%04x:%04x] at %02x:%2x.%x DMA @%x\n",
- pci_dev->vendor_id, pci_dev->device_id,
- pci_dev->dev.bits.bus, pci_dev->dev.bits.dev, pci_dev->dev.bits.func,
- base);
- if (*((unsigned char*)base) < 0xc)
- {
- DBG("Config space too small: no legacy implementation\n");
- return 1;
- }
- eecp = *((unsigned char*)(base + 9));
- if (!eecp) {
- DBG("No extended capabilities: no legacy implementation\n");
- return 1;
- }
- DBG("eecp=%x\n",eecp);
- // bad way to do it
- // pci_conf_write(pci_dev->dev.addr, eecp, 4, 0x01000001);
- for (j = 0; j < 8; j++) {
- legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
- DBG("%02x ", legacy[j]);
- }
- DBG("\n");
- //Real Job: based on orByte's AppleUSBEHCI.cpp
- //We try soft reset first - some systems hang on reboot with hard reset
- // Definitely needed during reboot on 10.4.6
- isOwnershipConflict = ((legacy[3] & 1 != 0) && (legacy[2] & 1 != 0));
- if (!alwaysHardBIOSReset && isOwnershipConflict) {
- DBG("EHCI - Ownership conflict - attempting soft reset ...\n");
- DBG("EHCI - toggle OS Ownership to 0\n");
- pci_config_write8(pci_dev->dev.addr, eecp + 3, 0);
- for (k = 0; k < 25; k++) {
- for (j = 0; j < 8; j++) {
- legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
- }
- if (legacy[3] == 0) {
- break;
- }
- delay(10);
- }
- }
- DBG("Found USBLEGSUP_ID - value %x:%x - writing OSOwned\n", legacy[3],legacy[2]);
- pci_config_write8(pci_dev->dev.addr, eecp + 3, 1);
- // wait for kEHCI_USBLEGSUP_BIOSOwned bit to clear
- for (k = 0; k < 25; k++) {
- for (j = 0;j < 8; j++) {
- legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
- }
- DBG ("%x:%x,",legacy[3],legacy[2]);
- if (legacy[2] == 0) {
- break;
- }
- delay(10);
- }
- for (j = 0;j < 8; j++) {
- legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
- }
- isOwnershipConflict = ((legacy[2]) != 0);
- if (isOwnershipConflict) {
- // Soft reset has failed. Assume SMI being ignored
- // Hard reset
- // Force Clear BIOS BIT
- DBG("EHCI - Ownership conflict - attempting hard reset ...\n");
- DBG ("%x:%x\n",legacy[3],legacy[2]);
- DBG("EHCI - Force BIOS Ownership to 0\n");
- pci_config_write8(pci_dev->dev.addr, eecp + 2, 0);
- for (k = 0; k < 25; k++) {
- for (j = 0; j < 8; j++) {
- legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
- }
- DBG ("%x:%x,",legacy[3],legacy[2]);
- if ((legacy[2]) == 0) {
- break;
- }
- delay(10);
- }
- // Disable further SMI events
- for (j = 4; j < 8; j++) {
- pci_config_write8(pci_dev->dev.addr, eecp + j, 0);
- }
- }
- for (j = 0; j < 8; j++) {
- legacy[j] = pci_config_read8(pci_dev->dev.addr, eecp + j);
- }
- DBG ("%x:%x\n",legacy[3],legacy[2]);
- // Final Ownership Resolution Check...
- if (legacy[2] & 1) {
- DBG("EHCI controller unable to take control from BIOS\n");
- return 0;
- }
- DBG("EHCI Acquire OS Ownership done\n");
- return 1;
- }
复制代码 对PCI_CONFIGURATION偏移量0x04的地方写入0x0002,可惜没有主板的详细开发文档,而且INTEL提供的EHCI只提到说00−08h,Register implementation as needed for specific PCI device。所以具体作用不得而知,其实鄙人也不相信这个有啥用。然后剩下的部分就是EHCI的Ownership修复。
所以整体上看,只要进行Ownership修复,以及重置USB相关寄存器,就可以使HM55的睡眠唤醒正常。
若想详细了解以上所说函数中对USB相关寄存器地址以及设定的具体值含义请查阅INTEL官方的EHCI文档《Enhanced Host Controller Interface Specification for Universal Serial Bus 》,我就不再复述。今天就说到这里吧。抱砖引玉,希望大家少扔点鸡蛋。等这段子忙完看看,能否单纯在DSDT中添加相应的Ownership fix以及USB reset来修复HM55的睡眠唤醒问题,而不需要在变蛇龙引导参数中添加相关的USB设定。 |
评分
-
8
查看全部评分
-
|