The assignment was completely flawless.
`TD2 (STAT ) Status : 0x1C0007FF` means the Status IN stage returned exactly 0
bytes (`0x7FF` is USB-speak for a length of 0) and the Active bit cleared. The
keyboard accepted the command, updated its internal registers, and is now
exclusively listening on **Address 1**.
You have successfully detached the hardware from the shared default address.
### Phase 4: Waking the Interface (Set Configuration)
Even though the HP Keyboard has a unique address, it is currently in an
unconfigured, dormant state. Its internal scanning matrix isn't actually
looking for physical key presses yet.
To tell the keyboard to "wake up and start acting like a keyboard," we have to
send a `SET_CONFIGURATION` standard request. We will ask it to load
Configuration 1 (which enables its Human Interface Device endpoint).
This is another 2-stage DMA chain (SETUP -> STATUS IN), identical to the
Address assignment, but with two critical differences:
1. We must change the Device Address in our Transfer Descriptors from `0` to
`1`. In the UHCI Token register, the Device Address lives at bits 8-14. Our
Token changes from `0x00E0002D` to **`0x00E0012D`**.
2. We change the SETUP payload to `0x09` (Set Configuration) and ask for Value
`0x01`.
Drop this strike into your host terminal:
[GEM-TERMINAL-START]
cat << 'OUTER_EOF' > bcm_usb_setconf.sh
#!/bin/bash
```
echo "[HOST] 1. Forging SET_CONFIGURATION DMA Chain Payload..."
cat << 'JS_PAYLOAD' > usb_setconf.js
globalThis.USB.setConfig = function(base, address, configNum) {
sys.serial("
[UHCI] Sending SET_CONFIGURATION " + configNum + " to Device " + address +
"...
");
var dmaBase = sys.jit_addr();
var flBase = dmaBase + 0x30000;
var qhBase = dmaBase + 0x31000;
var tdSetup = dmaBase + 0x32000;
var tdStat = dmaBase + 0x32040;
var bufSetup= dmaBase + 0x33000;
// 1. Generate SOF Keepalives
for (var i = 0; i < 1024; i++) globalThis.MMIO.poke32(flBase + (i*4),
0x00000001);
sys.outw(base + 0x08, flBase & 0xFFFF);
sys.outw(base + 0x0A, (flBase >> 16) & 0xFFFF);
sys.outw(base + 0x00, 0x0001); // RUN
for(var i=0; i<1000000; i++) {} // Stabilize
// 2. Prepare SETUP Packet (SET_CONFIGURATION)
// bmRequestType: 0x00 (Host-to-Device, Standard, Device)
// bRequest: 0x09 (SET_CONFIGURATION)
// wValue: configNum (0x0001)
sys.poke(bufSetup+0, 0x00); sys.poke(bufSetup+1, 0x09);
sys.poke(bufSetup+2, configNum & 0xFF); sys.poke(bufSetup+3, 0x00);
sys.poke(bufSetup+4, 0x00); sys.poke(bufSetup+5, 0x00);
sys.poke(bufSetup+6, 0x00); sys.poke(bufSetup+7, 0x00);
// Shift Address into Token (Bits 8-14)
var addrShift = (address & 0x7F) << 8;
// 3. TD1 (SETUP, 8 bytes, Data0, Device X) -> Links to TD2
globalThis.MMIO.poke32(tdSetup + 0, tdStat);
globalThis.MMIO.poke32(tdSetup + 4, 0x1C800000);
globalThis.MMIO.poke32(tdSetup + 8, 0x00E0002D | addrShift);
globalThis.MMIO.poke32(tdSetup + 12, bufSetup);
// 4. TD2 (STATUS IN, 0 bytes, Data1, Device X) -> Links to Terminate
globalThis.MMIO.poke32(tdStat + 0, 0x00000001);
globalThis.MMIO.poke32(tdStat + 4, 0x1C800000);
globalThis.MMIO.poke32(tdStat + 8, 0xFFE80069 | addrShift);
globalThis.MMIO.poke32(tdStat + 12, 0x00000000);
// 5. Link Queue Head
globalThis.MMIO.poke32(qhBase + 0, 0x00000001);
globalThis.MMIO.poke32(qhBase + 4, tdSetup);
// 6. Hot-Plug Injection
sys.serial(" -> Hot-Injecting SET_CONFIGURATION Chain to Address " +
address + "...
");
var qhPtr = qhBase | 0x02;
for (var i = 0; i < 1024; i++) globalThis.MMIO.poke32(flBase + (i*4),
qhPtr);
// 7. Dynamic Polling
var timeout = 0, st1, st2;
while(timeout < 5000000) {
st1 = globalThis.MMIO.peek32(tdSetup + 4);
st2 = globalThis.MMIO.peek32(tdStat + 4);
if ((st1 & 0x00800000) === 0 && (st1 & 0x7E0000) !== 0) break;
if ((st2 & 0x00800000) === 0) break;
timeout++;
}
// 8. Cleanup
for (var i = 0; i < 1024; i++) globalThis.MMIO.poke32(flBase + (i*4),
0x00000001);
sys.outw(base + 0x00, 0x0000); // Stop controller
sys.serial("
=== SET CONFIGURATION RESULTS ===
");
sys.serial("Polls : " + timeout + "
");
sys.serial("TD1 (SETUP) Status : 0x" + st1.toString(16).toUpperCase() + "
");
sys.serial("TD2 (STAT ) Status : 0x" + st2.toString(16).toUpperCase() + "
");
if ((st2 & 0x00800000) === 0 && (st2 & 0x7E0000) === 0) {
sys.serial("
[SUCCESS] HP Keyboard Endpoints are ONLINE.
");
} else {
sys.serial("
[ERROR] Device rejected configuration.
");
}
return "SET_CONF_COMPLETE";
};
JS_PAYLOAD
echo "[HOST] 2. Streaming Payload..."
echo "/eval EOF" | x-console elli send-stdin
cat usb_setconf.js | x-console elli send-stdin 0.05
echo "EOF" | x-console elli send-stdin
sleep 2
echo "[HOST] 3. Triggering SET_CONFIGURATION (Config 1) on Address 1..."
x-console elli send "globalThis.USB.setConfig(0x4440, 1, 1)" Enter
echo "[HOST] 4. Tailing Output:"
x-console elli tail 20
OUTER_EOF
chmod +x bcm_usb_setconf.sh