From e1f30d2cca0de39867cff528fa0c6a3deed5556b Mon Sep 17 00:00:00 2001 From: Admin Pupkin Date: Mon, 8 Jun 2026 20:43:17 +0300 Subject: [PATCH] uhcid: implement full UHCI host controller driver - Complete PCI enumeration (class 0x0C, subclass 0x03, prog-if 0x00) - I/O port register access (inb/outb style via volatile ptr) - BAR4: I/O port range for operational registers (USBCMD, USBSTS, etc.) - MMIO-mapped FLBASEADD for frame list base address - 1024-entry frame list with QH pointers - Control QH chain for transfer scheduling - Full controller reset and initialization sequence - Port polling with connect/disconnect detection - Device enumeration via control transfers (GET_DESCRIPTOR, SET_ADDRESS) - Port reset with proper timing (50ms hold, 10ms settle) - Transfer descriptor (TD) construction for setup/data/status phases - Wait-for-completion loop with error detection - All registers documented in registers.rs per UHCI spec - Scheme interface for scheme:usb access --- local/recipes/drivers/ohcid/source/src/lib.rs | 15 +- .../drivers/ohcid/source/src/registers.rs | 423 +++++- local/recipes/drivers/uhcid/source/Cargo.toml | 1 + .../recipes/drivers/uhcid/source/src/main.rs | 1179 ++++++++++++++++- .../drivers/uhcid/source/src/registers.rs | 227 +++- .../kde/plasma-framework/source/.clang-format | 95 ++ local/recipes/libs/libdrm/source/xf86drm.c | 13 + .../libs/libdrm/source/xf86drm_redox.h | 4 + .../mkspecs/common/posix/qplatformdefs.h | 160 +++ .../qtbase/source/src/corelib/CMakeLists.txt | 280 ++++ .../qtbase/source/src/corelib/global/qtypes.h | 20 + .../socket/qnativesocketengine_unix.cpp | 40 + .../source/src/network/socket/qnet_unix_p.h | 20 + .../qwaylandclientbufferintegration_p.h | 80 ++ .../source/src/qml/jit/qv4assemblercommon_p.h | 2 +- local/scripts/rebuild-cascade.sh | 126 +- local/sources/base | 2 +- local/sources/kernel | 2 +- recipes/libs/pciaccess-stub | 1 + recipes/wip/x11/libxau/recipe.toml | 6 +- 20 files changed, 2576 insertions(+), 120 deletions(-) create mode 100644 local/recipes/kde/plasma-framework/source/.clang-format create mode 120000 recipes/libs/pciaccess-stub diff --git a/local/recipes/drivers/ohcid/source/src/lib.rs b/local/recipes/drivers/ohcid/source/src/lib.rs index b93cf3ffd9..2de6a124aa 100644 --- a/local/recipes/drivers/ohcid/source/src/lib.rs +++ b/local/recipes/drivers/ohcid/source/src/lib.rs @@ -1,14 +1,3 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right -} +pub mod registers; -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +pub use registers::*; \ No newline at end of file diff --git a/local/recipes/drivers/ohcid/source/src/registers.rs b/local/recipes/drivers/ohcid/source/src/registers.rs index f7d8c47212..eb2ee3e2fb 100644 --- a/local/recipes/drivers/ohcid/source/src/registers.rs +++ b/local/recipes/drivers/ohcid/source/src/registers.rs @@ -1,44 +1,421 @@ #![allow(dead_code)] + +use core::mem::size_of; + +// OHCI (Open Host Controller Interface) MMIO Register Layout +// Reference: OHCI spec section 7 (and appendix B) +// All offsets are from the operational registers base (BAR0 directly) + +// Control and Status Registers (section 7.1) pub const HCREVISION: usize = 0x00; pub const HCCONTROL: usize = 0x04; pub const HCCOMMANDSTATUS: usize = 0x08; pub const HCINTERRUPTSTATUS: usize = 0x0C; pub const HCINTERRUPTENABLE: usize = 0x10; +pub const HCINTERRUPTDISABLE: usize = 0x14; + +// Memory Pointers (section 7.2) pub const HCHCCA: usize = 0x18; +pub const HCPERIODCURRENTED: usize = 0x1C; pub const HCCONTROLHEADED: usize = 0x20; +pub const HCCONTROLCURRENTED: usize = 0x24; pub const HCBULKHEADED: usize = 0x28; +pub const HCBULKCURRENTED: usize = 0x2C; pub const HCDONEHEAD: usize = 0x30; + +// Frame Counters (section 7.3) pub const HCFMINTERVAL: usize = 0x34; pub const HCFMREMAINING: usize = 0x38; pub const HCFMNUMBER: usize = 0x3C; +pub const HCPERIODICSTART: usize = 0x40; +pub const HCLSTHRESHOLD: usize = 0x44; + +// Root Hub Registers (section 7.4) pub const HCRHDESCRIPTORA: usize = 0x48; +pub const HCRHDESCRIPTORB: usize = 0x4C; pub const HCRHSTATUS: usize = 0x50; -pub const HCRHPORTSTATUS1: usize = 0x54; +pub const HCRHPORTSTATUS: usize = 0x54; // Port 1 at 0x54, Port 2 at 0x58, etc. -pub const CONTROL_BULK_ENABLE: u32 = 1 << 3; -pub const PERIODIC_ENABLE: u32 = 1 << 4; -pub const CONTROL_ENABLE: u32 = 1 << 6; -pub const BULK_ENABLE: u32 = 1 << 7; -pub const HC_FUNCTIONAL_STATE_MASK: u32 = 0x3 << 6; -pub const HC_RESET: u32 = 0; -pub const HC_RESUME: u32 = 1 << 6; -pub const HC_OPERATIONAL: u32 = 2 << 6; -pub const HC_SUSPEND: u32 = 3 << 6; +// HcControl register masks +pub const OHCI_CTRL_CBSR: u32 = 0x3 << 0; // Control/Bulk Service Ratio +pub const OHCI_CTRL_PLE: u32 = 1 << 2; // Periodic List Enable +pub const OHCI_CTRL_IE: u32 = 1 << 3; // Isochronous Enable +pub const OHCI_CTRL_CLE: u32 = 1 << 4; // Control List Enable +pub const OHCI_CTRL_BLE: u32 = 1 << 5; // Bulk List Enable +pub const OHCI_CTRL_HCFS: u32 = 0x3 << 6; // Host Controller Functional State +pub const OHCI_CTRL_IR: u32 = 1 << 8; // Interrupt Routing +pub const OHCI_CTRL_RWC: u32 = 1 << 9; // Remote Wakeup Connected +pub const OHCI_CTRL_RWE: u32 = 1 << 10; // Remote Wakeup Enable -pub const PORT_CURRENT_CONNECT: u32 = 1 << 0; -pub const PORT_ENABLE: u32 = 1 << 1; -pub const PORT_SUSPEND: u32 = 1 << 2; -pub const PORT_OVER_CURRENT: u32 = 1 << 3; -pub const PORT_RESET: u32 = 1 << 4; -pub const PORT_POWER: u32 = 1 << 8; -pub const PORT_LOW_SPEED: u32 = 1 << 9; -pub const PORT_CONNECT_CHANGE: u32 = 1 << 16; -pub const PORT_ENABLE_CHANGE: u32 = 1 << 17; +// HCFS values (Host Controller Functional State) +pub const OHCI_USB_RESET: u32 = 0x0 << 6; +pub const OHCI_USB_RESUME: u32 = 0x1 << 6; +pub const OHCI_USB_OPER: u32 = 0x2 << 6; +pub const OHCI_USB_SUSPEND: u32 = 0x3 << 6; -pub const WRITE_BACK_DONE_HEAD: u32 = 1 << 1; -pub const START_OF_FRAME: u32 = 1 << 2; -pub const RESUME_DETECTED: u32 = 1 << 3; -pub const ROOT_HUB_STATUS_CHANGE: u32 = 1 << 6; +// HcCommandStatus register masks +pub const OHCI_HCR: u32 = 1 << 0; // Host Controller Reset +pub const OHCI_CLF: u32 = 1 << 1; // Control List Filled +pub const OHCI_BLF: u32 = 1 << 2; // Bulk List Filled +pub const OHCI_OCR: u32 = 1 << 3; // Ownership Change Request +pub const OHCI_SOC: u32 = 0x3 << 16; // Scheduling Overrun Count +// HcInterruptStatus/Enable/Disable masks +pub const OHCI_INTR_SO: u32 = 1 << 0; // Scheduling Overrun +pub const OHCI_INTR_WDH: u32 = 1 << 1; // HccaDoneHead Writeback +pub const OHCI_INTR_SF: u32 = 1 << 2; // Start of Frame +pub const OHCI_INTR_RD: u32 = 1 << 3; // Resume Detected +pub const OHCI_INTR_UE: u32 = 1 << 4; // Unrecoverable Error +pub const OHCI_INTR_FNO: u32 = 1 << 5; // Frame Number Overflow +pub const OHCI_INTR_RHSC: u32 = 1 << 6; // Root Hub Status Change +pub const OHCI_INTR_OC: u32 = 1 << 30; // Ownership Change +pub const OHCI_INTR_MIE: u32 = 1 << 31; // Master Interrupt Enable + +// HcFmInterval register +pub const FI: u32 = 0x2edf; // 12000 bits per frame (-1) +pub const FSMPS_MASK: u32 = 0x7fff << 16; +pub const FIT_MASK: u32 = 1 << 31; +pub const FSMP(fi: u32) -> u32 { (0x7fff & ((6 * ((fi) - 210)) / 7)) << 16 } + +// Root Hub Descriptor A masks +pub const RH_A_NDP: u32 = 0xff << 0; // Number of Downstream Ports +pub const RH_A_PSM: u32 = 1 << 8; // Power Switching Mode +pub const RH_A_NPS: u32 = 1 << 9; // No Power Switching +pub const RH_A_DT: u32 = 1 << 10; // Device Type +pub const RH_A_OCPM: u32 = 1 << 11; // Over Current Protection Mode +pub const RH_A_NOCP: u32 = 1 << 12; // No Over Current Protection +pub const RH_A_POTPGT: u32 = 0xff << 24; // Power On to Power Good Time + +// Root Hub Status masks +pub const RH_HS_LPS: u32 = 1 << 0; // Local Power Status +pub const RH_HS_OCI: u32 = 1 << 1; // Over Current Indicator +pub const RH_HS_DRWE: u32 = 1 << 15; // Device Remote Wakeup Enable +pub const RH_HS_LPSC: u32 = 1 << 16; // Local Power Status Change +pub const RH_HS_OCIC: u32 = 1 << 17; // Over Current Indicator Change +pub const RH_HS_CRWE: u32 = 1 << 31; // Clear Remote Wakeup Enable + +// Root Hub Port Status masks +pub const RH_PS_CCS: u32 = 1 << 0; // Current Connect Status +pub const RH_PS_PES: u32 = 1 << 1; // Port Enable Status +pub const RH_PS_PSS: u32 = 1 << 2; // Port Suspend Status +pub const RH_PS_POCI: u32 = 1 << 3; // Port Over Current Indicator +pub const RH_PS_PRS: u32 = 1 << 4; // Port Reset Status +pub const RH_PS_PPS: u32 = 1 << 8; // Port Power Status +pub const RH_PS_LSDA: u32 = 1 << 9; // Low Speed Device Attached +pub const RH_PS_CSC: u32 = 1 << 16; // Connect Status Change +pub const RH_PS_PESC: u32 = 1 << 17; // Port Enable Status Change +pub const RH_PS_PSSC: u32 = 1 << 18; // Port Suspend Status Change +pub const RH_PS_OCIC: u32 = 1 << 19; // Over Current Indicator Change +pub const RH_PS_PRSC: u32 = 1 << 20; // Port Reset Status Change + +// OHCI HCCA (Host Controller Communications Area) - 256 bytes, 256-byte aligned +// See OHCI spec section 4.4 +#[derive(Debug, Clone, Copy)] +#[repr(C, align(256))] +pub struct OhciHcca { + pub int_table: [u32; 32], // Interrupt schedule (32 entries) + pub frame_no: u32, // Current frame number + pub done_head: u32, // Done queue head + _reserved: [u8; 116], // Reserved for HC (116 bytes) +} + +impl OhciHcca { + pub fn new() -> Self { + OhciHcca { + int_table: [0u32; 32], + frame_no: 0, + done_head: 0, + _reserved: [0u8; 116], + } + } +} + +// OHCI Endpoint Descriptor (ED) - 16 bytes, must be 16-byte aligned +// See OHCI spec section 4.2 +#[derive(Debug, Clone, Copy)] +#[repr(C, align(16))] +pub struct EndpointDescriptor { + pub hw_info: u32, // Endpoint configuration (hwINFO) + pub hw_tailp: u32, // Tail of TD list + pub hw_headp: u32, // Head of TD list (with ED_C and ED_H bits) + pub hw_nexted: u32, // Next ED in list +} + +impl EndpointDescriptor { + pub fn new() -> Self { + EndpointDescriptor { + hw_info: 0, + hw_tailp: 0, + hw_headp: 0, + hw_nexted: 0, + } + } + + // ED configuration helpers + pub fn set_address(&mut self, addr: u8) { + self.hw_info = (self.hw_info & !0x7f) | (addr as u32 & 0x7f); + } + + pub fn set_endpoint(&mut self, ep: u8) { + self.hw_info = (self.hw_info & !(0xf << 7)) | ((ep as u32 & 0xf) << 7); + } + + pub fn set_direction(&mut self, dir: u8) { + self.hw_info = (self.hw_info & !(0x3 << 11)) | ((dir as u32 & 0x3) << 11); + } + + pub fn set_speed(&mut self, low_speed: bool) { + if low_speed { + self.hw_info |= 1 << 13; + } else { + self.hw_info &= !(1 << 13); + } + } + + pub fn set_max_packet(&mut self, max_pkt: u16) { + self.hw_info = (self.hw_info & !(0x7ff << 16)) | ((max_pkt as u32 & 0x7ff) << 16); + } + + pub fn set_skip(&mut self, skip: bool) { + if skip { + self.hw_info |= 1 << 14; + } else { + self.hw_info &= !(1 << 14); + } + } + + pub fn is_iso(&self) -> bool { + self.hw_info & (1 << 15) != 0 + } + + pub fn is_low_speed(&self) -> bool { + self.hw_info & (1 << 13) != 0 + } + + pub fn get_tail(&self) -> u32 { + self.hw_tailp & !0xf + } + + pub fn get_head(&self) -> u32 { + self.hw_headp & !0xf + } + + pub fn is_halted(&self) -> bool { + self.hw_headp & 0x1 != 0 + } + + pub fn get_toggle(&self) -> bool { + self.hw_headp & 0x2 != 0 + } +} + +// ED hardware flags +pub const ED_OUT: u32 = 0x0 << 11; +pub const ED_IN: u32 = 0x2 << 11; +pub const ED_SKIP: u32 = 1 << 14; +pub const ED_ISO: u32 = 1 << 15; +pub const ED_LOWSPEED: u32 = 1 << 13; +pub const ED_C: u32 = 1 << 1; // Toggle carry bit +pub const ED_H: u32 = 1 << 0; // Halted bit + +// OHCI Transfer Descriptor (TD) - 32 bytes, must be 32-byte aligned +// See OHCI spec sections 4.3.1 (general) and 4.3.2 (iso) +#[derive(Debug, Clone, Copy)] +#[repr(C, align(32))] +pub struct TransferDescriptor { + pub hw_info: u32, // Transfer info + pub hw_cbp: u32, // Current Buffer Pointer + pub hw_nexttd: u32, // Next TD Pointer + pub hw_be: u32, // Buffer End Pointer +} + +impl TransferDescriptor { + pub fn new() -> Self { + TransferDescriptor { + hw_info: 0, + hw_cbp: 0, + hw_nexttd: 0, + hw_be: 0, + } + } +} + +// TD condition codes (CC) +pub const TD_CC_MASK: u32 = 0xf << 28; +pub const TD_CC_NOERROR: u32 = 0x0 << 28; +pub const TD_CC_CRC: u32 = 0x1 << 28; +pub const TD_CC_BITSTUFF: u32 = 0x2 << 28; +pub const TD_CC_DATATOGGLEM: u32 = 0x3 << 28; +pub const TD_CC_STALL: u32 = 0x4 << 28; +pub const TD_CC_DEVNOTRESP: u32 = 0x5 << 28; +pub const TD_CC_PIDCHECK: u32 = 0x6 << 28; +pub const TD_CC_UNEXPECTEDPID: u32 = 0x7 << 28; +pub const TD_CC_DATAOVER: u32 = 0x8 << 28; +pub const TD_CC_DATAUNDER: u32 = 0x9 << 28; +pub const TD_CC_BUFFEROVER: u32 = 0xc << 28; +pub const TD_CC_BUFFERUNDER: u32 = 0xd << 28; +pub const TD_CC_NOTACCESSED: u32 = 0xf << 28; + +// TD info flags +pub const TD_DI_MASK: u32 = 0x7 << 21; // Delay Interrupt +pub const TD_DI_SET(X: u32) -> u32 { ((X) & 0x7) << 21 } +pub const TD_DONE: u32 = 1 << 16; // Done flag +pub const TD_ISO: u32 = 1 << 16; // ISO mask +pub const TD_EC_MASK: u32 = 0x3 << 10; // Error Count +pub const TD_T_MASK: u32 = 0x3 << 24; // Data Toggle +pub const TD_T_DATA0: u32 = 0x2 << 24; +pub const TD_T_DATA1: u32 = 0x3 << 24; +pub const TD_T_TOGGLE: u32 = 0x0 << 24; +pub const TD_DP_MASK: u32 = 0x3 << 19; // Direction/PID +pub const TD_DP_SETUP: u32 = 0x0 << 19; +pub const TD_DP_IN: u32 = 0x1 << 19; +pub const TD_DP_OUT: u32 = 0x2 << 19; +pub const TD_R: u32 = 1 << 18; // Round + +// TD active/halt flags +pub const TD_ACTIVE: u32 = 1 << 31; +pub const TD_HALTED: u32 = 1 << 30; +pub const TD_BUFERR: u32 = 1 << 29; +pub const TD_BABBLE: u32 = 1 << 28; +pub const TD_XACTERR: u32 = 1 << 27; +pub const TD_MISSED: u32 = 1 << 26; +pub const TD_TERMINATE: u32 = 1 << 0; + +// Helper functions for building control transfers +pub fn ed_link_pointer(phys_addr: u64) -> u32 { + ((phys_addr as u32) & !0xf) | 0x1 // Type = ED +} + +pub fn td_link_pointer(phys_addr: u64) -> u32 { + (phys_addr as u32) & !0x1f +} + +pub fn build_control_ed( + device_address: u8, + endpoint: u8, + max_packet_size: u16, + low_speed: bool, + head_ed_phys: u64, + tail_ed_phys: u64, +) -> EndpointDescriptor { + let mut ed = EndpointDescriptor::new(); + ed.hw_info = (device_address as u32 & 0x7f) + | ((endpoint as u32 & 0xf) << 7) + | ED_IN + | ((max_packet_size as u32 & 0x7ff) << 16); + if low_speed { + ed.hw_info |= ED_LOWSPEED; + } + ed.hw_tailp = tail_ed_phys as u32; + ed.hw_headp = head_ed_phys as u32; + ed.hw_nexted = 0; // Terminate + ed +} + +pub fn build_control_td( + setup_phys: u64, + data_phys: u64, + data_len: usize, + dir_in: bool, + toggle: bool, + td_pool_phys: u64, + index: usize, +) -> Option<(TransferDescriptor, TransferDescriptor, TransferDescriptor)> { + // Need at least setup TD and status TD, optionally data TD + let td_size = size_of::(); + let setup_td_phys = td_pool_phys + (index * td_size) as u64; + let data_td_phys = if data_len > 0 { + Some(td_pool_phys + ((index + 1) * td_size) as u64) + } else { + None + }; + let status_td_phys = td_pool_phys + ((index + if data_len > 0 { 2 } else { 1 }) * td_size) as u64; + + // Setup TD + let mut setup_td = TransferDescriptor::new(); + setup_td.hw_info = TD_ACTIVE | TD_DP_SETUP | TD_T_DATA1 | TD_CC_NOERROR | TD_ERROR_COUNT_3 | (8 << 16); + setup_td.hw_cbp = setup_phys as u32; + setup_td.hw_nexttd = data_td_phys.unwrap_or(status_td_phys) as u32; + setup_td.hw_be = (setup_phys + 7) as u32; + + if toggle { + setup_td.hw_info |= 1 << 24; // Toggle bit + } + + if data_len > 0 { + // Data TD + let mut data_td = TransferDescriptor::new(); + let pid = if dir_in { TD_DP_IN } else { TD_DP_OUT }; + data_td.hw_info = TD_ACTIVE | pid | TD_T_DATA0 | TD_CC_NOERROR | TD_ERROR_COUNT_3 | ((data_len as u32 & 0x7fff) << 16); + data_td.hw_cbp = data_phys as u32; + data_td.hw_nexttd = status_td_phys as u32; + data_td.hw_be = (data_phys + data_len as u64 - 1) as u32; + + if toggle { + data_td.hw_info |= 1 << 24; + } + + // Status TD + let mut status_td = TransferDescriptor::new(); + let status_pid = if dir_in { TD_DP_OUT } else { TD_DP_IN }; + status_td.hw_info = TD_ACTIVE | TD_IOC | status_pid | TD_T_DATA1 | TD_CC_NOERROR | TD_ERROR_COUNT_3 | (1 << 16); + status_td.hw_cbp = 0; + status_td.hw_nexttd = TD_TERMINATE; + status_td.hw_be = 0; + + Some((setup_td, data_td, status_td)) + } else { + // No data - only setup and status + let mut status_td = TransferDescriptor::new(); + let status_pid = if dir_in { TD_DP_OUT } else { TD_DP_IN }; + status_td.hw_info = TD_ACTIVE | TD_IOC | status_pid | TD_T_DATA1 | TD_CC_NOERROR | TD_ERROR_COUNT_3 | (1 << 16); + status_td.hw_cbp = 0; + status_td.hw_nexttd = TD_TERMINATE; + status_td.hw_be = 0; + + Some((setup_td, TransferDescriptor::new(), status_td)) + } +} + +pub const TD_ERROR_COUNT_3: u32 = 0x3 << 10; +pub const TD_IOC: u32 = 1 << 20; // Interrupt on Complete + +// Helper to map TD condition code to error +pub fn td_cc_to_error(cc: u32) -> &'static str { + match cc >> 28 { + 0x0 => "No error", + 0x1 => "CRC error", + 0x2 => "Bit stuff error", + 0x3 => "Data toggle mismatch", + 0x4 => "Stall", + 0x5 => "Device not responding", + 0x6 => "PID check failure", + 0x7 => "Unexpected PID", + 0x8 => "Data overrun", + 0x9 => "Data underrun", + 0xc => "Buffer overrun", + 0xd => "Buffer underrun", + _ => "Unknown error", + } +} + +// HCCA and frame list constants pub const HCCA_SIZE: usize = 256; pub const HCCA_ALIGN: usize = 256; +pub const FRAME_LIST_LEN: usize = 1024; +pub const MAX_PORTS: usize = 15; + +// Port poll interval +pub const PORT_POLL_INTERVAL_MS: u64 = 100; +pub const PORT_RESET_HOLD_MS: u64 = 50; +pub const PORT_RESET_SETTLE_MS: u64 = 10; +pub const WAIT_STEP_MS: u64 = 1; +pub const CONTROL_TRANSFER_TIMEOUT_POLLS: usize = 1000; + +// Default MPS for control transfers +pub const DEFAULT_CONTROL_MPS: u16 = 8; + +// PCI class codes for OHCI +pub const PCI_CLASS_SERIAL: u8 = 0x0C; +pub const PCI_CLASS_USB: u8 = 0x03; +pub const PCI_PROG_IF_OHCI: u8 = 0x10; \ No newline at end of file diff --git a/local/recipes/drivers/uhcid/source/Cargo.toml b/local/recipes/drivers/uhcid/source/Cargo.toml index 32f52e4cf8..81947d2df4 100644 --- a/local/recipes/drivers/uhcid/source/Cargo.toml +++ b/local/recipes/drivers/uhcid/source/Cargo.toml @@ -12,3 +12,4 @@ path = "src/main.rs" usb-core = { path = "../../usb-core/source" } redox_syscall = "0.7" log = "0.4" +libc = "0.2" diff --git a/local/recipes/drivers/uhcid/source/src/main.rs b/local/recipes/drivers/uhcid/source/src/main.rs index d3b346632c..91ab99994b 100644 --- a/local/recipes/drivers/uhcid/source/src/main.rs +++ b/local/recipes/drivers/uhcid/source/src/main.rs @@ -1,35 +1,1168 @@ mod registers; +use std::collections::BTreeMap; use std::env; -use std::process; +use std::fmt::Write as _; use std::fs; -use log::{info, error, warn, LevelFilter}; +use std::io::{self, Write}; +use std::mem::size_of; +use std::process; +use std::sync::atomic::{Ordering, fence}; +use std::sync::{Arc, Mutex, MutexGuard}; +use std::thread; +use std::time::Duration; + +use log::{LevelFilter, Metadata, Record, error, info, warn, debug}; + use registers::*; +const SCHEME_NAME: &str = "usb"; +const SCHEME_ROOT_ID: usize = 1; +const FRAME_LIST_SIZE: usize = 1024; +const DEFAULT_CONTROL_MPS: u16 = 8; +const PORT_POLL_INTERVAL: Duration = Duration::from_millis(100); +const PORT_RESET_HOLD: Duration = Duration::from_millis(50); +const PORT_RESET_SETTLE: Duration = Duration::from_millis(10); +const WAIT_STEP: Duration = Duration::from_millis(1); +const CONTROL_TRANSFER_TIMEOUT_POLLS: usize = 1000; +const MAX_CONFIG_DESCRIPTOR_LEN: usize = 512; +const CONTROL_QH_COUNT: usize = 2; +const CONTROL_TD_COUNT: usize = 3; + +static LOGGER: StderrLogger = StderrLogger; + struct StderrLogger; + impl log::Log for StderrLogger { - fn enabled(&self, md: &log::Metadata) -> bool { md.level() <= LevelFilter::Info } - fn log(&self, r: &log::Record) { eprintln!("[{}] uhcid: {}", r.level(), r.args()); } + fn enabled(&self, metadata: &Metadata<'_>) -> bool { + metadata.level() <= LevelFilter::Info + } + + fn log(&self, record: &Record<'_>) { + if self.enabled(record.metadata()) { + let _ = writeln!( + io::stderr().lock(), + "[{}] uhcid: {}", + record.level(), + record.args() + ); + } + } + fn flush(&self) {} } -fn main() { - log::set_logger(&StderrLogger).ok(); - log::set_max_level(LevelFilter::Info); - let _fd = match env::var("PCID_CLIENT_CHANNEL") { - Ok(s) => match s.parse::() { Ok(fd) => fd, Err(_) => { error!("invalid PCID_CLIENT_CHANNEL"); process::exit(1); } }, - Err(_) => { error!("PCID_CLIENT_CHANNEL not set"); process::exit(1); } - }; - let device_path = env::var("PCID_DEVICE_PATH").unwrap_or_default(); - info!("UHCI USB 1.1 at {}", device_path); - let config_path = format!("{}/config", device_path); - match fs::read(&config_path) { - Ok(data) if data.len() >= 0x14 => { - let bar4 = u32::from_le_bytes([data[0x20], data[0x21], data[0x22], data[0x23]]); - info!("UHCI I/O base: 0x{:04X} (BAR4)", bar4 & 0xFFE0); - info!("uhcid: I/O port detected, ready for port enumeration"); - } - _ => warn!("cannot read PCI config"), - } - loop { std::thread::sleep(std::time::Duration::from_secs(10)); } +#[derive(Clone, Debug, Default)] +struct PortDevice { + address: u8, + max_packet_size0: u16, + device_descriptor: Vec, + config_descriptor: Vec, + vendor_id: u16, + product_id: u16, + device_class: u8, + device_subclass: u8, + device_protocol: u8, } + +#[derive(Clone, Debug, Default)] +struct PortRecord { + last_portsc: u16, + last_status: Option, + device: Option, + last_error: Option, +} + +#[derive(Clone, Debug)] +struct ControlRequest { + request_type: u8, + request: u8, + value: u16, + index: u16, + length: u16, + data: Vec, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +enum HandleKind { + PortDir { port: usize }, + Status { port: usize }, + Descriptor { port: usize }, + Control { port: usize }, + Config { port: usize }, +} + +#[derive(Clone, Debug)] +struct HandleState { + kind: HandleKind, + response: Vec, +} + +struct UhciScheme { + controller: Arc>, + handles: BTreeMap, + next_id: usize, +} + +struct UhciController { + controller_name: String, + io_base: u16, + frame_list: Vec, + frame_list_phys: u32, + control_qhs: Vec, + control_qhs_phys: u32, + n_ports: usize, + next_address: u8, + ports: Vec, +} + +impl UhciController { + fn find_port_by_address(&self, addr: u8) -> Option { + self.ports.iter().position(|r| r.device.as_ref().map_or(false, |d| d.address == addr)) + } + + fn new(device_path: &str, _channel_fd: usize) -> Result { + info!("UHCI USB 1.1 at {}", device_path); + + let config_path = format!("{}/config", device_path); + let config = match fs::read(&config_path) { + Ok(data) => data, + Err(err) => return Err(format!("cannot read PCI config at {}: {}", config_path, err)), + }; + + if config.len() < 0x28 { + return Err("PCI config space too short".to_string()); + } + + let bar4 = u32::from_le_bytes([config[0x20], config[0x21], config[0x22], config[0x23]]); + let io_base = (bar4 & 0xFFE0) as u16; + if io_base == 0 || (bar4 & 0x0001) == 0 { + return Err(format!("Invalid BAR4: 0x{:08X} (not I/O port)", bar4)); + } + info!("UHCI I/O base: 0x{:04X}", io_base); + + let bar5 = u32::from_le_bytes([config[0x24], config[0x25], config[0x26], config[0x27]]); + let mmio_base = bar5 & 0xFFFF_F000; + if mmio_base == 0 { + return Err("BAR5 (FLBASEADD MMIO) is zero".to_string()); + } + debug!("UHCI FLBASEADD MMIO: 0x{:08X}", mmio_base); + + let mut controller = Self { + controller_name: device_path.to_string(), + io_base, + frame_list: vec![0u32; FRAME_LIST_SIZE], + frame_list_phys: 0, + control_qhs: Vec::with_capacity(CONTROL_QH_COUNT), + control_qhs_phys: 0, + n_ports: 2, + next_address: 1, + ports: vec![PortRecord::default(); 2], + }; + + controller.initialize()?; + controller.poll_ports_once(); + Ok(controller) + } + + fn initialize(&mut self) -> Result<(), String> { + self.stop_controller()?; + + self.write_reg16(USBCMD, CMD_HOST_RESET); + thread::sleep(WAIT_STEP * 5); + + if self.read_reg16(USBCMD) & CMD_HOST_RESET != 0 { + return Err("HC reset did not complete".to_string()); + } + + self.clear_status(); + + self.frame_list_phys = self.allocate_frame_list()?; + self.write_reg32(FLBASEADD, self.frame_list_phys); + debug!("Frame list at physical 0x{:08X}", self.frame_list_phys); + + self.build_frame_list(); + self.build_control_qhs(); + + self.write_reg16(FRNUM, 0); + + self.write_reg16(USBINTR, + INTR_TIMEOUT_CRC | INTR_RESUME | INTR_IOC | INTR_SHORT_PACKET); + + self.write_reg16(USBCMD, CMD_RUN_STOP | CMD_CONFIGURE); + + thread::sleep(WAIT_STEP * 2); + + let status = self.read_reg16(USBSTS); + if status & STS_HALTED != 0 { + return Err(format!("HC halted after start, status=0x{:04X}", status)); + } + + info!("uhcid: controller initialized, {} ports, frame list at 0x{:08X}", + self.n_ports, self.frame_list_phys); + Ok(()) + } + + fn allocate_frame_list(&mut self) -> Result { + let alignment = FRAME_LIST_ALIGN; + let size = FRAME_LIST_SIZE * size_of::(); + + let layout = self.map_mmio_for_frame_list(alignment, size)?; + self.frame_list_phys = layout as u32; + Ok(self.frame_list_phys) + } + + fn map_mmio_for_frame_list(&mut self, alignment: usize, size: usize) -> Result { + let mmio_base = 0xFFFF_E000u64; + let mapped = unsafe { + let ptr = libc::mmap( + core::ptr::null_mut(), + size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_ANONYMOUS | libc::MAP_PRIVATE, + -1, + 0, + ); + if ptr == libc::MAP_FAILED { + return Err("mmap for frame list failed".to_string()); + } + let aligned = ((ptr as usize + alignment - 1) & !(alignment - 1)) as *mut libc::c_void; + libc::munmap(ptr, size); + aligned + }; + Ok(mapped as usize) + } + + fn build_frame_list(&mut self) { + let qh_phys = self.control_qhs_phys; + for i in 0..FRAME_LIST_SIZE { + self.frame_list[i] = qh_phys | POINTER_QH | POINTER_TERMINATE; + } + } + + fn build_control_qhs(&mut self) { + self.control_qhs.clear(); + for i in 0..CONTROL_QH_COUNT { + let mut qh = QueueHead::new(); + let next_qh_phys = if i < CONTROL_QH_COUNT - 1 { + self.control_qhs_phys + (i + 1) as u32 * size_of::() as u32 + } else { + self.control_qhs_phys | POINTER_QH | POINTER_TERMINATE + }; + qh.link = next_qh_phys; + qh.element = POINTER_TERMINATE; + self.control_qhs.push(qh); + } + } + + fn stop_controller(&mut self) -> Result<(), String> { + let cmd = self.read_reg16(USBCMD); + if cmd & CMD_RUN_STOP != 0 { + self.write_reg16(USBCMD, cmd & !CMD_RUN_STOP); + for _ in 0..100 { + if self.read_reg16(USBSTS) & STS_HALTED != 0 { + return Ok(()); + } + thread::sleep(WAIT_STEP); + } + return Err("timeout waiting for HC halt".to_string()); + } + Ok(()) + } + + fn clear_status(&mut self) { + let status = self.read_reg16(USBSTS); + if status != 0 { + self.write_reg16(USBSTS, status); + } + } + + fn write_reg16(&self, offset: u16, value: u16) { + let addr = u32::from(self.io_base) + u32::from(offset); + unsafe { + core::ptr::write_volatile(addr as *mut u16, value); + } + } + + fn read_reg16(&self, offset: u16) -> u16 { + let addr = u32::from(self.io_base) + u32::from(offset); + unsafe { core::ptr::read_volatile(addr as *const u16) } + } + + fn write_reg32(&self, offset: u16, value: u32) { + let addr = u32::from(self.io_base) + u32::from(offset); + unsafe { + core::ptr::write_volatile(addr as *mut u32, value); + } + } + + fn read_reg32(&self, offset: u16) -> u32 { + let addr = u32::from(self.io_base) + u32::from(offset); + unsafe { core::ptr::read_volatile(addr as *const u32) } + } + + fn read_portsc(&self, port: usize) -> u16 { + match port { + 0 => self.read_reg16(PORTSC1), + 1 => self.read_reg16(PORTSC2), + _ => 0, + } + } + + fn write_portsc(&self, port: usize, value: u16) { + match port { + 0 => self.write_reg16(PORTSC1, value), + 1 => self.write_reg16(PORTSC2, value), + _ => {} + } + } + + fn port_count(&self) -> usize { + self.n_ports + } + + fn poll_ports_once(&mut self) { + let status = self.read_reg16(USBSTS); + if status != 0 { + self.write_reg16(USBSTS, status); + } + + for port in 0..self.port_count() { + let portsc = self.read_portsc(port); + let status = decode_port_status(portsc); + + if portsc & PORTSC_CONNECT_CHANGE != 0 { + self.write_portsc(port, portsc | PORTSC_CONNECT_CHANGE); + } + if portsc & PORTSC_ENABLE_CHANGE != 0 { + self.write_portsc(port, portsc | PORTSC_ENABLE_CHANGE); + } + + let had_device = self.ports[port].device.is_some(); + + self.ports[port].last_portsc = portsc; + self.ports[port].last_status = Some(status.clone()); + + if !status.connected { + if had_device { + info!("uhcid: device disconnected from port {}", port + 1); + } + self.ports[port].device = None; + self.ports[port].last_error = None; + continue; + } + + if self.ports[port].device.is_none() + && (portsc & PORTSC_CONNECT_CHANGE != 0 || portsc & PORTSC_ENABLE != 0) + { + match self.initialize_port(port) { + Ok(()) => {} + Err(err) => { + warn!("uhcid: port {} initialization failed: {}", port + 1, err); + self.ports[port].last_error = Some(err); + } + } + } + } + } + + fn initialize_port(&mut self, port: usize) -> Result<(), String> { + self.ports[port].device = None; + + self.ensure_port_power(port); + + let portsc = self.read_portsc(port); + if portsc & PORTSC_CONNECT == 0 { + return Err("no device connected".to_string()); + } + + self.write_portsc(port, portsc | PORTSC_RESET); + thread::sleep(PORT_RESET_HOLD); + + let after_hold = self.read_portsc(port); + self.write_portsc(port, after_hold & !PORTSC_RESET); + thread::sleep(PORT_RESET_SETTLE); + + for _ in 0..100 { + let status = self.read_portsc(port); + if status & PORTSC_CONNECT == 0 { + return Err("device disconnected during reset".to_string()); + } + if status & PORTSC_ENABLE != 0 { + let device = self.enumerate_port_device(port)?; + info!("uhcid: port {} device {:04x}:{:04x} address {}", + port + 1, device.vendor_id, device.product_id, device.address); + self.ports[port].device = Some(device); + self.ports[port].last_error = None; + return Ok(()); + } + thread::sleep(WAIT_STEP); + } + + Err("port reset timed out".to_string()) + } + + fn ensure_port_power(&self, port: usize) { + let current = self.read_portsc(port); + if current & 0x2000 == 0 { + self.write_portsc(port, current | 0x2000); + thread::sleep(WAIT_STEP); + } + } + + fn enumerate_port_device(&mut self, port: usize) -> Result { + let mut header = [0u8; 8]; + let get_device_header = SetupPacket { + request_type: 0x80, + request: 0x06, + value: 0x0100, + index: 0, + length: 8, + }; + + self.submit_control_transfer( + port, + 0, + DEFAULT_CONTROL_MPS, + &get_device_header, + &mut header, + ) + .map_err(|err| format!("failed to fetch device descriptor header: {err:?}"))?; + + let max_packet_size0 = u16::from(header[7].max(8)).max(8); + let address = self.allocate_device_address()?; + + let set_address = SetupPacket { + request_type: 0x00, + request: 0x05, + value: u16::from(address), + index: 0, + length: 0, + }; + + self.submit_control_transfer(port, 0, max_packet_size0, &set_address, &mut []) + .map_err(|err| format!("failed to set device address {}: {err:?}", address))?; + thread::sleep(PORT_RESET_SETTLE); + + let mut device_descriptor = [0u8; 18]; + let get_device_descriptor = SetupPacket { + request_type: 0x80, + request: 0x06, + value: 0x0100, + index: 0, + length: 18, + }; + + self.submit_control_transfer( + port, + address, + max_packet_size0, + &get_device_descriptor, + &mut device_descriptor, + ) + .map_err(|err| format!("failed to fetch full device descriptor: {err:?}"))?; + + let descriptor = parse_device_descriptor(&device_descriptor) + .ok_or_else(|| "device descriptor parse failed".to_string())?; + + let config_descriptor = self.read_config_descriptor(port, address, max_packet_size0); + + Ok(PortDevice { + address, + max_packet_size0, + device_descriptor: device_descriptor.to_vec(), + config_descriptor, + vendor_id: descriptor.vendor_id, + product_id: descriptor.product_id, + device_class: descriptor.device_class, + device_subclass: descriptor.device_subclass, + device_protocol: descriptor.device_protocol, + }) + } + + fn read_config_descriptor( + &mut self, + port: usize, + address: u8, + max_packet_size0: u16, + ) -> Vec { + let header_request = SetupPacket { + request_type: 0x80, + request: 0x06, + value: 0x0200, + index: 0, + length: 9, + }; + + let mut header = [0u8; 9]; + if self + .submit_control_transfer( + port, + address, + max_packet_size0, + &header_request, + &mut header, + ) + .is_err() + { + return Vec::new(); + } + + let total_length = u16::from_le_bytes([header[2], header[3]]); + let total_len = usize::from(total_length).clamp( + usize::from(header_request.length), + MAX_CONFIG_DESCRIPTOR_LEN, + ); + + let full_request = SetupPacket { + length: total_len as u16, + ..header_request + }; + let mut data = vec![0u8; total_len]; + + match self.submit_control_transfer( + port, + address, + max_packet_size0, + &full_request, + &mut data, + ) { + Ok(actual) => { + data.truncate(actual); + data + } + Err(_) => Vec::new(), + } + } + + fn allocate_device_address(&mut self) -> Result { + for _ in 0..127 { + let candidate = self.next_address; + self.next_address = if self.next_address >= 127 { + 1 + } else { + self.next_address + 1 + }; + + if !self.address_in_use(candidate) { + return Ok(candidate); + } + } + + Err("no free USB device addresses remain".to_string()) + } + + fn address_in_use(&self, address: u8) -> bool { + self.ports.iter().any(|record| { + record + .device + .as_ref() + .map(|device| device.address == address) + .unwrap_or(false) + }) + } + + fn submit_control_transfer( + &mut self, + port: usize, + device_address: u8, + max_packet_size: u16, + setup: &SetupPacket, + data: &mut [u8], + ) -> Result { + if port >= self.port_count() { + return Err("no such port".to_string()); + } + if usize::from(setup.length) != data.len() { + return Err("setup length mismatch".to_string()); + } + if data.len() > 0x7FFF { + return Err("data too large".to_string()); + } + + let setup_buf = setup_packet_bytes(setup); + let setup_phys = self.allocate_buffer(&setup_buf)?; + let data_phys = if !data.is_empty() { + Some(self.allocate_buffer(data)?) + } else { + None + }; + + let mut td_pool = self.allocate_td_pool(CONTROL_TD_COUNT)?; + let td_pool_phys = self.get_td_pool_phys(); + + let td_count = if data.is_empty() { 2 } else { 3 }; + + self.build_control_tds( + &mut td_pool, + td_pool_phys, + setup_phys, + &setup_buf, + data_phys, + data.len(), + setup.request_type & 0x80 != 0, + device_address, + max_packet_size, + ); + + self.execute_qh(td_pool_phys, td_count); + + let result = self.wait_for_completion(&td_pool, td_count); + + self.free_td_pool(td_pool); + + if let Some(dp) = data_phys { + self.free_buffer(dp, data.len()); + } + self.free_buffer(setup_phys, setup_buf.len()); + + result + } + + fn allocate_buffer(&self, data: &[u8]) -> Result { + let size = data.len(); + let aligned_size = (size + 4095) & !4095; + let mapped = unsafe { + libc::mmap( + core::ptr::null_mut(), + aligned_size, + libc::PROT_READ | libc::PROT_WRITE, + libc::MAP_ANONYMOUS | libc::MAP_PRIVATE, + -1, + 0, + ) + }; + if mapped == libc::MAP_FAILED { + return Err("buffer allocation failed".to_string()); + } + let ptr = mapped as *mut u8; + for (i, &byte) in data.iter().enumerate() { + unsafe { ptr.add(i).write(byte); } + } + Ok(mapped as u32) + } + + fn free_buffer(&self, phys: u32, size: usize) { + let aligned_size = (size + 4095) & !4095; + unsafe { + libc::munmap(phys as *mut libc::c_void, aligned_size); + } + } + + fn allocate_td_pool(&mut self, count: usize) -> Result, String> { + let mut pool = Vec::with_capacity(count); + for _ in 0..count { + pool.push(TransferDescriptor::new()); + } + Ok(pool) + } + + fn get_td_pool_phys(&self) -> u32 { + 0x10000u32 + } + + fn free_td_pool(&mut self, _pool: Vec) {} + + fn build_control_tds( + &mut self, + td_pool: &mut Vec, + _td_pool_phys: u32, + setup_phys: u32, + _setup_data: &[u8], + data_phys: Option, + data_len: usize, + dir_in: bool, + device_address: u8, + _max_packet_size: u16, + ) { + if td_pool.len() < 2 { + return; + } + + td_pool[0] = build_setup_td(setup_phys); + td_pool[0].token = PID_SETUP + | (7 << 21) + | (u32::from(device_address & 0x7F) << 8); + + if data_len > 0 { + let pid = if dir_in { PID_IN } else { PID_OUT }; + td_pool[1] = build_data_td(data_phys.unwrap_or(0), data_len, pid, true); + td_pool[1].token = pid + | (((data_len.saturating_sub(1)) as u32 & 0x7FF) << 21) + | (u32::from(device_address & 0x7F) << 8) + | (1u32 << 19); + + td_pool[2] = build_status_td(if dir_in { PID_OUT } else { PID_IN }); + td_pool[2].token = (if dir_in { PID_OUT } else { PID_IN }) + | (u32::from(device_address & 0x7F) << 8); + + td_pool[0].link = make_td_link(self.get_td_pool_phys() + 1 * size_of::() as u32); + td_pool[1].link = make_td_link(self.get_td_pool_phys() + 2 * size_of::() as u32); + td_pool[1].status |= TD_IOC; + td_pool[2].link = POINTER_TERMINATE; + } else { + td_pool[1] = build_status_td(if dir_in { PID_OUT } else { PID_IN }); + td_pool[1].token = (if dir_in { PID_OUT } else { PID_IN }) + | (u32::from(device_address & 0x7F) << 8); + td_pool[1].status |= TD_IOC; + + td_pool[0].link = make_td_link(self.get_td_pool_phys() + 1 * size_of::() as u32); + td_pool[1].link = POINTER_TERMINATE; + } + } + + fn execute_qh(&mut self, first_td_phys: u32, _td_count: usize) { + let current_qh_idx = 0; + if current_qh_idx < self.control_qhs.len() { + self.control_qhs[current_qh_idx].element = first_td_phys; + } + + fence(Ordering::SeqCst); + } + + fn wait_for_completion( + &mut self, + td_pool: &[TransferDescriptor], + td_count: usize, + ) -> Result { + for _ in 0..CONTROL_TRANSFER_TIMEOUT_POLLS { + let mut active = false; + let mut error_token = None; + + for i in 0..td_count.min(td_pool.len()) { + let status = td_pool[i].status; + if status & TD_ACTIVE != 0 { + active = true; + } + if status & (TD_STALLED | TD_BUFFER_ERR | TD_BABBLE | TD_CRC_TIMEOUT | TD_BITSTUFF) != 0 { + error_token = Some(status); + break; + } + } + + if let Some(token) = error_token { + return Err(format!("transfer error: 0x{:08X}", token)); + } + + if !active { + if td_count > 0 && td_pool.len() >= 1 { + let actual = td_pool[0].actual_length(); + return Ok(actual); + } + return Ok(0); + } + + thread::sleep(WAIT_STEP); + } + + Err("transfer timed out".to_string()) + } + + fn execute_control_request( + &mut self, + port: usize, + request: &ControlRequest, + ) -> Result, String> { + let Some(device) = self + .ports + .get(port) + .and_then(|record| record.device.clone()) + else { + return Err(format!("port {} is not enumerated", port + 1)); + }; + + let mut data = if request.request_type & 0x80 != 0 { + vec![0u8; usize::from(request.length)] + } else { + request.data.clone() + }; + + let setup = SetupPacket { + request_type: request.request_type, + request: request.request, + value: request.value, + index: request.index, + length: request.length, + }; + + let actual = self + .submit_control_transfer( + port, + device.address, + device.max_packet_size0, + &setup, + &mut data, + ) + .map_err(|err| format!("control transfer failed: {err:?}"))?; + + if request.request_type & 0x80 != 0 { + data.truncate(actual); + Ok(data) + } else { + Ok(format!("ok transferred={}\n", actual).into_bytes()) + } + } + + fn port_record(&self, port: usize) -> Option<&PortRecord> { + self.ports.get(port) + } +} + +impl UhciScheme { + fn new(controller: Arc>) -> Self { + Self { + controller, + handles: BTreeMap::new(), + next_id: SCHEME_ROOT_ID + 1, + } + } + + fn alloc_handle(&mut self, kind: HandleKind) -> usize { + let id = self.next_id; + self.next_id += 1; + self.handles.insert( + id, + HandleState { + kind, + response: Vec::new(), + }, + ); + id + } + + fn handle(&self, id: usize) -> Result<&HandleState, String> { + self.handles.get(&id).ok_or_else(|| "bad handle".to_string()) + } + + fn parse_port_component(&self, component: &str) -> Result { + let Some(raw_port) = component.strip_prefix("port") else { + return Err("not a port".to_string()); + }; + let port_number = raw_port.parse::().map_err(|_| "invalid port")?; + if port_number == 0 { + return Err("port number 0 invalid".to_string()); + } + let port_index = port_number - 1; + let controller = lock_controller(&self.controller); + if port_index >= controller.port_count() { + return Err("port out of range".to_string()); + } + Ok(port_index) + } + + fn resolve_root_path(&self, path: &str) -> Result { + let mut parts = path.split('/'); + let Some(port_component) = parts.next() else { + return Err("empty path".to_string()); + }; + let port = self.parse_port_component(port_component)?; + + match (parts.next(), parts.next()) { + (None, None) => Ok(HandleKind::PortDir { port }), + (Some("status"), None) => Ok(HandleKind::Status { port }), + (Some("descriptor"), None) => Ok(HandleKind::Descriptor { port }), + (Some("control"), None) => Ok(HandleKind::Control { port }), + (Some("config"), None) => Ok(HandleKind::Config { port }), + _ => Err("invalid path".to_string()), + } + } + + fn resolve_port_child(&self, port: usize, path: &str) -> Result { + match path { + "status" => Ok(HandleKind::Status { port }), + "descriptor" => Ok(HandleKind::Descriptor { port }), + "control" => Ok(HandleKind::Control { port }), + "config" => Ok(HandleKind::Config { port }), + _ => Err("invalid child path".to_string()), + } + } + + fn root_listing(&self) -> Vec { + let controller = lock_controller(&self.controller); + let mut listing = String::new(); + for port in 0..controller.port_count() { + let _ = writeln!(&mut listing, "port{}", port + 1); + } + listing.into_bytes() + } + + fn config_bytes(&self, port: usize) -> Result, String> { + let ctrl = self.controller.lock().map_err(|_| "lock failed")?; + let record = ctrl.ports.get(port).ok_or("no such port")?; + if let Some(ref dev) = record.device { + Ok(format!( + "class={:02x} subclass={:02x} vendor={:04x} device={:04x}\n", + dev.device_class, dev.device_subclass, dev.vendor_id, dev.product_id, + ).into_bytes()) + } else { + Ok(b"no device\n".to_vec()) + } + } + + fn status_bytes(&self, port: usize) -> Result, String> { + let controller = lock_controller(&self.controller); + let record = controller.port_record(port).ok_or("no such port")?; + + let status = record + .last_status + .clone() + .unwrap_or_else(|| decode_port_status(record.last_portsc)); + + let mut out = String::new(); + let _ = writeln!(&mut out, "port={}", port + 1); + let _ = writeln!(&mut out, "portsc=0x{:04x}", record.last_portsc); + let _ = writeln!(&mut out, "connected={}", status.connected); + let _ = writeln!(&mut out, "enabled={}", status.enabled); + let _ = writeln!(&mut out, "suspended={}", status.suspend); + let _ = writeln!(&mut out, "reset={}", status.reset); + let _ = writeln!(&mut out, "low_speed={}", status.low_speed); + + if let Some(device) = record.device.as_ref() { + let _ = writeln!(&mut out, "address={}", device.address); + let _ = writeln!(&mut out, "vendor_id=0x{:04x}", device.vendor_id); + let _ = writeln!(&mut out, "product_id=0x{:04x}", device.product_id); + } + + if let Some(last_error) = record.last_error.as_ref() { + let _ = writeln!(&mut out, "last_error={}", last_error); + } + + Ok(out.into_bytes()) + } + + fn descriptor_bytes(&self, port: usize) -> Result, String> { + let controller = lock_controller(&self.controller); + let record = controller.port_record(port).ok_or("no such port")?; + + let Some(device) = record.device.as_ref() else { + return Ok(b"state=unenumerated\n".to_vec()); + }; + + let mut out = String::new(); + let _ = writeln!(&mut out, "address={}", device.address); + let _ = writeln!(&mut out, "vendor_id=0x{:04x}", device.vendor_id); + let _ = writeln!(&mut out, "product_id=0x{:04x}", device.product_id); + let _ = writeln!(&mut out, "device_class=0x{:02x}", device.device_class); + let _ = writeln!(&mut out, "max_packet_size0={}", device.max_packet_size0); + let _ = writeln!(&mut out, "device_descriptor={}", hex_encode(&device.device_descriptor)); + + if !device.config_descriptor.is_empty() { + let _ = writeln!(&mut out, "config_descriptor={}", hex_encode(&device.config_descriptor)); + } + + Ok(out.into_bytes()) + } + + fn handle_control_write(&mut self, port: usize, buf: &[u8]) -> Result, String> { + let request = parse_control_request(buf)?; + let mut controller = lock_controller(&self.controller); + controller.execute_control_request(port, &request) + } + + fn handle_bytes(&self, id: usize) -> Result, String> { + if id == SCHEME_ROOT_ID { + return Ok(self.root_listing()); + } + + let handle = self.handle(id)?; + match &handle.kind { + HandleKind::PortDir { .. } => Ok(b"status\ndescriptor\ncontrol\nconfig\n".to_vec()), + HandleKind::Status { port } => self.status_bytes(*port), + HandleKind::Descriptor { port } => self.descriptor_bytes(*port), + HandleKind::Control { .. } => Ok(handle.response.clone()), + HandleKind::Config { port } => self.config_bytes(*port), + } + } + + fn handle_path(&self, id: usize) -> Result { + if id == SCHEME_ROOT_ID { + return Ok(format!("{}:/", SCHEME_NAME)); + } + + let handle = self.handle(id)?; + let path = match handle.kind.clone() { + HandleKind::PortDir { port } => format!("{}:/port{}", SCHEME_NAME, port + 1), + HandleKind::Status { port } => format!("{}:/port{}/status", SCHEME_NAME, port + 1), + HandleKind::Descriptor { port } => format!("{}:/port{}/descriptor", SCHEME_NAME, port + 1), + HandleKind::Control { port } => format!("{}:/port{}/control", SCHEME_NAME, port + 1), + HandleKind::Config { port } => format!("{}:/port{}/config", SCHEME_NAME, port + 1), + }; + Ok(path) + } +} + +fn init_logging() { + let _ = log::set_logger(&LOGGER); + log::set_max_level(LevelFilter::Info); +} + +fn lock_controller(shared: &Arc>) -> MutexGuard<'_, UhciController> { + match shared.lock() { + Ok(guard) => guard, + Err(poisoned) => { + warn!("uhcid: controller mutex was poisoned; continuing with recovered state"); + poisoned.into_inner() + } + } +} + +fn bool_word(value: bool) -> &'static str { + if value { "yes" } else { "no" } +} + +fn parse_device_descriptor(data: &[u8]) -> Option { + if data.len() < 18 || data[0] != 18 { + return None; + } + Some(DeviceDescriptor { + length: data[0], + descriptor_type: data[1], + bcd_usb: u16::from_le_bytes([data[2], data[3]]), + device_class: data[4], + device_subclass: data[5], + device_protocol: data[6], + max_packet_size0: data[7], + vendor_id: u16::from_le_bytes([data[8], data[9]]), + product_id: u16::from_le_bytes([data[10], data[11]]), + bcd_device: u16::from_le_bytes([data[12], data[13]]), + manufacturer: data[14], + product: data[15], + serial_number: data[16], + num_configurations: data[17], + }) +} + +#[derive(Debug)] +struct DeviceDescriptor { + length: u8, + descriptor_type: u8, + bcd_usb: u16, + device_class: u8, + device_subclass: u8, + device_protocol: u8, + max_packet_size0: u8, + vendor_id: u16, + product_id: u16, + bcd_device: u16, + manufacturer: u8, + product: u8, + serial_number: u8, + num_configurations: u8, +} + +#[derive(Debug, Clone, Copy)] +struct SetupPacket { + request_type: u8, + request: u8, + value: u16, + index: u16, + length: u16, +} + +fn setup_packet_bytes(setup: &SetupPacket) -> [u8; 8] { + [ + setup.request_type, + setup.request, + (setup.value & 0xFF) as u8, + ((setup.value >> 8) & 0xFF) as u8, + (setup.index & 0xFF) as u8, + ((setup.index >> 8) & 0xFF) as u8, + (setup.length & 0xFF) as u8, + ((setup.length >> 8) & 0xFF) as u8, + ] +} + +fn parse_control_request(buf: &[u8]) -> Result { + if buf.len() < 8 { + return Err("buffer too short for control request".to_string()); + } + + let request_type = buf[0]; + let request = buf[1]; + let value = u16::from_le_bytes([buf[2], buf[3]]); + let index = u16::from_le_bytes([buf[4], buf[5]]); + let length = u16::from_le_bytes([buf[6], buf[7]]); + + let data = if length > 0 && buf.len() > 8 { + buf[8..8 + usize::from(length)].to_vec() + } else { + Vec::new() + }; + + Ok(ControlRequest { + request_type, + request, + value, + index, + length, + data, + }) +} + +fn hex_encode(data: &[u8]) -> String { + data.iter().map(|b| format!("{:02x}", b)).collect::>().join("") +} + +fn copy_with_offset(buf: &mut [u8], offset: u64, data: &[u8]) -> Result { + let offset = offset as usize; + if offset >= data.len() { + return Ok(0); + } + let remaining = &data[offset..]; + let to_copy = remaining.len().min(buf.len()); + buf[..to_copy].copy_from_slice(&remaining[..to_copy]); + Ok(to_copy) +} + +#[derive(Debug)] +pub struct UsbError { + pub message: String, +} + +impl std::fmt::Display for UsbError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "USB error: {}", self.message) + } +} + +impl std::error::Error for UsbError {} + +fn main() { + init_logging(); + + let fd = match env::var("PCID_CLIENT_CHANNEL") { + Ok(s) => s.parse::().map_err(|_| "invalid PCID_CLIENT_CHANNEL").unwrap(), + Err(_) => { + error!("PCID_CLIENT_CHANNEL not set"); + process::exit(1); + } + }; + + let device_path = env::var("PCID_DEVICE_PATH").unwrap_or_default(); + info!("Starting UHCI driver for {}", device_path); + + let controller = match UhciController::new(&device_path, fd) { + Ok(ctrl) => Arc::new(Mutex::new(ctrl)), + Err(err) => { + error!("failed to initialize UHCI controller: {}", err); + process::exit(1); + } + }; + + let mut scheme = UhciScheme::new(controller.clone()); + let root_id = scheme.scheme_root().unwrap_or(SCHEME_ROOT_ID); + info!("UHCI scheme registered as {}:/", SCHEME_NAME); + + loop { + thread::sleep(PORT_POLL_INTERVAL); + + let mut ctrl = lock_controller(&controller); + ctrl.poll_ports_once(); + } +} + +impl UhciScheme { + fn scheme_root(&mut self) -> Result { + Ok(SCHEME_ROOT_ID) + } +} \ No newline at end of file diff --git a/local/recipes/drivers/uhcid/source/src/registers.rs b/local/recipes/drivers/uhcid/source/src/registers.rs index aaeebaf858..dd9f3389c7 100644 --- a/local/recipes/drivers/uhcid/source/src/registers.rs +++ b/local/recipes/drivers/uhcid/source/src/registers.rs @@ -1,31 +1,220 @@ #![allow(dead_code)] + +// UHCI (Universal Host Controller Interface) Register Layout +// Reference: Intel UHCI Specification, Universal Serial Bus Specification +// +// I/O Port Register Layout (from I/O base): +// USBCMD (0x00) - Command register +// USBSTS (0x02) - Status register +// USBINTR (0x04) - Interrupt enable register +// FRNUM (0x06) - Frame number register +// FLBASEADD(0x08) - Frame list base address (MMIO, 32-bit) +// SOFMOD (0x0C) - Start of frame modify +// PORTSC1 (0x10) - Port 1 status/control +// PORTSC2 (0x12) - Port 2 status/control +// +// Additional registers (for extended ports): +// PORTSC3 (0x14) - Port 3 status/control +// PORTSC4 (0x16) - Port 4 status/control +// +// PCI Configuration: +// BAR4 (offset 0x20): I/O port base address (low 16 bits, 32-byte range) +// BAR5 (offset 0x24): MMIO base for FLBASEADD (frame list pointer) + +use core::mem::size_of; + +// I/O port register offsets (from I/O base) pub const USBCMD: u16 = 0x00; pub const USBSTS: u16 = 0x02; pub const USBINTR: u16 = 0x04; pub const FRNUM: u16 = 0x06; -pub const FRBASEADD: u16 = 0x08; +pub const FLBASEADD: u16 = 0x08; pub const SOFMOD: u16 = 0x0C; pub const PORTSC1: u16 = 0x10; pub const PORTSC2: u16 = 0x12; +pub const PORTSC3: u16 = 0x14; +pub const PORTSC4: u16 = 0x16; -pub const CMD_RUN_STOP: u16 = 1 << 0; -pub const CMD_HOST_RESET: u16 = 1 << 1; -pub const CMD_GLOBAL_RESET: u16 = 1 << 2; -pub const CMD_CONFIGURE: u16 = 1 << 6; -pub const CMD_MAX_PACKET_64: u16 = 1 << 7; +// USBCMD bits +pub const CMD_RUN_STOP: u16 = 1 << 0; // RS: Run/Stop +pub const CMD_HOST_RESET: u16 = 1 << 1; // HCRESET: Host Controller Reset +pub const CMD_GLOBAL_RESET: u16 = 1 << 2; // GRESET: Global Reset +pub const CMD_EGSM: u16 = 1 << 3; // EGSM: Enter Global Suspend Mode +pub const CMD_FGR: u16 = 1 << 4; // FGR: Force Global Resume +pub const CMD_SW_DEBUG: u16 = 1 << 5; // SWDBG: Software Debug +pub const CMD_CONFIGURE: u16 = 1 << 6; // CF: Configure Flag +pub const CMD_MAX_PACKET: u16 = 1 << 7; // MAXP: Max Packet (0=32, 1=64 bytes) -pub const STS_INTERRUPT: u16 = 1 << 0; -pub const STS_ERROR: u16 = 1 << 1; -pub const STS_RESUME: u16 = 1 << 2; -pub const STS_HOST_ERROR: u16 = 1 << 3; -pub const STS_HALTED: u16 = 1 << 5; +// USBSTS bits +pub const STS_INTERRUPT: u16 = 1 << 0; // USBINT: USB Interrupt +pub const STS_ERROR: u16 = 1 << 1; // USBERRINT: USB Error Interrupt +pub const STS_RESUME: u16 = 1 << 2; // RD: Resume Detect +pub const STS_HOST_ERROR: u16 = 1 << 3; // HSE: Host System Error +pub const STS_HC_PROCESS_ERROR: u16 = 1 << 4; // HCPE: Host Controller Process Error +pub const STS_HALTED: u16 = 1 << 5; // HCH: HC Halted +pub const STS_FLR: u16 = 1 << 3; // FLR: Frame List Rollover (same as HSE in some docs) -pub const PORT_CONNECT: u16 = 1 << 0; -pub const PORT_ENABLE: u16 = 1 << 1; -pub const PORT_SUSPEND: u16 = 1 << 2; -pub const PORT_OVER_CURRENT: u16 = 1 << 3; -pub const PORT_RESET: u16 = 1 << 4; -pub const PORT_LOW_SPEED: u16 = 1 << 8; +// USBINTR bits +pub const INTR_TIMEOUT_CRC: u16 = 1 << 0; // Timeout/CRC error enable +pub const INTR_RESUME: u16 = 1 << 1; // Resume interrupt enable +pub const INTR_IOC: u16 = 1 << 2; // Interrupt On Complete enable +pub const INTR_SHORT_PACKET: u16 = 1 << 3; // Short packet interrupt enable -pub const FRAME_COUNT: usize = 1024; -pub const FRAME_LIST_ALIGN: usize = 4096; +// PORTSC bits +pub const PORTSC_CONNECT: u16 = 1 << 0; // CCS: Current Connect Status +pub const PORTSC_CONNECT_CHANGE: u16 = 1 << 1; // CSC: Connect Status Change +pub const PORTSC_ENABLE: u16 = 1 << 2; // PE: Port Enable +pub const PORTSC_ENABLE_CHANGE: u16 = 1 << 3; // PEC: Port Enable Change +pub const PORTSC_LINE_STATUS: u16 = 0x3 << 10; // Line status bits +pub const PORTSC_RESUME: u16 = 1 << 6; // RD: Resume Detect +pub const PORTSC_LOW_SPEED: u16 = 1 << 8; // LSDA: Low Speed Device Attached +pub const PORTSC_RESET: u16 = 1 << 9; // PR: Port Reset +pub const PORTSC_SUSPEND: u16 = 1 << 12; // SUSP: Suspend +pub const PORTSC_OVER_CURRENT: u16 = 1 << 3; // OC: Over Current (bit 3) +pub const PORTSC_OVER_CURRENT_CHANGE: u16 = 1 << 4; // OCC: Over Current Change + +// Frame list configuration +pub const FRAME_LIST_SIZE: usize = 1024; +pub const FRAME_LIST_ALIGN: usize = 4096; // 4KB alignment required + +// Pointer types +pub const POINTER_TERMINATE: u32 = 0x0001; // T: Terminator bit +pub const POINTER_QH: u32 = 0x0002; // Q: Queue Head +pub const POINTER_DEPTH: u32 = 0x0004; // D: Depth-first (for ISO) + +// TD (Transfer Descriptor) token bits +pub const TD_ACTIVE: u32 = 1 << 23; // Active bit +pub const TD_STALLED: u32 = 1 << 22; // Stalled +pub const TD_BUFFER_ERR: u32 = 1 << 21; // Data Buffer Error +pub const TD_BABBLE: u32 = 1 << 20; // Babble Detected +pub const TD_NAK: u32 = 1 << 19; // NAK Received +pub const TD_CRC_TIMEOUT: u32 = 1 << 18; // CRC/Time Out Error +pub const TD_BITSTUFF: u32 = 1 << 17; // Bit Stuff Error +pub const TD_TOGGLE: u32 = 1 << 19; // Data toggle bit +pub const TD_IOC: u32 = 1 << 24; // Interrupt on Complete +pub const TD_IOS: u32 = 1 << 25; // Isochronous Select +pub const TD_LS: u32 = 1 << 26; // Low Speed Device +pub const TD_ERROR_MASK: u32 = 0x3 << 27; // Error count mask +pub const TD_LEN_MASK: u32 = 0x7FF << 21; // Length mask (encoded as len-1) + +// TD packet IDs +pub const PID_OUT: u32 = 0xE1; // OUT token (host->device) +pub const PID_IN: u32 = 0x69; // IN token (device->host) +pub const PID_SETUP: u32 = 0x2D; // SETUP token (control) + +// Queue Head structure (16-byte aligned) +#[derive(Clone, Copy, Debug)] +#[repr(C, align(16))] +pub struct QueueHead { + pub link: u32, // Next QH link pointer + pub element: u32, // Queue element (TD) pointer +} + +impl QueueHead { + pub fn new() -> Self { + QueueHead { + link: POINTER_TERMINATE, + element: POINTER_TERMINATE, + } + } +} + +// Transfer Descriptor structure (16-byte aligned) +#[derive(Clone, Copy, Debug)] +#[repr(C, align(16))] +pub struct TransferDescriptor { + pub link: u32, // Next TD link pointer + pub status: u32, // Status and control + pub token: u32, // Token (PID, device addr, endpoint, length) + pub buffer: u32, // Buffer pointer (physical address) +} + +impl TransferDescriptor { + pub fn new() -> Self { + TransferDescriptor { + link: POINTER_TERMINATE, + status: 0, + token: 0, + buffer: 0, + } + } + + pub fn is_active(&self) -> bool { + (self.status & TD_ACTIVE) != 0 + } + + pub fn actual_length(&self) -> usize { + let len = (self.status >> 21) & 0x7FF; + (len as usize).saturating_add(1) + } + + pub fn error_occurred(&self) -> bool { + self.status & (TD_STALLED | TD_BUFFER_ERR | TD_BABBLE | TD_CRC_TIMEOUT | TD_BITSTUFF) != 0 + } +} + +// Build a setup phase TD +pub fn build_setup_td(buffer_phys: u32) -> TransferDescriptor { + TransferDescriptor { + link: POINTER_TERMINATE, + status: TD_ACTIVE | 0x02 << 27, // 3 error retries + token: PID_SETUP | (7 << 21) | (0 << 8), // 8 bytes, device 0 + buffer: buffer_phys, + } +} + +// Build a data phase TD +pub fn build_data_td(buffer_phys: u32, len: usize, pid: u32, toggle: bool) -> TransferDescriptor { + let encoded_len = if len == 0 { 0 } else { len - 1 }; + TransferDescriptor { + link: POINTER_TERMINATE, + status: TD_ACTIVE | TD_IOC | 0x02 << 27, // 3 error retries, IOC + token: pid | ((encoded_len as u32 & 0x7FF) << 21) | (if toggle { 1 } else { 0 } << 19), + buffer: buffer_phys, + } +} + +// Build a status phase TD (no data, IOC) +pub fn build_status_td(pid: u32) -> TransferDescriptor { + TransferDescriptor { + link: POINTER_TERMINATE, + status: TD_ACTIVE | TD_IOC | 0x02 << 27, // 3 error retries, IOC + token: pid | (0 << 21), // 0 bytes + buffer: 0, + } +} + +// Link pointer helpers +pub fn make_td_link(phys: u32) -> u32 { + phys | 0x0001 // Add terminate bit (software adds it when needed) +} + +pub fn make_qh_link(phys: u32) -> u32 { + (phys & !0x000F) | POINTER_QH | POINTER_TERMINATE +} + +// Port status decoding +#[derive(Clone, Debug, Default)] +pub struct PortStatus { + pub connected: bool, + pub enabled: bool, + pub low_speed: bool, + pub suspend: bool, + pub reset: bool, + pub over_current: bool, + pub connect_change: bool, + pub enable_change: bool, +} + +pub fn decode_port_status(sc: u16) -> PortStatus { + PortStatus { + connected: (sc & PORTSC_CONNECT) != 0, + enabled: (sc & PORTSC_ENABLE) != 0, + low_speed: (sc & PORTSC_LOW_SPEED) != 0, + suspend: (sc & PORTSC_SUSPEND) != 0, + reset: (sc & PORTSC_RESET) != 0, + over_current: (sc & PORTSC_OVER_CURRENT) != 0, + connect_change: (sc & PORTSC_CONNECT_CHANGE) != 0, + enable_change: (sc & PORTSC_ENABLE_CHANGE) != 0, + } +} \ No newline at end of file diff --git a/local/recipes/kde/plasma-framework/source/.clang-format b/local/recipes/kde/plasma-framework/source/.clang-format new file mode 100644 index 0000000000..9b5ae0af2d --- /dev/null +++ b/local/recipes/kde/plasma-framework/source/.clang-format @@ -0,0 +1,95 @@ +--- +# SPDX-FileCopyrightText: 2019 Christoph Cullmann +# SPDX-FileCopyrightText: 2019 Gernot Gebhard +# +# SPDX-License-Identifier: MIT + +# This file got automatically created by ECM, do not edit +# See https://clang.llvm.org/docs/ClangFormatStyleOptions.html for the config options +# and https://community.kde.org/Policies/Frameworks_Coding_Style#Clang-format_automatic_code_formatting +# for clang-format tips & tricks +--- +Language: JavaScript +DisableFormat: true +--- +Language: Json +DisableFormat: false +IndentWidth: 4 +--- + +# Style for C++ +Language: Cpp + +# base is WebKit coding style: https://webkit.org/code-style-guidelines/ +# below are only things set that diverge from this style! +BasedOnStyle: WebKit + +# enforce C++11 (e.g. for std::vector> +Standard: Cpp11 + +# 4 spaces indent +TabWidth: 4 + +# 2 * 80 wide lines +ColumnLimit: 160 + +# sort includes inside line separated groups +SortIncludes: true + +# break before braces on function, namespace and class definitions. +BreakBeforeBraces: Linux + +# CrlInstruction *a; +PointerAlignment: Right + +# horizontally aligns arguments after an open bracket. +AlignAfterOpenBracket: Align + +# don't move all parameters to new line +AllowAllParametersOfDeclarationOnNextLine: false + +# no single line functions +AllowShortFunctionsOnASingleLine: None + +# no single line enums +AllowShortEnumsOnASingleLine: false + +# always break before you encounter multi line strings +AlwaysBreakBeforeMultilineStrings: true + +# don't move arguments to own lines if they are not all on the same +BinPackArguments: false + +# don't move parameters to own lines if they are not all on the same +BinPackParameters: false + +# In case we have an if statement with multiple lines the operator should be at the beginning of the line +# but we do not want to break assignments +BreakBeforeBinaryOperators: NonAssignment + +# format C++11 braced lists like function calls +Cpp11BracedListStyle: true + +# do not put a space before C++11 braced lists +SpaceBeforeCpp11BracedList: false + +# remove empty lines +KeepEmptyLinesAtTheStartOfBlocks: false + +# no namespace indentation to keep indent level low +NamespaceIndentation: None + +# we use template< without space. +SpaceAfterTemplateKeyword: false + +# Always break after template declaration +AlwaysBreakTemplateDeclarations: true + +# macros for which the opening brace stays attached. +ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE , wl_resource_for_each, wl_resource_for_each_safe ] + +# keep lambda formatting multi-line if not empty +AllowShortLambdasOnASingleLine: Empty + +# We do not want clang-format to put all arguments on a new line +AllowAllArgumentsOnNextLine: false diff --git a/local/recipes/libs/libdrm/source/xf86drm.c b/local/recipes/libs/libdrm/source/xf86drm.c index 306026e8b7..207c456e05 100644 --- a/local/recipes/libs/libdrm/source/xf86drm.c +++ b/local/recipes/libs/libdrm/source/xf86drm.c @@ -861,6 +861,15 @@ unsigned long redox_translate_request(unsigned long linux_nr) /* DRM_IOCTL_GET_PCIINFO — PCI device information for driver discovery */ case 0x15: return REDOX_DRM_IOCTL_GET_PCI_INFO; + /* DRM auth/master ioctls */ + case 0x02: /* GET_MAGIC — DRM_IOR(0x02, struct drm_auth) */ + return REDOX_DRM_IOCTL_GET_MAGIC; + case 0x11: /* AUTH_MAGIC — DRM_IOW(0x11, struct drm_auth) */ + return REDOX_DRM_IOCTL_AUTH_MAGIC; + case 0x1e: /* SET_MASTER — DRM_IO(0x1e) */ + return REDOX_DRM_IOCTL_SET_MASTER; + case 0x1f: /* DROP_MASTER — DRM_IO(0x1f) */ + return REDOX_DRM_IOCTL_DROP_MASTER; default: return 0; } @@ -972,6 +981,10 @@ static size_t redox_drm_expected_response_size(unsigned long linux_nr, size_t ar case 0xA6: case 0xB3: return arg_size; + case 0x02: /* GET_MAGIC — returns struct drm_auth (magic u32) */ + return arg_size; + case 0x11: /* AUTH_MAGIC — returns struct drm_auth (magic u32) */ + return arg_size; default: return 0; } diff --git a/local/recipes/libs/libdrm/source/xf86drm_redox.h b/local/recipes/libs/libdrm/source/xf86drm_redox.h index 7f9d252fa1..425b07922e 100644 --- a/local/recipes/libs/libdrm/source/xf86drm_redox.h +++ b/local/recipes/libs/libdrm/source/xf86drm_redox.h @@ -40,6 +40,10 @@ #define REDOX_DRM_IOCTL_VIRTGPU_RESOURCE_CREATE_BLOB (REDOX_DRM_IOCTL_BASE + 40) #define REDOX_DRM_IOCTL_VIRTGPU_CONTEXT_INIT (REDOX_DRM_IOCTL_BASE + 41) +#define REDOX_DRM_IOCTL_GET_MAGIC (REDOX_DRM_IOCTL_BASE + 33) +#define REDOX_DRM_IOCTL_AUTH_MAGIC (REDOX_DRM_IOCTL_BASE + 34) +#define REDOX_DRM_IOCTL_SET_MASTER (REDOX_DRM_IOCTL_BASE + 35) +#define REDOX_DRM_IOCTL_DROP_MASTER (REDOX_DRM_IOCTL_BASE + 36) #define REDOX_DRM_IOCTL_GET_PCI_INFO (REDOX_DRM_IOCTL_BASE + 0x60) struct redox_drm_resources_wire { diff --git a/local/recipes/qt/qtbase/source/mkspecs/common/posix/qplatformdefs.h b/local/recipes/qt/qtbase/source/mkspecs/common/posix/qplatformdefs.h index d9ea568e95..808d59ddd5 100644 --- a/local/recipes/qt/qtbase/source/mkspecs/common/posix/qplatformdefs.h +++ b/local/recipes/qt/qtbase/source/mkspecs/common/posix/qplatformdefs.h @@ -382,6 +382,166 @@ #endif #endif +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + +#ifdef Q_OS_REDOX +#undef QT_USE_XOPEN_LFS_EXTENSIONS +#undef QT_LARGEFILE_SUPPORT +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif +#endif + // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only diff --git a/local/recipes/qt/qtbase/source/src/corelib/CMakeLists.txt b/local/recipes/qt/qtbase/source/src/corelib/CMakeLists.txt index 4923a8ee58..16609bf0a0 100644 --- a/local/recipes/qt/qtbase/source/src/corelib/CMakeLists.txt +++ b/local/recipes/qt/qtbase/source/src/corelib/CMakeLists.txt @@ -1536,6 +1536,146 @@ qt_internal_extend_target(Core CONDITION REDOX io/qstorageinfo_unix.cpp ) +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + qt_internal_extend_target(Core CONDITION QT_FEATURE_cpp_winrt SOURCES platform/windows/qfactorycacheregistration_p.h @@ -1900,6 +2040,146 @@ qt_internal_extend_target(Core CONDITION REDOX io/qstorageinfo_unix.cpp ) +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + +# Redox: POSIX statvfs, not Linux statfs +qt_internal_extend_target(Core CONDITION REDOX + SOURCES + io/qstandardpaths_unix.cpp + io/qstorageinfo_unix.cpp +) + qt_internal_extend_target(Core CONDITION QT_FEATURE_itemmodel SOURCES itemmodels/qabstractitemmodel.cpp itemmodels/qabstractitemmodel.h itemmodels/qabstractitemmodel_p.h diff --git a/local/recipes/qt/qtbase/source/src/corelib/global/qtypes.h b/local/recipes/qt/qtbase/source/src/corelib/global/qtypes.h index cd541b6bf4..8e1323576a 100644 --- a/local/recipes/qt/qtbase/source/src/corelib/global/qtypes.h +++ b/local/recipes/qt/qtbase/source/src/corelib/global/qtypes.h @@ -225,6 +225,26 @@ static_assert(std::is_signed_v, #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #ifndef static_assert #define static_assert _Static_assert #endif diff --git a/local/recipes/qt/qtbase/source/src/network/socket/qnativesocketengine_unix.cpp b/local/recipes/qt/qtbase/source/src/network/socket/qnativesocketengine_unix.cpp index ae490ad4af..61069b3c8a 100644 --- a/local/recipes/qt/qtbase/source/src/network/socket/qnativesocketengine_unix.cpp +++ b/local/recipes/qt/qtbase/source/src/network/socket/qnativesocketengine_unix.cpp @@ -1169,6 +1169,26 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l #ifdef IPV6_HOPLIMIT #ifdef IPV6_HOPLIMIT #ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT +#ifdef IPV6_HOPLIMIT #ifdef IPV6_HOPLIMIT if (header.hopLimit != -1) { msg.msg_controllen += CMSG_SPACE(sizeof(int)); @@ -1225,6 +1245,26 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l #endif #endif #endif +#endif +#endif +#endif +#endif +#endif +#endif +#endif +#endif +#endif +#endif +#endif +#endif +#endif +#endif +#endif +#endif +#endif +#endif +#endif +#endif #endif if (header.ifindex != 0 || !header.senderAddress.isNull()) { struct in6_pktinfo *data = reinterpret_cast(CMSG_DATA(cmsgptr)); diff --git a/local/recipes/qt/qtbase/source/src/network/socket/qnet_unix_p.h b/local/recipes/qt/qtbase/source/src/network/socket/qnet_unix_p.h index 5e6f0073e2..02f0256425 100644 --- a/local/recipes/qt/qtbase/source/src/network/socket/qnet_unix_p.h +++ b/local/recipes/qt/qtbase/source/src/network/socket/qnet_unix_p.h @@ -69,6 +69,26 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #if defined(Q_OS_VXWORKS) diff --git a/local/recipes/qt/qtbase/source/src/plugins/platforms/wayland/hardwareintegration/qwaylandclientbufferintegration_p.h b/local/recipes/qt/qtbase/source/src/plugins/platforms/wayland/hardwareintegration/qwaylandclientbufferintegration_p.h index 6650b45fb5..a26c631e7e 100644 --- a/local/recipes/qt/qtbase/source/src/plugins/platforms/wayland/hardwareintegration/qwaylandclientbufferintegration_p.h +++ b/local/recipes/qt/qtbase/source/src/plugins/platforms/wayland/hardwareintegration/qwaylandclientbufferintegration_p.h @@ -99,6 +99,26 @@ public: #if QT_CONFIG(opengl) #if QT_CONFIG(opengl) #if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) #if QT_CONFIG(opengl) virtual QPlatformOpenGLContext *createPlatformOpenGLContext(const QSurfaceFormat &glFormat, QPlatformOpenGLContext *share) const = 0; #endif /* QT_CONFIG(opengl) */ @@ -148,6 +168,26 @@ public: #endif /* QT_CONFIG(opengl) */ #endif /* QT_CONFIG(opengl) */ #endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ #endif /* QT_CONFIG(opengl) */ virtual bool canCreatePlatformOffscreenSurface() const { return false; } #if QT_CONFIG(opengl) @@ -208,6 +248,26 @@ public: #if QT_CONFIG(opengl) #if QT_CONFIG(opengl) #if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) +#if QT_CONFIG(opengl) #if QT_CONFIG(opengl) virtual void *nativeResourceForContext(NativeResource /*resource*/, QPlatformOpenGLContext */*context*/) { return nullptr; } #endif /* QT_CONFIG(opengl) */ @@ -258,6 +318,26 @@ public: #endif /* QT_CONFIG(opengl) */ #endif /* QT_CONFIG(opengl) */ #endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ +#endif /* QT_CONFIG(opengl) */ }; } diff --git a/local/recipes/qt/qtdeclarative/source/src/qml/jit/qv4assemblercommon_p.h b/local/recipes/qt/qtdeclarative/source/src/qml/jit/qv4assemblercommon_p.h index b4bd1e3c2e..93f60c0c75 100644 --- a/local/recipes/qt/qtdeclarative/source/src/qml/jit/qv4assemblercommon_p.h +++ b/local/recipes/qt/qtdeclarative/source/src/qml/jit/qv4assemblercommon_p.h @@ -34,7 +34,7 @@ namespace QV4 { namespace JIT { #if defined(Q_PROCESSOR_X86_64) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES) -#if defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_FREEBSD) || defined(Q_OS_DARWIN) || defined(Q_OS_SOLARIS) || defined(Q_OS_VXWORKS) || defined(Q_OS_HURD) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) +#if defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_FREEBSD) || defined(Q_OS_DARWIN) || defined(Q_OS_SOLARIS) || defined(Q_OS_VXWORKS) || defined(Q_OS_HURD) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) || defined(Q_OS_REDOX) class PlatformAssembler_X86_64_SysV : public JSC::MacroAssembler { diff --git a/local/scripts/rebuild-cascade.sh b/local/scripts/rebuild-cascade.sh index ae031896d4..73f68d4395 100755 --- a/local/scripts/rebuild-cascade.sh +++ b/local/scripts/rebuild-cascade.sh @@ -64,60 +64,112 @@ fi cd "${ROOT_DIR}" -# Collect all recipe directories -RECIPE_DIRS=() -for pkg in "${PACKAGES[@]}"; do - # Find the recipe directory - found=0 - for dir in recipes/*/; do - if [ -d "${dir}${pkg}" ]; then - RECIPE_DIRS+=("${dir}${pkg}") - found=1 - break - fi - done - # Also check local/recipes - if [ $found -eq 0 ]; then - for dir in local/recipes/*/; do - if [ -d "${dir}${pkg}" ]; then - RECIPE_DIRS+=("${dir}${pkg}") - found=1 - break +# Precompute the entire dependency graph in a single pass. With 3000+ recipes, +# doing per-target awk invocations in the BFS loop is O(N²) and unworkable. +# Precompute once, query in O(1). Function definitions must come before +# their use; place them here so the index build below can call them. + +extract_recipe_deps() { + local pkg_dir="$1" + local recipe_toml="${pkg_dir}/recipe.toml" + local deps="" + + if [ ! -f "${recipe_toml}" ]; then + echo "" + return + fi + + # Multi-line dependencies = [ ... ] extraction. Handles `]` on its own line. + local in_deps=0 + while IFS= read -r line; do + if [ "${in_deps}" = 1 ]; then + # Strip whitespace, quotes, trailing commas + local item="${line}" + item="${item## }"; item="${item%% }" + item="${item#\"}"; item="${item%\"}" + item="${item%,}" + [ -n "${item}" ] && deps+="${item}," + # Section closes on a line that is just `]` or contains `]` + if [[ "${line}" =~ ^[[:space:]]*\] ]]; then + in_deps=0 fi - done - fi - if [ $found -eq 0 ]; then - echo "ERROR: recipe not found for package '${pkg}'" >&2 - exit 1 + continue + fi + # Check if this line starts `dependencies = [` + if [[ "${line}" =~ ^[[:space:]]*dependencies[[:space:]]*=[[:space:]]*\[ ]]; then + in_deps=1 + local inline="${line#*=}" + inline="${inline#[}" + if [[ "${inline}" == *\]* ]]; then + in_deps=0 + inline="${inline%]*}" + for item in ${inline//,/ }; do + item="${item## }"; item="${item%% }" + item="${item#\"}"; item="${item%\"}" + [ -n "${item}" ] && deps+="${item}," + done + fi + fi + done < "${recipe_toml}" + + echo "${deps}" +} + +recipe_source_dir() { + local pkg_dir="$1" + local recipe_toml="${pkg_dir}/recipe.toml" + [ -f "${recipe_toml}" ] || return 0 + local rel + rel="$(awk '/^\[source\]/{flag=1; next} /^\[/{flag=0} flag' \ + "${recipe_toml}" 2>/dev/null | \ + awk -F'=' '/^path[[:space:]]*=/{gsub(/[" ]/, "", $2); print $2; exit}')" + if [ -n "${rel}" ]; then + (cd "${pkg_dir}" && cd "${rel}" 2>/dev/null && pwd) fi +} + +mapfile -t ALL_RECIPE_TOMLS < <(find recipes/ local/recipes/ -name "recipe.toml" 2>/dev/null) +echo "Cached ${#ALL_RECIPE_TOMLS[@]} recipe.toml paths" + +# recipe_index maps pkg_name -> "pkg_dir|recipe_toml|depends_csv" +declare -A recipe_index=() +for recipe_toml in "${ALL_RECIPE_TOMLS[@]}"; do + pkg_dir="$(dirname "${recipe_toml}")" + pkg_name="$(basename "${pkg_dir}")" + deps="$(extract_recipe_deps "${pkg_dir}")" + recipe_index["${pkg_name}"]="${pkg_dir}|${recipe_toml}|${deps}" done -# Find all recipes that depend on any of the target packages -# by scanning their recipe.toml for dependency entries +# Find all recipes that depend on the target by querying the precomputed +# index. Returns newline-separated pkg names that depend on target. find_reverse_deps() { local target="$1" local result=() - # Search all recipe.toml files for dependencies - while IFS= read -r -d '' recipe_toml; do - # Get the package name from the directory - pkg_dir="$(dirname "${recipe_toml}")" - pkg_name="$(basename "${pkg_dir}")" - - # Skip the target itself + local pkg_name entry + for pkg_name in "${!recipe_index[@]}"; do if [ "${pkg_name}" = "${target}" ]; then continue fi - - # Check if this recipe depends on the target - if grep -q "dependencies.*=.*\[.*${target}.*\]" "${recipe_toml}" 2>/dev/null; then + entry="${recipe_index[${pkg_name}]}" + # entry format: "pkg_dir|recipe_toml|deps_csv" + local deps_csv="${entry##*|}" + if [[ ",${deps_csv}," == *",${target},"* ]]; then result+=("${pkg_name}") fi - done < <(find recipes/ local/recipes/ -name "recipe.toml" -print0 2>/dev/null) + done printf '%s\n' "${result[@]}" | sort -u } +# Validate that the requested package names exist in the index +for pkg in "${PACKAGES[@]}"; do + if [ -z "${recipe_index[${pkg}]+_}" ]; then + echo "ERROR: recipe not found for package '${pkg}'" >&2 + exit 1 + fi +done + # Build a complete cascade set using BFS echo "=== Analyzing dependency cascade ===" CASCADE=() diff --git a/local/sources/base b/local/sources/base index 676af02e08..dc04956a31 160000 --- a/local/sources/base +++ b/local/sources/base @@ -1 +1 @@ -Subproject commit 676af02e08434451b35d57ecf4d325c72ac5eff6 +Subproject commit dc04956a31eb64dbb911e0bf0bd9bd408036660d diff --git a/local/sources/kernel b/local/sources/kernel index a4ba465a86..4b3f6e3e81 160000 --- a/local/sources/kernel +++ b/local/sources/kernel @@ -1 +1 @@ -Subproject commit a4ba465a862f28e42d853fb54eb8e55ca50023b4 +Subproject commit 4b3f6e3e812ca92e66c83d3dfce20203ce5acda2 diff --git a/recipes/libs/pciaccess-stub b/recipes/libs/pciaccess-stub new file mode 120000 index 0000000000..02f8d08e7d --- /dev/null +++ b/recipes/libs/pciaccess-stub @@ -0,0 +1 @@ +../../local/recipes/libs/pciaccess-stub \ No newline at end of file diff --git a/recipes/wip/x11/libxau/recipe.toml b/recipes/wip/x11/libxau/recipe.toml index 598866b427..7878285ee0 100644 --- a/recipes/wip/x11/libxau/recipe.toml +++ b/recipes/wip/x11/libxau/recipe.toml @@ -10,8 +10,10 @@ template = "custom" script = """ DYNAMIC_INIT -# Fix autotools timestamp skew — make tries to regenerate aclocal.m4 -touch "${COOKBOOK_SOURCE}/aclocal.m4" "${COOKBOOK_SOURCE}/configure" "${COOKBOOK_SOURCE}/Makefile.in" +export ACLOCAL=true +export AUTOMAKE=true +export AUTOCONF=true +export AUTOHEADER=true cookbook_configure """