{"record":{"id":"bc2a129237035ac4","repo":"embassy-rs/embassy","slug":"the-hub-change-must-be-taken-before-a-port-change","errorCode":null,"errorMessage":"the hub change must be taken before a port change is taken","messagePattern":"the hub change must be taken before a port change is taken","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"embassy-usb-host/src/class/hub.rs","lineNumber":444,"sourceCode":"                hub_change = true;\n            }\n        }\n        hub_change\n    }\n    /// Returns the 0-based port number of the first port that has a status change, consuming it.\n    /// Returns `None` if no port has a status change.\n    ///\n    /// ### Panic\n    /// Panics if the hub status change has not been taken.\n    fn take_port_change(&mut self) -> Option<u8> {\n        self.0\n            .iter_mut()\n            .enumerate()\n            .find(|(_, v)| v.trailing_zeros() < u8::BITS)\n            .map(|(idx, v)| {\n                let bit = v.trailing_zeros() as usize;\n                if idx == 0 && bit == 0 {\n                    panic!(\"the hub change must be taken before a port change is taken\");\n                }\n                *v &= !(1 << bit);\n                // The first port is in idx 0 bit 1.\n                // This code starts port numbers at 0, so it needs a `- 1`.\n                // On the other hand, the usb spec starts port numbers at 1,\n                // so this number will be increased by 1 for `SetupPacket`.\n                (bit + idx * 8 - 1) as u8\n            })\n    }\n}\n\n/// USB Hub class descriptor (USB 2.0 §11.23.2.1)\n#[derive(Copy, Clone, Debug, PartialEq, Eq)]\n#[cfg_attr(feature = \"defmt\", derive(defmt::Format))]\npub struct HubDescriptor {\n    /// Number of ports.\n    pub port_num: u8,\n    /// Characteristics bitmap.","sourceCodeStart":426,"sourceCodeEnd":462,"githubUrl":"https://github.com/embassy-rs/embassy/blob/463a07b963419a1bfe61d5d597c44acb810afb8b/embassy-usb-host/src/class/hub.rs#L426-L462","documentation":"In the USB hub class, hub-level and port-level change bits live in one bitmap; the hub change bit (idx 0, bit 0) must be cleared (taken) before any port change bit is taken. take_port_change panics if it encounters bit 0 first, enforcing the driver's documented retrieval order.","triggerScenarios":"Calling take_port_change when the hub-level change bit is still set (i.e. hub change was never taken via the hub-level API first).","commonSituations":"Handling port interrupts before reading the hub status; a driver rewrite that polls port changes directly; resuming after reset where the hub change bit was re-set but the handler only queries ports.","solutions":["Always take the hub change (via the hub-level change accessor) before calling take_port_change.","Restructure the interrupt/status handler to process hub events first, then port events.","Clear/re-read the full status bitmap in one pass so the hub bit is consumed in order."],"exampleFix":"// before\nlet port_change = hub.take_port_change(port).await; // hub bit still set\n// after\nlet hub_change = hub.take_hub_change().await;\nif hub_change { hub.handle_hub_event().await; }\nlet port_change = hub.take_port_change(port).await;","handlingStrategy":"validation","validationCode":"// Enforce ordering before reading port changes:\n// only call take_port_change after take_hub_change has consumed the hub bit.\nlet hub_change = hub.take_hub_change().await;\nif hub_change {\n    hub.process_hub_status().await;\n}\nlet port_change = hub.take_port_change(port).await;","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always drain the hub-level change bit before port bits","Process hub status in the same interrupt handler before ports","Document the hub-before-port retrieval order in your USB host task"],"tags":["embedded","usb","usb-host","state-machine","panic"],"backgroundTag":"invalid-state-transition","analyzedSha":"463a07b963419a1bfe61d5d597c44acb810afb8b","analyzedAt":"2026-09-10T13:38:26.660Z","contentChangedAt":"2026-09-10T13:38:26.660Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}