Support addressing of hub devices
This commit is contained in:
@@ -240,6 +240,7 @@ fn main() {
|
||||
config_desc: conf_desc.configuration_value,
|
||||
interface_desc: Some(interface_num),
|
||||
alternate_setting: Some(if_desc.alternate_setting),
|
||||
hub_ports: None,
|
||||
})
|
||||
.expect("Failed to configure endpoints");
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ fn daemon(daemon: redox_daemon::Daemon, scheme: String, port: PortId, protocol:
|
||||
config_desc: configuration_value,
|
||||
interface_desc: Some(interface_num),
|
||||
alternate_setting: Some(alternate_setting),
|
||||
hub_ports: None,
|
||||
})
|
||||
.expect("Failed to configure endpoints");
|
||||
|
||||
|
||||
+71
-22
@@ -19,7 +19,7 @@ fn main() {
|
||||
const USAGE: &'static str = "usbhubd <scheme> <port> <interface>";
|
||||
|
||||
let scheme = args.next().expect(USAGE);
|
||||
let port = args
|
||||
let port_id = args
|
||||
.next()
|
||||
.expect(USAGE)
|
||||
.parse::<PortId>()
|
||||
@@ -33,11 +33,11 @@ fn main() {
|
||||
log::info!(
|
||||
"USB HUB driver spawned with scheme `{}`, port {}, interface {}",
|
||||
scheme,
|
||||
port,
|
||||
port_id,
|
||||
interface_num
|
||||
);
|
||||
|
||||
let handle = XhciClientHandle::new(scheme, port);
|
||||
let handle = XhciClientHandle::new(scheme.clone(), port_id);
|
||||
let desc: DevDesc = handle
|
||||
.get_standard_descs()
|
||||
.expect("Failed to get standard descriptors");
|
||||
@@ -57,14 +57,6 @@ fn main() {
|
||||
})
|
||||
.expect("Failed to find suitable configuration");
|
||||
|
||||
handle
|
||||
.configure_endpoints(&ConfigureEndpointsReq {
|
||||
config_desc: conf_desc.configuration_value,
|
||||
interface_desc: Some(interface_num),
|
||||
alternate_setting: Some(if_desc.alternate_setting),
|
||||
})
|
||||
.expect("Failed to configure endpoints");
|
||||
|
||||
let mut hub_desc = usb::HubDescriptor::default();
|
||||
handle
|
||||
.device_request(
|
||||
@@ -78,10 +70,69 @@ fn main() {
|
||||
)
|
||||
.expect("Failed to retrieve hub descriptor");
|
||||
|
||||
handle
|
||||
.configure_endpoints(&ConfigureEndpointsReq {
|
||||
config_desc: conf_desc.configuration_value,
|
||||
interface_desc: Some(interface_num),
|
||||
alternate_setting: Some(if_desc.alternate_setting),
|
||||
hub_ports: Some(hub_desc.ports),
|
||||
})
|
||||
.expect("Failed to configure endpoints");
|
||||
|
||||
/*TODO: only set hub depth on USB 3+ hubs
|
||||
handle
|
||||
.device_request(
|
||||
PortReqTy::Class,
|
||||
PortReqRecipient::Device,
|
||||
0x0c, // SET_HUB_DEPTH
|
||||
port_id.hub_depth().into(),
|
||||
0,
|
||||
DeviceReqData::NoData,
|
||||
)
|
||||
.expect("Failed to set hub depth");
|
||||
*/
|
||||
|
||||
// Initialize states
|
||||
struct PortState {
|
||||
port_id: PortId,
|
||||
port_sts: usb::HubPortStatus,
|
||||
handle: XhciClientHandle,
|
||||
attached: bool,
|
||||
}
|
||||
|
||||
impl PortState {
|
||||
pub fn ensure_attached(&mut self, attached: bool) {
|
||||
if attached == self.attached {
|
||||
return;
|
||||
}
|
||||
|
||||
if attached {
|
||||
self.handle.attach().expect("Failed to attach");
|
||||
} else {
|
||||
self.handle.detach().expect("Failed to detach");
|
||||
}
|
||||
|
||||
self.attached = attached;
|
||||
}
|
||||
}
|
||||
|
||||
let mut states = Vec::new();
|
||||
for port in 1..=hub_desc.ports {
|
||||
let child_port_id = port_id.child(port).expect("Cannot get child port ID");
|
||||
states.push(PortState {
|
||||
port_id: child_port_id,
|
||||
port_sts: usb::HubPortStatus::default(),
|
||||
handle: XhciClientHandle::new(scheme.clone(), child_port_id),
|
||||
attached: false,
|
||||
});
|
||||
}
|
||||
|
||||
//TODO: use change flags?
|
||||
let mut last_port_statuses = vec![usb::HubPortStatus::default(); hub_desc.ports.into()];
|
||||
loop {
|
||||
for port in 1..=hub_desc.ports {
|
||||
let port_idx: usize = port.checked_sub(1).unwrap().into();
|
||||
let mut state = states.get_mut(port_idx).unwrap();
|
||||
|
||||
let mut port_sts = usb::HubPortStatus::default();
|
||||
handle
|
||||
.device_request(
|
||||
@@ -93,14 +144,9 @@ fn main() {
|
||||
DeviceReqData::In(unsafe { plain::as_mut_bytes(&mut port_sts) }),
|
||||
)
|
||||
.expect("Failed to retrieve port status");
|
||||
|
||||
{
|
||||
let port_idx: usize = port.checked_sub(1).unwrap().into();
|
||||
let last_port_sts = last_port_statuses.get_mut(port_idx).unwrap();
|
||||
if *last_port_sts != port_sts {
|
||||
*last_port_sts = port_sts;
|
||||
log::info!("port {} status {:X?}", port, port_sts);
|
||||
}
|
||||
if state.port_sts != port_sts {
|
||||
state.port_sts = port_sts;
|
||||
log::info!("port {} status {:X?}", port, port_sts);
|
||||
}
|
||||
|
||||
// Ensure port is powered on
|
||||
@@ -116,17 +162,19 @@ fn main() {
|
||||
DeviceReqData::NoData,
|
||||
)
|
||||
.expect("Failed to set port power");
|
||||
state.ensure_attached(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore disconnected port
|
||||
//TODO: turn off disconnected ports?
|
||||
if !port_sts.contains(usb::HubPortStatus::CONNECTION) {
|
||||
state.ensure_attached(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore port in reset
|
||||
if port_sts.contains(usb::HubPortStatus::RESET) {
|
||||
state.ensure_attached(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -143,10 +191,11 @@ fn main() {
|
||||
DeviceReqData::NoData,
|
||||
)
|
||||
.expect("Failed to set port enable");
|
||||
state.ensure_attached(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
//TODO: address device
|
||||
state.ensure_attached(true);
|
||||
}
|
||||
|
||||
//TODO: use interrupts or poll faster?
|
||||
|
||||
@@ -20,6 +20,7 @@ pub struct ConfigureEndpointsReq {
|
||||
pub config_desc: u8,
|
||||
pub interface_desc: Option<u8>,
|
||||
pub alternate_setting: Option<u8>,
|
||||
pub hub_ports: Option<u8>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
@@ -290,6 +291,33 @@ impl PortId {
|
||||
pub fn root_hub_port_index(&self) -> usize {
|
||||
self.root_hub_port_num.checked_sub(1).unwrap().into()
|
||||
}
|
||||
|
||||
pub fn hub_depth(&self) -> u8 {
|
||||
let mut hub_depth = 0;
|
||||
let mut route_string = self.route_string;
|
||||
while route_string > 0 {
|
||||
route_string >>= 4;
|
||||
hub_depth += 1;
|
||||
}
|
||||
hub_depth
|
||||
}
|
||||
|
||||
pub fn child(&self, value: u8) -> Result<Self, String> {
|
||||
let depth = self.hub_depth();
|
||||
if depth >= 5 {
|
||||
return Err(format!("too many route string components"));
|
||||
}
|
||||
if value & 0xF0 != 0 {
|
||||
return Err(format!(
|
||||
"value {:?} is too large for route string component",
|
||||
value
|
||||
));
|
||||
}
|
||||
Ok(Self {
|
||||
root_hub_port_num: self.root_hub_port_num,
|
||||
route_string: self.route_string | u32::from(value) << (depth * 4),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PortId {
|
||||
@@ -299,8 +327,7 @@ impl fmt::Display for PortId {
|
||||
// The Route String is a 20-bit field in downstream directed packets that the hub uses to route
|
||||
// each packet to the designated downstream port. It is composed of a concatenation of the
|
||||
// downstream port numbers (4 bits per hub) for each hub traversed to reach a device.
|
||||
// The lowest 4 bits are ignored.
|
||||
let mut route_string = self.route_string >> 4;
|
||||
let mut route_string = self.route_string;
|
||||
while route_string > 0 {
|
||||
write!(f, ".{}", route_string & 0xF)?;
|
||||
route_string >>= 4;
|
||||
@@ -320,6 +347,12 @@ impl str::FromStr for PortId {
|
||||
.parse()
|
||||
.map_err(|e| format!("failed to parse {:?}: {}", part, e))?;
|
||||
|
||||
// Neither root hub port number nor route string support 0 components
|
||||
// to identify downstream ports
|
||||
if value == 0 {
|
||||
return Err(format!("zero is not a valid port ID component"));
|
||||
}
|
||||
|
||||
// Parse root hub port number
|
||||
if i == 0 {
|
||||
root_hub_port_num = value;
|
||||
@@ -327,7 +360,8 @@ impl str::FromStr for PortId {
|
||||
}
|
||||
|
||||
// Parse route string component
|
||||
if i > 5 {
|
||||
let depth = i - 1;
|
||||
if depth >= 5 {
|
||||
return Err(format!("too many route string components"));
|
||||
}
|
||||
if value & 0xF0 != 0 {
|
||||
@@ -336,13 +370,7 @@ impl str::FromStr for PortId {
|
||||
value
|
||||
));
|
||||
}
|
||||
route_string |= (value as u32) << (i * 4);
|
||||
}
|
||||
if root_hub_port_num == 0 {
|
||||
return Err(format!(
|
||||
"invalid root hub port number {:?}",
|
||||
root_hub_port_num
|
||||
));
|
||||
route_string |= u32::from(value) << (depth * 4);
|
||||
}
|
||||
Ok(Self {
|
||||
root_hub_port_num,
|
||||
@@ -485,6 +513,18 @@ impl XhciClientHandle {
|
||||
Self { scheme, port }
|
||||
}
|
||||
|
||||
pub fn attach(&self) -> result::Result<(), XhciClientHandleError> {
|
||||
let path = format!("/scheme/{}/port{}/attach", self.scheme, self.port);
|
||||
let mut file = OpenOptions::new().read(false).write(true).open(path)?;
|
||||
let _bytes_written = file.write(&[])?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn detach(&self) -> result::Result<(), XhciClientHandleError> {
|
||||
let path = format!("/scheme/{}/port{}/detach", self.scheme, self.port);
|
||||
let mut file = OpenOptions::new().read(false).write(true).open(path)?;
|
||||
let _bytes_written = file.write(&[])?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn get_standard_descs(&self) -> result::Result<DevDesc, XhciClientHandleError> {
|
||||
let path = format!("/scheme/{}/port{}/descriptors", self.scheme, self.port);
|
||||
let json = std::fs::read(path)?;
|
||||
|
||||
@@ -739,6 +739,7 @@ impl Xhci {
|
||||
|
||||
pub async fn attach_device(&self, port_id: PortId) -> syscall::Result<()> {
|
||||
if self.port_states.contains_key(&port_id) {
|
||||
println!("Already contains port {}", port_id);
|
||||
return Err(syscall::Error::new(EAGAIN));
|
||||
}
|
||||
|
||||
|
||||
+120
-8
@@ -52,6 +52,10 @@ use regex::Regex;
|
||||
lazy_static! {
|
||||
static ref REGEX_PORT_CONFIGURE: Regex = Regex::new(r"^port([\d\.]+)/configure$")
|
||||
.expect("Failed to create the regex for the port<n>/configure scheme.");
|
||||
static ref REGEX_PORT_ATTACH: Regex = Regex::new(r"^port([\d\.]+)/attach$")
|
||||
.expect("Failed to create the regex for the port<n>/attach scheme.");
|
||||
static ref REGEX_PORT_DETACH: Regex = Regex::new(r"^port([\d\.]+)/detach$")
|
||||
.expect("Failed to create the regex for the port<n>/detach scheme.");
|
||||
static ref REGEX_PORT_DESCRIPTORS: Regex = Regex::new(r"^port([\d\.]+)/descriptors$")
|
||||
.expect("Failed to create the regex for the port<n>/descriptors");
|
||||
static ref REGEX_PORT_STATE: Regex = Regex::new(r"^port([\d\.]+)/state$")
|
||||
@@ -131,6 +135,8 @@ pub enum Handle {
|
||||
Endpoints(PortId, Vec<u8>), // port, contents
|
||||
Endpoint(PortId, u8, EndpointHandleTy), // port, endpoint, state
|
||||
ConfigureEndpoints(PortId), // port
|
||||
AttachDevice(PortId), // port
|
||||
DetachDevice(PortId), // port
|
||||
}
|
||||
|
||||
/// The type of handle.
|
||||
@@ -170,6 +176,10 @@ enum SchemeParameters {
|
||||
Endpoint(PortId, u8, String), // port number, endpoint number, handle type
|
||||
/// /port<n>/configure
|
||||
ConfigureEndpoints(PortId), // port number
|
||||
/// /port<n>/attach
|
||||
AttachDevice(PortId), // port number
|
||||
/// /port<n>/detach
|
||||
DetachDevice(PortId), // port number
|
||||
}
|
||||
|
||||
impl Handle {
|
||||
@@ -212,6 +222,12 @@ impl Handle {
|
||||
Handle::ConfigureEndpoints(port_num) => {
|
||||
format!("port{}/configure", port_num)
|
||||
}
|
||||
Handle::AttachDevice(port_num) => {
|
||||
format!("port{}/attach", port_num)
|
||||
}
|
||||
Handle::DetachDevice(port_num) => {
|
||||
format!("port{}/detach", port_num)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,6 +252,8 @@ impl Handle {
|
||||
&Handle::PortState(_) => HandleType::Character,
|
||||
&Handle::PortReq(_, _) => HandleType::Character,
|
||||
&Handle::ConfigureEndpoints(_) => HandleType::Character,
|
||||
&Handle::AttachDevice(_) => HandleType::Character,
|
||||
&Handle::DetachDevice(_) => HandleType::Character,
|
||||
&Handle::Endpoint(_, _, ref st) => match st {
|
||||
EndpointHandleTy::Data => HandleType::Character,
|
||||
EndpointHandleTy::Ctl => HandleType::Character,
|
||||
@@ -264,6 +282,8 @@ impl Handle {
|
||||
&Handle::PortState(_) => None,
|
||||
&Handle::PortReq(_, _) => None,
|
||||
&Handle::ConfigureEndpoints(_) => None,
|
||||
&Handle::AttachDevice(_) => None,
|
||||
&Handle::DetachDevice(_) => None,
|
||||
&Handle::Endpoint(_, _, ref st) => match st {
|
||||
EndpointHandleTy::Data => None,
|
||||
EndpointHandleTy::Ctl => None,
|
||||
@@ -345,6 +365,14 @@ impl SchemeParameters {
|
||||
let port_num = get_port_id_from_regex(®EX_PORT_CONFIGURE, scheme, 0)?;
|
||||
|
||||
Ok(Self::ConfigureEndpoints(port_num))
|
||||
} else if REGEX_PORT_ATTACH.is_match(scheme) {
|
||||
let port_num = get_port_id_from_regex(®EX_PORT_ATTACH, scheme, 0)?;
|
||||
|
||||
Ok(Self::AttachDevice(port_num))
|
||||
} else if REGEX_PORT_DETACH.is_match(scheme) {
|
||||
let port_num = get_port_id_from_regex(®EX_PORT_DETACH, scheme, 0)?;
|
||||
|
||||
Ok(Self::DetachDevice(port_num))
|
||||
} else if REGEX_PORT_DESCRIPTORS.is_match(scheme) {
|
||||
let port_num = get_port_id_from_regex(®EX_PORT_DESCRIPTORS, scheme, 0)?;
|
||||
|
||||
@@ -971,13 +999,28 @@ impl Xhci {
|
||||
const CONTEXT_ENTRIES_MASK: u32 = 0xF800_0000;
|
||||
const CONTEXT_ENTRIES_SHIFT: u8 = 27;
|
||||
|
||||
let current_slot_a = input_context.device.slot.a.read();
|
||||
const HUB_PORTS_MASK: u32 = 0xFF00_0000;
|
||||
const HUB_PORTS_SHIFT: u8 = 24;
|
||||
|
||||
let mut current_slot_a = input_context.device.slot.a.read();
|
||||
let mut current_slot_b = input_context.device.slot.b.read();
|
||||
|
||||
// Set context entries
|
||||
current_slot_a &= !CONTEXT_ENTRIES_MASK;
|
||||
current_slot_a |=
|
||||
(u32::from(new_context_entries) << CONTEXT_ENTRIES_SHIFT) & CONTEXT_ENTRIES_MASK;
|
||||
|
||||
// Set hub data
|
||||
current_slot_a &= !(1 << 26);
|
||||
current_slot_b &= !HUB_PORTS_MASK;
|
||||
if let Some(hub_ports) = req.hub_ports {
|
||||
current_slot_a |= 1 << 26;
|
||||
current_slot_b |= (u32::from(hub_ports) << HUB_PORTS_SHIFT) & HUB_PORTS_MASK;
|
||||
}
|
||||
|
||||
input_context.device.slot.a.write(current_slot_a);
|
||||
input_context.device.slot.b.write(current_slot_b);
|
||||
|
||||
input_context.device.slot.a.write(
|
||||
(current_slot_a & !CONTEXT_ENTRIES_MASK)
|
||||
| ((u32::from(new_context_entries) << CONTEXT_ENTRIES_SHIFT)
|
||||
& CONTEXT_ENTRIES_MASK),
|
||||
);
|
||||
let control = if self.op.lock().unwrap().cie() {
|
||||
(u32::from(req.alternate_setting.unwrap_or(0)) << 16)
|
||||
| (u32::from(req.interface_desc.unwrap_or(0)) << 8)
|
||||
@@ -1966,6 +2009,54 @@ impl Xhci {
|
||||
Ok(Handle::ConfigureEndpoints(port_num))
|
||||
}
|
||||
|
||||
/// implements open() for /port<n>/attach
|
||||
///
|
||||
/// # Arguments
|
||||
/// - 'port_num: [PortId]' - The port number specified in the scheme path
|
||||
/// - 'flags: [usize]' - The flags parameter passed to open()
|
||||
///
|
||||
/// # Returns
|
||||
/// This function returns a [Result] containing either:
|
||||
///
|
||||
/// - [Handle::Port] - The handle was opened successfully
|
||||
/// - [EISDIR] - open() was called on this scheme endpoint, but no directory-specific flags were passed to open
|
||||
/// - [ENOENT] - The scheme is valid, but there is no port associated with the given port_num, or no endpoint with the given endpoint_num
|
||||
fn open_handle_attach_device(&self, port_num: PortId, flags: usize) -> Result<Handle> {
|
||||
if flags & O_DIRECTORY != 0 && flags & O_STAT == 0 {
|
||||
return Err(Error::new(ENOTDIR));
|
||||
}
|
||||
|
||||
if flags & O_RDWR != O_WRONLY && flags & O_STAT == 0 {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
|
||||
Ok(Handle::AttachDevice(port_num))
|
||||
}
|
||||
|
||||
/// implements open() for /port<n>/detach
|
||||
///
|
||||
/// # Arguments
|
||||
/// - 'port_num: [PortId]' - The port number specified in the scheme path
|
||||
/// - 'flags: [usize]' - The flags parameter passed to open()
|
||||
///
|
||||
/// # Returns
|
||||
/// This function returns a [Result] containing either:
|
||||
///
|
||||
/// - [Handle::Port] - The handle was opened successfully
|
||||
/// - [EISDIR] - open() was called on this scheme endpoint, but no directory-specific flags were passed to open
|
||||
/// - [ENOENT] - The scheme is valid, but there is no port associated with the given port_num, or no endpoint with the given endpoint_num
|
||||
fn open_handle_detach_device(&self, port_num: PortId, flags: usize) -> Result<Handle> {
|
||||
if flags & O_DIRECTORY != 0 && flags & O_STAT == 0 {
|
||||
return Err(Error::new(ENOTDIR));
|
||||
}
|
||||
|
||||
if flags & O_RDWR != O_WRONLY && flags & O_STAT == 0 {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
|
||||
Ok(Handle::DetachDevice(port_num))
|
||||
}
|
||||
|
||||
/// implements open() for /port<n>/request
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -2020,6 +2111,12 @@ impl Scheme for &Xhci {
|
||||
SchemeParameters::ConfigureEndpoints(port_number) => {
|
||||
self.open_handle_configure_endpoints(port_number, flags)?
|
||||
}
|
||||
SchemeParameters::AttachDevice(port_number) => {
|
||||
self.open_handle_attach_device(port_number, flags)?
|
||||
}
|
||||
SchemeParameters::DetachDevice(port_number) => {
|
||||
self.open_handle_detach_device(port_number, flags)?
|
||||
}
|
||||
};
|
||||
|
||||
let fd = self.next_handle.fetch_add(1, atomic::Ordering::Relaxed);
|
||||
@@ -2049,8 +2146,11 @@ impl Scheme for &Xhci {
|
||||
};
|
||||
|
||||
//If we have a handle to the configure scheme, we need to mark it as write only.
|
||||
if let &Handle::ConfigureEndpoints(_) = &*guard {
|
||||
stat.st_mode = stat.st_mode | 0o200;
|
||||
match &*guard {
|
||||
Handle::ConfigureEndpoints(_) | Handle::AttachDevice(_) | Handle::DetachDevice(_) => {
|
||||
stat.st_mode = stat.st_mode | 0o200;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
@@ -2098,6 +2198,8 @@ impl Scheme for &Xhci {
|
||||
Ok(bytes_to_read)
|
||||
}
|
||||
Handle::ConfigureEndpoints(_) => Err(Error::new(EBADF)),
|
||||
Handle::AttachDevice(_) => Err(Error::new(EBADF)),
|
||||
Handle::DetachDevice(_) => Err(Error::new(EBADF)),
|
||||
|
||||
&mut Handle::Endpoint(port_num, endp_num, ref mut st) => match st {
|
||||
EndpointHandleTy::Ctl => self.on_read_endp_ctl(port_num, endp_num, buf),
|
||||
@@ -2150,6 +2252,16 @@ impl Scheme for &Xhci {
|
||||
block_on(self.configure_endpoints(port_num, buf))?;
|
||||
Ok(buf.len())
|
||||
}
|
||||
&mut Handle::AttachDevice(port_num) => {
|
||||
//TODO: accept some arguments in buffer?
|
||||
block_on(self.attach_device(port_num))?;
|
||||
Ok(buf.len())
|
||||
}
|
||||
&mut Handle::DetachDevice(port_num) => {
|
||||
//TODO: accept some arguments in buffer?
|
||||
block_on(self.detach_device(port_num))?;
|
||||
Ok(buf.len())
|
||||
}
|
||||
&mut Handle::Endpoint(port_num, endp_num, ref ep_file_ty) => match ep_file_ty {
|
||||
EndpointHandleTy::Ctl => block_on(self.on_write_endp_ctl(port_num, endp_num, buf)),
|
||||
EndpointHandleTy::Data => {
|
||||
|
||||
Reference in New Issue
Block a user