diff --git a/xhcid/src/driver_interface.rs b/xhcid/src/driver_interface.rs index 7fbc3b1f49..c11dc1dc01 100644 --- a/xhcid/src/driver_interface.rs +++ b/xhcid/src/driver_interface.rs @@ -180,6 +180,7 @@ impl EndpDesc { } pub fn isoch_mult(&self, lec: bool) -> u8 { if !lec && self.is_isoch() { + if self.is_superspeedplus() { return 0 } self.ssc .as_ref() .map(|ssc| ssc.attributes & 0x3) diff --git a/xhcid/src/xhci/extended.rs b/xhcid/src/xhci/extended.rs index a128a778b4..2330b29f0d 100644 --- a/xhcid/src/xhci/extended.rs +++ b/xhcid/src/xhci/extended.rs @@ -114,13 +114,28 @@ impl ProtocolSpeed { Self { a: Mmio::from(raw) } } pub fn is_lowspeed(&self) -> bool { - self.psim() == 1500 && self.psie() == Psie::Kbps + self.psim() == 1500 && self.psie() == Psie::Kbps && !self.pfd() } pub fn is_fullspeed(&self) -> bool { - self.psim() == 12 && self.psie() == Psie::Mbps + self.psim() == 12 && self.psie() == Psie::Mbps && !self.pfd() } pub fn is_highspeed(&self) -> bool { - self.psim() == 480 && self.psie() == Psie::Mbps + self.psim() == 480 && self.psie() == Psie::Mbps && !self.pfd() + } + pub fn is_superspeed_gen1x1(&self) -> bool { + self.psim() == 5 && self.psie() == Psie::Gbps && self.pfd() && self.lp() == Lp::SuperSpeed + } + pub fn is_superspeedplus_gen2x1(&self) -> bool { + self.psim() == 10 && self.psie() == Psie::Gbps && self.pfd() && self.lp() == Lp::SuperSpeedPlus + } + pub fn is_superspeedplus_gen1x2(&self) -> bool { + self.psim() == 10 && self.psie() == Psie::Gbps && self.pfd() && self.lp() == Lp::SuperSpeedPlus + } + pub fn is_superspeedplus_gen2x2(&self) -> bool { + self.psim() == 20 && self.psie() == Psie::Gbps && self.pfd() && self.lp() == Lp::SuperSpeedPlus + } + pub fn is_superspeed_gen_x(&self) -> bool { + self.is_superspeed_gen1x1() || self.is_superspeedplus_gen2x1() || self.is_superspeedplus_gen1x2() || self.is_superspeedplus_gen2x2() } /// Protocol speed ID value pub fn psiv(&self) -> u8 { diff --git a/xhcid/src/xhci/mod.rs b/xhcid/src/xhci/mod.rs index 6f3cd5d157..bf9c6a251a 100644 --- a/xhcid/src/xhci/mod.rs +++ b/xhcid/src/xhci/mod.rs @@ -344,49 +344,8 @@ impl Xhci { println!(" - Slot {}", slot); - // transfer ring? - let mut ring = Ring::new(true)?; - let mut input = Dma::::zeroed()?; - { - input.add_context.write(1 << 1 | 1); // Enable the slot (zeroth bit) and the control endpoint (first bit). - - input - .device - .slot - .a - .write((1 << 27) | (u32::from(speed) << 20)); // FIXME: The speed field, bits 23:20, is deprecated. - input.device.slot.b.write(((i as u32 + 1) & 0xFF) << 16); - - // control endpoint? - input.device.endpoints[0] - .b - .write(4096 << 16 | 4 << 3 | 3 << 1); // packet size | control endpoint | allowed error count - let tr = ring.register(); - input.device.endpoints[0].trh.write((tr >> 32) as u32); - input.device.endpoints[0].trl.write(tr as u32); - } - - { - let (cmd, cycle, event) = self.cmd.next(); - - cmd.address_device(slot, input.physical(), cycle); - - self.dbs[0].write(0); - - while event.data.read() == 0 { - println!(" - Waiting for event"); - } - - if event.completion_code() != TrbCompletionCode::Success as u8 - || event.trb_type() != TrbType::CommandCompletion as u8 - { - panic!("ADDRESS DEVICE FAILED"); - } - - cmd.reserved(false); - event.reserved(false); - } + let mut ring = self.address_device(&mut input, i, slot, speed)?; let dev_desc = Self::get_dev_desc_raw( &mut self.ports, @@ -397,6 +356,9 @@ impl Xhci { slot, &mut ring, )?; + + self.update_default_control_pipe(&mut input, slot, &dev_desc)?; + let mut port_state = PortState { slot, input_context: input, @@ -424,6 +386,142 @@ impl Xhci { Ok(()) } + pub fn update_default_control_pipe(&mut self, input_context: &mut Dma, slot_id: u8, dev_desc: &DevDesc) -> Result<()> { + let new_max_packet_size = if dev_desc.major_version() == 2 { + u32::from(dev_desc.packet_size) + } else { + 1u32 << dev_desc.packet_size + }; + let endp_ctx = &mut input_context.device.endpoints[0]; + let mut b = endp_ctx.b.read(); + b &= 0x0000_FFFF; + b |= (new_max_packet_size) << 16; + endp_ctx.b.write(b); + + { + let (cmd, cycle, event) = self.cmd.next(); + + cmd.evaluate_context(slot_id, input_context.physical(), false, cycle); + + self.dbs[0].write(0); + + while event.data.read() == 0 { + println!(" - Waiting for event"); + } + + if event.completion_code() != TrbCompletionCode::Success as u8 + || event.trb_type() != TrbType::CommandCompletion as u8 + { + panic!("EVALUATE_CONTEXT failed with {:#0x} {:#0x} {:#0x}", event.data.read(), event.status.read(), event.control.read()); + } + + cmd.reserved(false); + event.reserved(false); + } + Ok(()) + } + + pub fn address_device(&mut self, input_context: &mut Dma, i: usize, slot: u8, speed: u8) -> Result { + let mut ring = Ring::new(true)?; + + { + input_context.add_context.write(1 << 1 | 1); // Enable the slot (zeroth bit) and the control endpoint (first bit). + + let slot_ctx = &mut input_context.device.slot; + + let route_string = 0u32; // TODO + let context_entries = 1u8; + let mtt = false; + let hub = false; + + assert_eq!(route_string & 0x000F_FFFF, route_string); + slot_ctx.a.write( + route_string + | (u32::from(mtt) << 25) + | (u32::from(hub) << 26) + | (u32::from(context_entries) << 27) + ); + + let max_exit_latency = 0u16; + let root_hub_port_num = (i + 1) as u8; + let number_of_ports = 0u8; + slot_ctx.b.write( + u32::from(max_exit_latency) + | (u32::from(root_hub_port_num) << 16) + | (u32::from(number_of_ports) << 24) + ); + + // TODO + let parent_hud_slot_id = 0u8; + let parent_port_num = 0u8; + let ttt = 0u8; + let interrupter = 0u8; + + assert_eq!(ttt & 0b11, ttt); + slot_ctx.c.write( + u32::from(parent_hud_slot_id) + | (u32::from(parent_port_num) << 8) + | (u32::from(ttt) << 16) + | (u32::from(interrupter) << 22) + ); + + let endp_ctx = &mut input_context.device.endpoints[0]; + + let speed_id = self.lookup_psiv(root_hub_port_num, speed).expect("Failed to retrieve speed ID"); + + let max_error_count = 3u8; // recommended value according to the XHCI spec + let ep_ty = 4u8; // control endpoint, bidirectional + let max_packet_size: u32 = if speed_id.is_lowspeed() { + 8 // only valid value + } else if speed_id.is_fullspeed() { + 64 // valid values are 8, 16, 32, 64 + } else if speed_id.is_highspeed() { + 64 // only valid value + } else if speed_id.is_superspeed_gen_x() { + 512 // only valid value + } else { + unreachable!() + }; + let host_initiate_disable = false; // only applies to streams + let max_burst_size = 0u8; // TODO + + assert_eq!(max_error_count & 0b11, max_error_count); + endp_ctx.b.write( + (u32::from(max_error_count) << 1) + | (u32::from(ep_ty) << 3) + | (u32::from(host_initiate_disable) << 7) + | (u32::from(max_burst_size) << 8) + | (u32::from(max_packet_size) << 16) + ); + + let tr = ring.register(); + endp_ctx.trh.write((tr >> 32) as u32); + endp_ctx.trl.write(tr as u32); + } + + { + let (cmd, cycle, event) = self.cmd.next(); + + cmd.address_device(slot, input_context.physical(), false, cycle); + + self.dbs[0].write(0); + + while event.data.read() == 0 { + println!(" - Waiting for event"); + } + + if event.completion_code() != TrbCompletionCode::Success as u8 + || event.trb_type() != TrbType::CommandCompletion as u8 + { + panic!("ADDRESS DEVICE FAILED"); + } + + cmd.reserved(false); + event.reserved(false); + } + Ok(ring) + } + pub fn ring_command_doorbell(&mut self) { self.dbs[0].write(0); } diff --git a/xhcid/src/xhci/scheme.rs b/xhcid/src/xhci/scheme.rs index 3ca6a3953e..4bdbc60d7a 100644 --- a/xhcid/src/xhci/scheme.rs +++ b/xhcid/src/xhci/scheme.rs @@ -316,7 +316,7 @@ impl Xhci { } fn endp_ctx_max_packet_size(endp_desc: &EndpDesc) -> u16 { // TODO: Control endpoint? Encoding? - endp_desc.max_packet_size & 0x03FF + endp_desc.max_packet_size & 0x07FF } fn endp_ctx_max_esit_payload(speed_id: &ProtocolSpeed, dev_desc: &DevDesc, endp_desc: &EndpDesc, max_packet_size: u16, max_burst_size: u8) -> u32 { const KIB: u32 = 1024; diff --git a/xhcid/src/xhci/trb.rs b/xhcid/src/xhci/trb.rs index e6a92f7ba8..bc3e359f60 100644 --- a/xhcid/src/xhci/trb.rs +++ b/xhcid/src/xhci/trb.rs @@ -190,17 +190,22 @@ impl Trb { ); } - pub fn address_device(&mut self, slot_id: u8, input: usize, cycle: bool) { + pub fn address_device(&mut self, slot_id: u8, input_ctx_ptr: usize, bsr: bool, cycle: bool) { + assert_eq!( + input_ctx_ptr & 0xFFFF_FFFF_FFFF_FFF0, + input_ctx_ptr, + "unaligned input context ptr" + ); self.set( - input as u64, + input_ctx_ptr as u64, 0, - ((slot_id as u32) << 24) | ((TrbType::AddressDevice as u32) << 10) | (cycle as u32), + (u32::from(slot_id) << 24) | ((TrbType::AddressDevice as u32) << 10) | (u32::from(bsr) << 9) | u32::from(cycle), ); } // Synchronizes the input context endpoints with the device context endpoints, I think. pub fn configure_endpoint(&mut self, slot_id: u8, input_ctx_ptr: usize, cycle: bool) { assert_eq!( - input_ctx_ptr & 0xFFFF_FFFF_FFFF_FF80, + input_ctx_ptr & 0xFFFF_FFFF_FFFF_FFF0, input_ctx_ptr, "unaligned input context ptr" ); @@ -213,6 +218,18 @@ impl Trb { | u32::from(cycle), ); } + pub fn evaluate_context(&mut self, slot_id: u8, input_ctx_ptr: usize, bsr: bool, cycle: bool) { + assert_eq!( + input_ctx_ptr & 0xFFFF_FFFF_FFFF_FFF0, + input_ctx_ptr, + "unaligned input context ptr" + ); + self.set( + input_ctx_ptr as u64, + 0, + (u32::from(slot_id) << 24) | ((TrbType::EvaluateContext as u32) << 10) | (u32::from(bsr) << 9) | u32::from(cycle), + ); + } pub fn reset_endpoint(&mut self, slot_id: u8, endp_num_xhc: u8, tsp: bool, cycle: bool) { assert_eq!(endp_num_xhc & 0x1F, endp_num_xhc); self.set(