Terms of Endearment
There's a lot of tech jargon in this process - so let's go over some terms and their meanings!
When I first started talking with people about this process, I got some questions as to what I mean when I say "mapping USB ports". Well, what I'm referring to is the actual process of plugging USB devices into physical ports, and taking note of where they show up in the USBMap script or IORegistryExplorer.
The way USBInjectAll.kext performs its magic is by injecting the needed information for all possible USB ports for your chipset at once, regardless of whether your motherboard physically has those ports wired up (USB+Inject+All - I guess it was named that way for a reason). Looking at the USBMap Discovery or IORegistryExplorer output and saying "those are my USB ports" without first pruning that list is a bit like pointing your finger at a map of California and saying "there's California." It's a spot that represents California, but it may not be exactly 1:1 equivalent with the real California. There may be roads left out, or things shown on the map that are no longer there - or just generic assumptions made. In the same fashion, USBInjectAll just gives us a starting representation of which ports are possible for our chipset, and it's our responsibility to track down which are actually there, and which we plan to keep.
Let's check out a hypothetical example (put on your science caps, kids!) - let's say USBInjectAll injects USB ports 1-4 and they look like this in IOReg or the USBMap script (note, the names of the ports are arbitrary in this example):
- Port #1
- Port #2
- Port #3
- Port #4
Then we plug a USB 2 flash drive into the front USB port of your case and our USB ports now look like this:
- Port #1
- Port #2
- Flash Drive
- Port #3
- Port #4
We have successfully "mapped" the USB 2 port on the front of your case to "Port #2". We would then carry on testing other ports to "map" them as well. ProTip: If anyone asks what you're doing while you map your USB ports, it's perfectly acceptable to say "I'm a cartographer, and I'm busy."
These two refer to two different USB "controllers" - they act as brains that handle the USB ports of your motherboard. EHCI is a USB 2 controller, and XHCI is a USB 3 controller - but what's super neat is they're both backward compatible (excluding some cases where XHCI compatibility mode struggles with older devices - this is why we can't have nice things). That means that XHCI can also handle USB 2 devices :O
But... who cares?
Well, knowing which controller(s) your motherboard has is an important part of mapping out your USB ports. The following serves as a general rule of thumb:
- If you have a Skylake (series-100) or newer motherboard, you only have XHCI, no EHCI for you!
- Intel dropped the EHCI controller with the release of Skylake - it caused some issues for Windows 7 too as that doesn't have XHCI drivers during installation - so no USB until the OS is loaded and USB 3 drivers are installed :O
- XHCI's got a slick compatibility mode to work with USB 2 - but some older devices can have issues with that, although in my general experience that seems to be rather rare
- If you have Broadwell or earlier, and have USB 3 support, then you likely have EHCI and XHCI
- If you only have USB 2 (and likely some rather old hardware), then you probably only have EHCI, or possibly older controllers (I'm lookin at you, UHCI and OHCI)
Knowing which controller(s) your motherboard has can play into how many ports you have vs those that you can enable as we'll see in the USB Port Limit section below.
You've probably heard this term before - or maybe its close cousin the USB Port Limit Increase Patch. Whether you have or not, let's give it some backstory!
Back in the days of Yosemite, we were spoiled. Hackintoshes roamed free in the tech fields, grazing lazily on the abundant USB ports that sprouted from the fertile ground... Then El Capitan showed up - touting that mouse cursor trick where it gets bigger when you wiggle it around a bunch (and uh.. probably other useful features), and we Hack Ranchers gathered up our livestock and trotted to the greener pastures of 10.11 - little did we know, though, but Apple snuck something in the code that would prove to be a thorn in our sides for OS versions to come...
There were some major under-the-hood changes to USB from 10.10 to 10.11 - and the two main effects they had were as follows:
- Some USB ports needed to be injected (i.e. necessary data was supplemented to make them visible to the system)
- There was a limit to the number of ports the system can see at one time - and that limit is 15 ports per controller
That per controller part is what I was talking about in the EHCI and XHCI section - if you have multiple controllers, that can drastically increase the number of ports the OS can see at any given time.
What do you even mean by "see"? Hate to break it to you, but operating systems don't have eyes... or do they?
Well, when the OS starts communicating with the USB controllers, it keeps track of how many ports it has initialized and stops after hitting 15. I don't mean it will still use them if you really want it to, or ask suuuper nice - I mean it stops cold, casts a dismissive glance at whatever ports remain on that controller, shakes its head disapprovingly and walks away. Cold shoulder.
But wait, I don't even have 15 USB ports on my motherboard + case - so uh... why should I care?
This part is a bit confusing at first, and requires us to look at the Anatomy Of A USB 3 Plug per the following picture (look away if you're squeamish):
Courtesy of USB.com <3
You see, every USB 3 port has split personalities! See those 4 pins at the top? Those pins are used to represent the USB 2.0 personality of the USB 3 port. And those 4 coupled with the 5 at the bottom (for a total of all 9, if you're counting along) - those babies make up all the connecty-parts for USB 3! That means that each physical USB 3 port is actually two separate ports. Need a bit of a hands-on example? Me to, so let's look at the USB ports for my Asus Maximus X Code quick via the spec page:
Since my mobo is a Z370 - which is newer than Skylake (series-100), the only controller USBInjectAll is gonna be slathering with ports is XHCI (look back at the EHCI and XHCI section if you're lost as to how I came to that conclusion).
Time to get out your calculators, folks! Let's go through the ports listed for my motherboard, and tally up how many physical ports we truly have (remember that each USB 3 port has 2 personalities - so it's really a USB 2 port and a USB 3 port sharing one spot - Dr. Jekyll and Mr XHydeCI):
1 x USB 3.1 = 2 port
2 x USB 3.1 = 4 ports
6 x USB 3.1 = 12 ports
6 x USB 2.0 = 6 ports
Add them up = 24 ports
Well... that's a lotta ports. Let's go deeper and check out what USBInjectAll shows the OS with my motherboard. The following table is going to have some new info - so I'll go through that first.
The way USBInjectAll injects ports for macOS is a bit less intuitive than it's laid out in my motherboard spec page. It sees them as follows (long-list incoming):
Yeah, looking at that hefty list of ports can really give you a whole lot of I don't know what's going on feelings - so let's break that down a bit.
You can almost think of those prefixes as titles for your ports. Mr. and Mrs. USB if you will. They truly could be named anything in your ACPI, but USBInjectAll (bless its heart) does its best to standardize them for us - which subsequently makes scripting/automating this process much more predictable. The titles and numbers of ports that I have on my motherboard are as follows:
HS01 -> HS14- these 14 ports represent USB 2.0 ports on the XHCI controller (HS stands for High Speed)
USR1 -> USR2- Initially I thought these would represent the ASMedia USB 3.1 controllers - but I later located those under some generic PXSX device names in ioreg. I still have yet to see the USRx personalities populated.
SS01 -> SS10- these 10 ports represent USB 3(.1) ports on the XHCI controller (SS stands for Super Speed)
So, that means that macOS is presented with 26 total ports for my motherboard's chipset when all personalities are injected by USBInjectAll (remember, this may not reflect the physical number of ports available). That is a whopping 11 ports over the 15 port limit! It's also worth noting that HSxx ports are typically listed first, followed by USRx ports, followed by SSxx ports - which explains why USB 2 ports often work, even with the 15 port limit in effect.
When the dust finally settled on the Hackintosh Ranch after the great USB tornado of `11 swept through, a lone gunslinger stepped into town to give the ports back to the people - by any means necessary. He paced the streets, gunning down any that opposed his wild and open USB policies!
His name? The Port Limit Patch.
As the masses were scraping for any solution to their missing beloved ports, some bright minds reverse engineered the AppleUSBXHCI.kext (it's inside /System/Library/Extensions/IOUSBHostFamily.kext/Contents/PlugIns for the curious) and found that the port limit could be patched to allow more ports!
The crowd goes wild! Cheers can be heard from miles away - "We can use all our ports again!"
Well... nothing's ever that easy, is it? The patch lifts the hardcoded 15 port limit in one of just many places... and furthermore, there's other problems brought on by the patch - as RehabMan himself put it:
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.
That's some heavy stuff. But, if you had the same mindset I did when that info was found, you probably saw the No More Port Limit part and the rest just kinda fizzled out.
More time passes, and 10.12 comes out - the old port limit patch no longer works.
Oh... okay. That's not good. More minds plow through the code and figure out a new patch! Yay! There was much rejoicing! This pattern continues as so:
10.13.0 comes out - new patch, 10.13.4 - new patch, 10.13.5 - New Patch, 10.13.6 - NEW Patch, 10.14.0 - NEW PATCH, and then 10.14.1 - no consistently working patch is found (at least at the time of this writing).
I felt a great disturbance in the Force... as if millions of voices suddenly cried out in terror and were suddenly silenced. I fear something terrible has happened.
When the patches ceased to work for me, I decided to dive headfirst into all this, and my goodness was there a lot of information out there. This coupled with the fact that things can get very technical, and aren't terribly straightforward for most end-users prompted me to work on a script to automate the creation of an Injector Kext or SSDT-UIAC (lolwut? Yeah, we'll go over what those mean next!) - and also put together this guide.
Let's go over these bad boys one at a time to try and outline the purposes, as well as advantages/disadvantages of each.
Let me set the stage for you:
You've followed the guide through and through. You've mapped all the ports on your Skylake motherboard that you could ever use. You're a USB Master. Your keen mind traces over your progress - HS01, HS03, HS04, HS05, HS06, SS01, SS03, SS04, SS05, SS06... you tally up the totals and find that you've got 10... 10 ports total? Sweet! You're under the 15 port limit, and that's great! So you think "I don't need this silly USBInjectAll.kext, my XHCI controller is skinny enough to skirt on by!" and you remove the kext and reboot...
"What... the... heck. Where did all my USB ports go?" You fire up IOReg/USBMap and see that you now only have HS01 and SS01... wtf.
Well, as it turns out, your motherboard and macOS don't fully understand each other without the Hot Properties USBInjectAll was supplementing.
If this sounds like you, then an Injector Kext could be the fix you need!
An injector kext is a "codeless kext" (meaning it only has an Info.plist, and no actual executable binary - more about them here) that is capable of adding some properties to different devices (cough cough - like the missing the hot props that we needed from USBInjectAll??). You can actually use an injector kext, populated with the needed properties for your physical ports to inject only those ports. This works without USBInjectAll as well!
Well, if your motherboard is like mine, then there are a bunch of ports already available to the system even without USBInjectAll. After removing USBInjectAll and checking, my board has the following ports visible on the XHCI controller:
- HS01 -> HS14
- SS01 -> I can't even see the rest because we're already over the limit
Ouch - that's more than 15... That means that injecting properties for more ports will do me a grand total of 0 good.
So... what can I do to get some precious USB 3 back up in this OS?
After some time forcibly introducing my face to my desk and spending some time reading a very glorious thread sent to me by MykolaG (Dracoflar, Khronokernel), it turns out that you can add and remove ports with only the injector kext!
USBInjectAll's default behavior is to just inject all the ports the controllers could ever support and let the ports fall where they may - as seen in the tech diagram below:
Actual Picture of USBInjectAll's Code
What many don't know is there's more it can do. In my specific case, where my motherboard is throwing ports left and right at macOS, and everything over 15 just falls flat - USBInjectAll can be used to remove unused ports.
"B-but wait.. how do?" You may find yourself asking, and there's actually a couple ways.
UIA is the prefix applied to many of the settings, values, what-have-yous that correspond to USBInjectAll (as you can see by my clever highlighting - it's just the acronym) - and if we browse through the github repo, we see some neat boot arguments (these can be applied either in the Clover boot menu, or the config.plist at Boot -> Arguments):
uia_include=: this flag allows you to pick specific ports to include
- Ports are separated by a comma. If you wanted to include HS01 and HS02, it would look like
uia_exclude=: this flag allows you to pick specific ports to exclude
- Ports are separated by a comma here as well. If you wanted to exclude HS01 and HS02, it would look like
-uia_exclude_hs: excludes all HSxx ports
-uia_exclude_ss: excludes all SSxx ports
-uia_exclude_xhc: disables injection on XHC
I'm aware that some of those args start with
-and some do not - that's not a typo. Using those boot args, we can add/remove ports as we see fit and the end result is that USBInjectAll will only inject the ports that we tell it to! But why do that? Well - with my motherboard, the OS is already on Port Overload, with > 15 ports visible out of the gates. If I actually want to use USB 3, I have to remove some of the other ports to make room for them to show up. Remember, the OS tends to see them HSxx [USB2] first, then USRx [ASMedia 3.1], then finally our good buddy SSxx [USB3] last - so we could remove some HSxx ports to shift things down and make room for SSxx ports while still staying at or under the 15 port limit.
That's neat, but who wants a string of boot args a mile long?
No one. Well, no one that I know at least. So, there's gotta be a cleaner way to set this all up, right? You bet your boots there is!
If we take a look at the SSDT-UIAC-ALL.dsl file on RehabMan's USB-Inject-All repo - we can see just how confusing the cosmos get. Let's put on our raincoats and dive through this thunderstorm together!
The name... "SSDT-UIAC-ALL.dsl"... What does that mean?
Well, an "SSDT" is a Secondary System Description Table - a subsection of DSDT (Differentiated System Description Table). It's a part of the glue that helps your OS communicate with your BIOS and hardware. "UIAC" comes from the USBInjectAll acronym we discovered before - but that little C part of it stands for Customization. The ALL part of the name just means this file has all the current chipset's USB ports mapped out (it carries the same data that USBInjectAll injects by default). A .dsl file is a disassembled ACPI Machine Language file (otherwise known as .aml). It's like some source code for your DSDT.
Neat... so what does it do?
This file, when compiled and placed into the EFI -> CLOVER -> ACPI -> patched folder can customize which ports USBInjectAll injects to the system. The SSDT-UIAC-ALL.dsl is intended to be used as a starting point, where you locate your chipset, figure out which ports you intend to keep, then remove everything else. Typically, when you edit it, you'll not use the -ALL suffix - as an edited version doesn't contain all the USB ports anymore, but the name is really only there for clarity - so you can keep track of what this specific file does.