Contribute
Register

[Guide] 10.11+ USB changes and solutions

Status
Not open for further replies.

RehabMan

Moderator
Joined
May 2, 2012
Messages
181,112
Motherboard
Intel DH67BL
CPU
i7-2600K
Graphics
HD 3000
Mac
  1. MacBook Air
Mobile Phone
  1. iOS
Overview

In 10.11, Apple has completely rewritten the USB drivers. The new drivers appear to employ the use of ACPI to a much greater extent than the previous drivers. This puts more pressure on ACPI (DSDT) to be correct. If it is not correct, ports that exist may not be enabled by the new drivers.

In fact, even Apple's own products were found to be incorrect, so Apple created a way to override DSDT with a port injector. In the event properties are injected to define the available ports, these properties are used instead of ACPI.

The port injectors that are built-in can also interfere with USB on a hack as it is unlikely the USB topology for a given Mac model is the same as your PC.

To work around these problems, there are potentially several steps that need to be taken.

Note: This guide is for Intel USB3. Third party USB3 such as Asmedia, NEC/Renasas are problematic. No solution for these controllers AFAIK. Please do not ask about it in this thread.

Update: For unsupported USB3 controllers (eg. not Intel, not Fresco Logic), you can try the latest GenericUSBXHCI.kext: https://github.com/RehabMan/OS-X-Generic-USB3


Overview of the tools/solutions

There are a few methods/kexts/techniques for dealing with it all.

  • Choosing correct BIOS options
  • Renaming EHCx->EH0x in DSDT
  • using FakePCIID_XHCIMux to route USB2 devices on XHC to EHCI
  • injecting ports with a custom port injector or custom SSDT for USBInjectAll.kext
  • injecting ports with USBInjectAll.kext
  • patching DSDT to simulate a version of Windows when running OS X

USBInjectAll.kext and a few other goodies are available here: https://github.com/RehabMan/OS-X-USB-Inject-All. If you need various files in the repo, download the ZIP: https://github.com/RehabMan/OS-X-USB-Inject-All/archive/master.zip. The built USBInjectAll.kext is available from the link provided in the README. Make sure you read it.

Note: It would be a good idea to read the USBInjectAll README, but probably not until reading this thread so you can understand the problems more clearly.

Unless your ACPI implementation is perfect, you should plan on creating a custom SSDT for USBInjectAll.kext. It is covered in my companion guide:
https://www.tonymacx86.com/threads/guide-creating-a-custom-ssdt-for-usbinjectall-kext.211311/


BIOS options

Configuration of the USB controllers is often dependent on BIOS settings. For most PCs, you should use XHCI "auto" or "smart auto".

These settings often affect code within the DSDT that deals with XHCI controller initialization, USB2 routing, and USB3 port enabling. Some DSDTs are very accurate and respond appropriately to the options selected (BRIX, for example), some not so much.


Disabling the default injectors

First step is to insure that the default injectors are not in effect. There are two classes of injectors:
  • Controller injectors: These match on Mac model (SMBIOS) and controller name in ACPI (EHC1/EHC2/XHC1).
  • Hub port injectors: These match on Mac model and locationID (which is based on the address of the controller and port).

The first class of port injectors are easy to disable with a DSDT patch. For most PCs, the XHCI controller (USB3) is named 'XHC', so it will not match on the built-in injectors which use 'XHC1'.

EHC1 and EHC2 are typically used by both Macs and PCs to identify the two EHCI controllers (USB2). By renaming EHC1 to EH01 and EHC2 to EH02, you can effectively disable the built-in port injectors that match the SMBIOS you're using.

Renaming can be accomplished with find/replace in your patched DSDT, or via Clover config.plist/ACPI/DSDT/Patches.

For example, on my Lenovo u430, I used the following patches to rename EHCx->EH0x:
Code:
# rename EHC1 to EH01
into device label EHC1 set_label begin EH01 end;
into_all all code_regex EHC1\. replaceall_matched begin EH01. end;
into_all all code_regex \.EHC1, replaceall_matched begin .EH01, end;

# rename EHC2 to EH02
into device label EHC2 set_label begin EH02 end;
into_all all code_regex EHC2\. replaceall_matched begin EH02. end;
into_all all code_regex \.EHC2, replaceall_matched begin .EH02, end;

Note: It is quite possible that the patches above may not catch all instances of EHC1/EHC2 in your DSDT, so you may have to change some references manually. A global search and replace is also a reasonable strategy.

For the Gigabyte BRIX, I didn't need to patch DSDT so I used Clover config.plist-based patches in config.plist/ACPI/DSDT/Patches:

Item 1:
Comment: change EHC1 to EH01
Find: <45484331>
Replace: <45483031>

Item 2:
Comment: change EHC2 to EH02
Find: <45484332>
Replace: <45483032>

Looking at it in Xcode, it appears as follows:
ehc_rename.png

You can copy/paste the patches to your own DSDT from the config_patches.plist provided at the USBInjectAll project (link provided above). Using config.plist to implement these patches is the easiest and most direct way.

Note: You also see "Item 0" here which is part of emulating Windows. Disregard for now, it will be discussed later.

You should also verify that XHCI is named XHC and not XHC1. A common fix for prior OS X versions was to apply the "USB3 Multiplex" patch. That patch should be avoided in 10.11, as it renames XHC to XHC1 which introduces the possibility of matching against built-in port injectors. There is also a much simpler way to implement USB2 routing in FakePCIID.kext and FakePCIID_XHCIMux.kext (discussed later).

There is no easy way to disable the built-in port injectors for a hub. If your SMBIOS choice has a built-in injector for it, and there are issues with devices on an internal hub due to the port injector, you will need to create your own hub port injector that overrides the default. You can override the default by setting a high IOProbeScore, much like the MacBookPro9,1-EH02-hub injector for the ProBook 4540s.

Note: USBInjectAll.kext also has two hub port injectors for EH01 and EH02 (port #1 only).


Need for port injectors

After the built-in port injectors are disabled by renaming, the drivers will use data from ACPI (the _UPC object) to determine which ports are available. You can read more about the _UPC object in the ACPI spec.

You can see which ports are active in the ioreg.
XHC_sp.png

Note: This is with my port injector kext active. The picture was much different without it... with many ports missing.

If the ports you need are not active, it is likely your DSDT is incorrect and has ports that are actually connectable but marked "not connectable" (first field of _UPC zero).

Although you can patch DSDT to change the data returned by _UPC, it is easier to build an injector that enables the missing ports. Since the controller objects are renamed, there is no possibility of conflict with built-in port injectors.

There are several example port injectors at my various repos:

https://github.com/RehabMan/Lenovo-U430-Touch-DSDT-Patch
https://github.com/RehabMan/Lenovo-Y50-DSDT-Patch
https://github.com/RehabMan/HP-Envy-DSDT-Patch
https://github.com/RehabMan/HP-Envy-K-DSDT-Patch
https://github.com/RehabMan/HP-ProBook-4x30s-DSDT-Patch

In the current projects, the injectors are not actually used. These projects also contain custom USBInjectAll configurations (via UIAC/RMCF in SSDT-HACK). You can read more about USBInjectAll configuration at the USBInjectAll README.

The data in IOACPIPlane can be valuable in determining the possible ports that might need to be enabled.

Here is an image from my u430's XHC in IOACPIPlane:
xhc_acpi.png

In fact, the port addresses are completely dependent on the XHCI device-id. USBInjectAll uses this to inject each port with the proper address.

Keep in mind the 15 port limit. You may have to test different versions of your port injector as the number of potential ports may not be within the 15 port limit. For example, you may have HS01-HS15, plus SSP1-SSP6. This would be 21 total ports (15+6). Just FYI... 21 ports is impossible with the current chipsets (the 8/9 series chipsets support a maximum of 14 ports, 6 that can be configured as USB3, which makes for 20 total ports... not sure why the extra port is there.

To test each port, you would need a port injector that covered HS01-HS09 and SSP1-SSP6 (total of 15), and a second for HS10-15 and SSP1-SSP6. Or you could eliminate the USB3 ports, and test SSPx and HSxx independently. You get the idea. Once you determine the actual ports that are active, you can customize the port injector.

USBInjectAll has special flags for disabling ports in groups. So, instead of creating two custom injectors, you can also use USBInjectAll.kext. Kernel flag -uia_exclude_hs will exclude all HSxx ports from being injected on XHC. And kernel flag -uia_exclude_ssp or -uia_exclude_ss will exclude SSPx or SSxx ports. Using these flags, you can test all ports without using the port limit patch. Keep in mind that if all your USB ports are on XHC, use of -uia_exclude_hs will likely keep your USB mouse/keyboard from working (USB3 keyboard/mice are not common). This is not a problem on a laptop as the built-in PS2 trackpad and keyboard are available. In order to gather information without a keyboard and mouse, you can use remote desktop from another computer on the LAN.

You can use USBInjectAll.kext to inject all ports possible for your USB controllers. With all ports injected, you can determine which ones are already used. There is also a patch available that can increase the port limit. Use only short term for determining which ports need to go into your port injector. Make sure you read the README for USBInjectAll.kext.

IMPORTANT! The port limit patch should not be used as a permanent solution. There is now clear evidence that using it causes other data beyond what is evidently a fixed size array to be clobbered. The result is strange behavior by the USB drivers when the port limit is exceeded.

Hub port injectors match on just the locationID, and SMBIOS. Since the locationID is derived from the port number and address of the controller (which is the same on Macs and PCs), it is not possible to disable a matching built-in port injector by renaming. Instead, the existing port injector must be overridden by using a higher IOProbeScore in the port injector kext. The u430 repo has an example of a hub port injector as it was needed when using FakePCIID_XHCIMux.kext. And the ProBook repo has an example of a hub port injector that overrides a built-in hub port injector using a higher IOProbeScore.

Note that there is a 15-port limit for each controller/hub. That is, you can define only 15 total ports in each injector personality. Typically, this is not an issue except for on the XHCI controller, where the limit is much easier to reach. See the discussion below on FakePCIID_XHCIMux because FakePCIID_XHCIMux can be used to move some of those ports to EHCI, which eliminates the limit as a problem.

Most of the entries in Info.plist for a port injector kext are "boiler plate." Recommend you use one of the examples and modify to suit your own hardware, SMBIOS, etc.

Some notes on the port injector properties:
  • port-count: Poorly named, as it is not a 'count' at all. Instead it is the maximum port address (as specified by 'port')
  • UsbConnector: describes the connector type of the USB port. Common values are 0, 3, and 255, (0: USB2 type-A, 3: USB3 type-A, 255: proprietary). More information in the ACPI spec (_UPC).
  • port: must match the port _ADR in DSDT. You can see this value in IOACPIPlane


_OSI and Windows version checks

ACPI code can use the _OSI method (implemented by the ACPI host) to check which Windows version it is running on. Most DSDT implementations will vary the USB configuration depending on the version of Windows that is running.

When running OS X, none of the checks DSDT might be doing for _OSI("Windows <version>") will return true because it only responds to "Darwin". This is the reason for the "OS Check Fix" family of DSDT patches. By patching DSDT to simulate a certain version of Windows when running Darwin, we can obtain behavior that would normally happen when running that specific version of Windows.

For 10.11, the correct Windows version simulation is system dependent. Some computers will need "Windows 8" ("Windows 2012" some "Windows 7" ("Windows 2009") and others "Windows Vista" ("Windows 2006"). Experimentation may be necessary.

ACPI patching is covered here: http://www.tonymacx86.com/yosemite-laptop-support/152573-guide-patching-laptop-dsdt-ssdts.html

If you don't need to patch DSDT (desktops), you can accomplish this fix with config.plist/ACPI/DSDT/Patches and a small SSDT.

For example, for the BRIX, I use the following patch to map _OSI to XOSI:
Comment: change _OSI to XOSI
Find: <5f4f5349>
Replace: <584f5349>

This is what it looks like in Xcode's plist editor:
xosi.png

I have extracted the relavant part from SSDT-HACK.dsl that is of general use for this purpose:
(the BRIX SSDT-HACK.dsl has a few other things in it)
Code:
DefinitionBlock ("", "SSDT", 1, "hack", "XOSI", 0)
{
    // All _OSI calls in DSDT are routed to XOSI...
    // XOSI simulates "Windows 2009" (which is Windows 7)
    // Note: According to ACPI spec, _OSI("Windows") must also return true
    //  Also, it should return true for all previous versions of Windows.
    Method(XOSI, 1)
    {
        // simulation targets
        // source: (google 'Microsoft Windows _OSI')
        //  http://download.microsoft.com/download/7/E/7/7E7662CF-CBEA-470B-A97E-CE7CE0D98DC2/WinACPI_OSI.docx
        Name(WINV, Package()
        {
            "Windows",              // generic Windows query
            "Windows 2001",         // Windows XP
            "Windows 2001 SP2",     // Windows XP SP2
            //"Windows 2001.1",     // Windows Server 2003
            //"Windows 2001.1 SP1", // Windows Server 2003 SP1
            "Windows 2006",         // Windows Vista
            "Windows 2006 SP1",     // Windows Vista SP1
            //"Windows 2006.1",     // Windows Server 2008
            "Windows 2009",         // Windows 7/Windows Server 2008 R2
            //"Windows 2012",       // Windows 8/Windows Server 2012
            //"Windows 2013",       // Windows 8.1/Windows Server 2012 R2
            //"Windows 2015",       // Windows 10/Windows Server TP
        })
        Return (Ones != Match(WINV, MEQ, Arg0, MTR, 0, 0))
    }
}

The USBInjectAll.kext project repo also has the _OSI->XOSI patches and SSDT-XOSI.dsl which contains the code for XOSI shown above.

Normally, _OSI calls would be handled by OS X (the ACPI host), but via the patch, _OSI calls are routed to XOSI so a particular version of Windows can be simulated.

The version of XOSI above implements a simulation of "Windows 2009" (Windows 7), which I found to work well with the BRIX. You could emulate any other version of Windows by modifying the code as needed (either commenting or uncommenting the Windows versions listed).

The full BRIX repo/config.plist/SSDT-hack.dsl, etc is here: https://github.com/RehabMan/Gigabyte-BRIX-s-DSDT-Patch


XWAK, XSEL, and ESEL

Commonly these three methods are in DSDT and tend to manipulate XHCI registers having to do with USB2 port routing from the XCHI controller.

USB2 port routing was created to provide backward compatibility for operating systems without proper XHCI drivers. By routing USB2 ports on XHCI to the EHCI controller, operating systems with only USB2 (EHCI) drivers can still work on computers with USB3 ports (at USB2 speed, of course).

This routing is often controlled in XSEL and ESEL. ESEL is called when older versions of Windows are detected, and XSEL is called when newer versions of Windows are detected. The circumstances of when one is selected over the other are system dependent.

XWAK is called from _WAK upon wake from sleep. If you find your ports not working after sleep, it could be because XWAK did something to the XCHI controller registers that the 10.11 drivers didn't expect. So it can be useful to disable XWAK.

For example, change from:
Code:
Method (XWAK, ...)
{
    ... original code ...
}

to:
Code:
Method (XWAK, ...)
{
    Return(0)
    ... original code ...
}

The return causes immediate exit from the method avoiding whatever actions it may take otherwise.


USB2 port routing(multiplex) and FakePCIID_XHCIMux

Prior to 10.11, certain computers benefited from using the Multiplex patch, first described by Mieze. It was a complex patch that was not easy to adapt to different computers due to differences in DSDT code.

The multiplex patch should not be used on 10.11.

To accomplish essentially the same thing, I created FakePCIID_XHCIMux.kext (FakePCIID.kext also required). The kext can force USB2 devices on XHCI to be routed to the EHCI controller.

This is useful for two reasons:
- some devices may behave better when connected to EHCI
- to work around the 15 port limit that is present in the new USB drivers

Although the 15 port limit is unlikely to have an impact on either of the EHCI controllers (EH01/EH02) or even on a hub (most internal hubs seem to consist of 8 ports), it can be a problem on the XHCI controller. Because each USB3 port on the XHCI controller are actually two ports in one, the limit can be reached quite easily.

Imagine a motherboard with six (6) USB3 ports on XHCI. Each port has both a USB2 component and a USB3 component, for a total of 12 ports. In addition, the XHCI controller may have several USB2 only ports (typically internal motherboard headers). It would not be atypical to have 6 USB3 ports and 8 USB2 ports on XHCI (14 real ports total), for a total of 20.... 5 ports over the limit.

But the USB2 ports can be routed from XHCI to EHC1 using a feature of the XHCI controller. FakePCIID_XHCIMux takes advantage of this feature and forces this routing to take place. It can block any attempt by the XHCI driver in OS X to change the routing configuration. In our example, it can move some of the 14 USB2 ports from XHCI to EHCI leaving room for the other ports on XHCI still staying within the 15 port limit. The BIOS imposes limitations on this routing by setting various XHCI registers (PR2M) that are honored by XHCIMux. So the ports that move may vary by BIOS/motherboard.

It is also much easier to deploy than the multiplex patch: Simply install FakePCIID.kext and FakePCIID_XHCIMux.kext.

But the ports that are moved to EH01 (often a hub on port 1 of EH01), are still subject to the issues previously discussed regarding port injectors. If your DSDT does not return the correct _UPC for these ports (or hub ports), then you need an injector to fix it.

FakePCIID is available from my github. As always, read the README.

https://github.com/RehabMan/OS-X-Fake-PCI-ID


Summary

The quick overview looks something like this:

  • DSDT contains information on USB ports, and ports on built-in USB hubs
  • DSDT may be wrong
  • Apple may have found DSDT wrong in several Apple products, so they implemented a mechanism to override DSDT (port injectors)
  • The Apple port injectors are matched on ACPI name of the controller and SMBIOS
  • The Apple port injectors will override your DSDT (correct or not) if the port injectors match
  • Renaming the controllers to prevent a match can be used to avoid the Apple provided port injectors, in which case, the system falls back on DSDT (which still may be wrong)
  • After the ports are renamed, should your DSDT provide inaccurate details about the USB port topology, you can create a port injector just like Apple did for their own computers (you could also fix DSDT _UPC, but it is easier to do a port injector)
  • USBInjectAll.kext can be used to automatically inject all ports possible for each controller/hub.
  • Regardless, there is a limit of 15 ports per controller (or hub)
  • The 15-port limit can be increased with a patch. The patch may be dangerous, so it is not for long term use.
  • USB3 ports count as two when both the USB2 and USB3 component are routed to the XHC controller
  • USB2 ports can be routed from XHC to EHC via registers on the XHC chip
  • The built-in drivers have support for this routing (muxing), but it is not well understood (there is a relationship between special DSDT methods and USB port injector data)
  • FakePCIID_XHCIMux.kext can be used to force the USB2 component on XHC to EHC
  • FakePCIID_XHCIMux.kext can block writes to the USB2 routing registers when such writes occur in the OS X USB drivers.
  • BIOS settings affect what FakePCIID_XHCIMux does (it honors BIOS setting for the USB2 port routing mask)
  • FakePCIID_XHCIMux.kext cannot block writes that occur from ACPI space (evidently AppleACPIPlatform.kext doesn't go through IOPCIDevice to do these writes)
  • As a result, it is possible to have DSDT breaking things as it relates to USB2 port routing, especially on wake from sleep
  • Such writes to the port routing registers from ACPI space can be fixed by ACPI patches, or sometimes by simulating a certain version of Windows (_OSI->XOSI mapping)

So... solutions employed depend on what conditions are actually happening:
  • BIOS settings for USB2 port routing on XHC ("auto" vs. "smart auto" vs. "enabled")
  • renaming EHCx->EH0x to avoid built-in port injectors
  • FakePCIID_XHCIMux to enforce USB2 port routing on XHC
  • DSDT patches to simulate Windows (various versions) when running Darwin
  • DSDT patches to avoid writes to port routing registers on XHC (nullifying XWAK, ESEL, XSEL)
  • custom port injector kexts to match actual port configuration
  • using USBInjectAll.kext to get things working so the actual ports can be determined

Specific Recommendations (older than 7-series)

Before 7-series, there is no Intel XHCI controller. Only USB2 problems addressed here.

Do the EHCx->EH0x rename. It is a no-brainer first step.

For 5-series, use FixUSB_1000 in config.plist/ACPI/DSDT/Fixes.

If you still have ports not working, install USBInjectAll.kext.

Windows version emulation is unlikely to matter, but if you still have an issue, it is always something to try. the _OSI->XOSI + SSDT method is the easiest to apply.


Specific Recommendations (7-series, 8-series, 9-series, X99)

Do the EHCx->EH0x rename. It is a no-brainer first step.

For 7-series, 8-series, and 9-series, install FakePCIID_XHCIMux.kext (FakePCIID_XHCIMux has no effect without FakePCIID.kext, and is valid only for the Intel chipsets mentioned).

For many boards, assuming _UPC is correct on XHC, that will have everything working.

But two items at this point may cause problems:
  • Your DSDT may be disabling USB3 due to improper Windows emulation. Apply the XOSI fix to address this.
  • Built-in hub port injectors for your SMBIOS may be interfering. Install USBInjectAll.kext and use kernel flag -uia_exclude_xhc. This will enable the USBInjectAll port injectors for EH0x and the associated hubs, overriding the built-in hub port injectors, but keep DSDT control of XHC.

If there are some ports still not working, remove -uia_exclude_xhc kernel flag. If your XHC has more than 15 ports (device-id is 8086:8xxx), you will need the port limit patch, or will need to divide your testing into stages (using -uia_exclude_hs, -uia_exclude_ss, -uia_exclude_ssp).

Note: For X99 systems, the native Intel XHC drivers do not support 8086:8d31. You need an injector kext to enable it. See USBInjectAll.kext README for details.

Specific Recommendations (200-series)

As of 10.12.3, there is no chipset support for the XHC controller on 200-series chipsets. As a result, we need an injector kext to load the 100-series driver. Use XHCI-200-series-injector.kext as per USBInjectAll README.

Also, see additional information for 100-series (below)...


Specific Recommendations (100-series)

There is no EHCI controller in 100-series, so several of the solutions (and problems) for 7/8/9-series don't apply to 100-series.

Install USBInjectAll.kext. You will need the port limit patch as well as 100-series injection will always inject more than 15 ports.

From there, customize USBInjectAll using an SSDT with UIAC/RMCF. See USBInjectAll README. It is worthwhile to seek out an SSDT specific for your board that an expert has already created.

Once you have all ports injected, test them, determine which ones you can live without and exclude them with kernel flag uia_exclude. Kernel flag uia_exlude will exclude ports even from a custom USBInjectAll configuration via RMCF. After enough ports are excluded to bring the total ports under 15, you can eliminate the port limit patch.


Note about System Information

The System Information app in OS X can display information about the USB devices and the controllers they are connected to (System Information -> USB).

The system displays USB devices connected to EHCI#1 or ECHI#2 as "USB 2.0 Bus" and any device connected to XHCI as "USB 3.0 Bus".

Do not be shocked if you see USB2 devices under "USB 3.0 Bus". Without FakePCIID_XHCIMux, USB2 devices plugged into a port handled by XHCI will be shown as "USB 3.0 Bus". XHCI can handle both USB3 and USB2 ports.

Do not be shocked if you see USB2 devices plugged into USB3 ports under "USB 2.0 Bus". With FakePCIID_XHCIMux, the USB2 component of any USB3 port is routed to EHCI.


Problem Reporting

If you have a problem, please describe the problem clearly, make sure your profile accurately describes your hardware and provide all the data as requested in the FAQ.

Read FAQ, "Problem Reporting"
https://www.tonymacx86.com/threads/faq-read-first-laptop-frequent-questions.164990/


Background information

There is a lot of information here (and a lot of noise):

http://www.insanelymac.com/forum/topic/306777-guide-usb-fix-el-capitan-1011/

Note: The use of so-called dummy kexts in this scenario is not recommended. They are not needed here like they are needed in the AppleHDA case.
 
Last edited:
Very good writeup! Clarifies things about the port injectors and such - will be implementing :thumbup::clap:
 
Two possibilities (as you didn't provide complete details)...


For example on my 4530s, I found the port injectors for MacBookPro8,1 lined up well, and I didn't have to do anything (no rename required).

I have a USB3 slot on my 4530s. It doesn't show up in System properties.

I have a two-slot USB3 Express card that eventually is recognized and will (after much blinking) mount a USB3 thumbdrive.

However, the beach ball spins if I try to access a file.

And it takes a long time to eject it.

I gather there is no solution yet for USB3 functionality. Is there anything I can experiment with?

Thanks for the detailed post.
 
since 10.11 requires diffrent dsdt fixes than Yosemite dual booting same drive partition may cause problems?(since it shares same EFI)?
 
Status
Not open for further replies.
Back
Top