Initial gamepad support
This commit is contained in:
Generated
+1
-1
@@ -1957,7 +1957,7 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||
[[package]]
|
||||
name = "rehid"
|
||||
version = "0.1.0"
|
||||
source = "git+https://gitlab.redox-os.org/redox-os/rehid.git#39d7ef05c97fd4e5e2cfd38b1a21f20d888c5e00"
|
||||
source = "git+https://gitlab.redox-os.org/redox-os/rehid.git#da987bc6d0f043bb025ece0d35804da2b250fab3"
|
||||
dependencies = [
|
||||
"hidreport",
|
||||
"log",
|
||||
|
||||
+367
-106
@@ -1,9 +1,15 @@
|
||||
use anyhow::{Context, Result};
|
||||
use std::{env, thread, time};
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
env,
|
||||
sync::mpsc,
|
||||
thread, time,
|
||||
};
|
||||
|
||||
use inputd::ProducerHandle;
|
||||
use orbclient::KeyEvent as OrbKeyEvent;
|
||||
use rehid::{
|
||||
hidreport::{Report, Usage},
|
||||
report_desc::{ReportTy, REPORT_DESC_TY},
|
||||
report_handler::ReportHandler,
|
||||
usage_tables::{GenericDesktopUsage, UsagePage},
|
||||
@@ -16,6 +22,7 @@ use xhcid_interface::{
|
||||
use crate::descs::HidDescriptor;
|
||||
|
||||
mod descs;
|
||||
mod overrides;
|
||||
mod reqs;
|
||||
|
||||
fn send_key_event(display: &mut ProducerHandle, usage_page: u16, usage: u16, pressed: bool) {
|
||||
@@ -173,6 +180,15 @@ fn main() -> Result<()> {
|
||||
.expect(USAGE)
|
||||
.parse::<u8>()
|
||||
.expect("Expected integer as input of interface");
|
||||
let mut report_desc_override = None;
|
||||
if let Some(override_name) = args.next() {
|
||||
for (name, bytes) in overrides::OVERRIDES.iter() {
|
||||
if name == &override_name {
|
||||
report_desc_override = Some(bytes.to_vec());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let name = format!("{}_{}_{}_hid", scheme, port, interface_num);
|
||||
common::setup_logging(
|
||||
@@ -204,7 +220,7 @@ fn main() -> Result<()> {
|
||||
log::debug!("{:X?}", desc);
|
||||
|
||||
let mut endp_count = 0;
|
||||
let (conf_desc, (if_desc, endp_desc_opt, hid_desc)) = desc
|
||||
let (conf_desc, (if_desc, endp_desc_opt, hid_desc_opt)) = desc
|
||||
.config_descs
|
||||
.iter()
|
||||
.find_map(|conf_desc| {
|
||||
@@ -215,16 +231,28 @@ fn main() -> Result<()> {
|
||||
if endp_desc.ty() == EndpointTy::Interrupt
|
||||
&& endp_desc.direction() == EndpDirection::In
|
||||
{
|
||||
log::warn!(
|
||||
"using endpoint 0x{:x} {:?} {:?}",
|
||||
endp_desc.address,
|
||||
endp_desc.ty(),
|
||||
endp_desc.direction()
|
||||
);
|
||||
Some((endp_count, endp_desc.clone()))
|
||||
} else {
|
||||
log::warn!(
|
||||
"ignoring endpoint 0x{:x} {:?} {:?}",
|
||||
endp_desc.address,
|
||||
endp_desc.ty(),
|
||||
endp_desc.direction()
|
||||
);
|
||||
None
|
||||
}
|
||||
});
|
||||
let hid_desc = if_desc.unknown_descs.iter().find_map(|unknown_desc| {
|
||||
let hid_desc_opt = if_desc.unknown_descs.iter().find_map(|unknown_desc| {
|
||||
//TODO: should we do any filtering?
|
||||
HidDescriptor::from_bytes(&unknown_desc.all_bytes).ok()
|
||||
})?;
|
||||
Some((if_desc.clone(), endp_desc_opt, hid_desc))
|
||||
});
|
||||
Some((if_desc.clone(), endp_desc_opt, hid_desc_opt))
|
||||
} else {
|
||||
endp_count += if_desc.endpoints.len();
|
||||
None
|
||||
@@ -247,21 +275,29 @@ fn main() -> Result<()> {
|
||||
|
||||
//TODO: dynamically create good values, fix xhcid so it does not block on each request
|
||||
// This sets all reports to a duration of 4ms
|
||||
reqs::set_idle(&handle, 1, 0, interface_num as u16).context("Failed to set idle")?;
|
||||
if let Err(err) = reqs::set_idle(&handle, 1, 0, interface_num as u16) {
|
||||
log::warn!("failed to set idle: {}", err);
|
||||
}
|
||||
|
||||
let report_desc_len = hid_desc.get_report_desc()?.desc_len;
|
||||
let report_desc_bytes = if let Some(hid_desc) = hid_desc_opt {
|
||||
let report_desc_len = hid_desc.get_report_desc()?.desc_len;
|
||||
|
||||
let mut report_desc_bytes = vec![0u8; report_desc_len as usize];
|
||||
handle
|
||||
.get_descriptor(
|
||||
PortReqRecipient::Interface,
|
||||
REPORT_DESC_TY,
|
||||
0,
|
||||
//TODO: should this be an index into interface_descs?
|
||||
interface_num as u16,
|
||||
&mut report_desc_bytes,
|
||||
)
|
||||
.context("Failed to retrieve report descriptor")?;
|
||||
let mut report_desc_bytes = vec![0u8; report_desc_len as usize];
|
||||
handle
|
||||
.get_descriptor(
|
||||
PortReqRecipient::Interface,
|
||||
REPORT_DESC_TY,
|
||||
0,
|
||||
//TODO: should this be an index into interface_descs?
|
||||
interface_num as u16,
|
||||
&mut report_desc_bytes,
|
||||
)
|
||||
.context("Failed to retrieve report descriptor")?;
|
||||
|
||||
report_desc_bytes
|
||||
} else {
|
||||
report_desc_override.expect("failed to find report descriptor")
|
||||
};
|
||||
|
||||
let mut handler =
|
||||
ReportHandler::new(&report_desc_bytes).expect("failed to parse report descriptor");
|
||||
@@ -270,9 +306,23 @@ fn main() -> Result<()> {
|
||||
Some((_endp_num, endp_desc)) => endp_desc.max_packet_size as usize,
|
||||
None => handler.total_byte_length as usize,
|
||||
};
|
||||
let mut report_buffer = vec![0u8; report_len];
|
||||
let report_ty = ReportTy::Input;
|
||||
let report_id = 0;
|
||||
|
||||
let mut report_collections = HashMap::new();
|
||||
for report in handler.descriptor.input_reports() {
|
||||
for field in report.fields() {
|
||||
let key = (report.report_id().map(u8::from), u32::from(field.id()));
|
||||
if let Some(old) = report_collections.get(&key) {
|
||||
log::warn!(
|
||||
"key {:?} has old collections {:?} and new collections {:?}",
|
||||
key,
|
||||
old,
|
||||
field.collections()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
report_collections.insert(key, field.collections().to_vec());
|
||||
}
|
||||
}
|
||||
|
||||
let mut display = ProducerHandle::new().context("Failed to open input socket")?;
|
||||
let mut endpoint_opt = match endp_desc_opt {
|
||||
@@ -285,107 +335,318 @@ fn main() -> Result<()> {
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
let (tx, rx) = mpsc::channel();
|
||||
thread::spawn(move || -> Result<()> {
|
||||
let mut report_buffer = vec![0u8; report_len];
|
||||
let report_ty = ReportTy::Input;
|
||||
let report_id = 0;
|
||||
loop {
|
||||
//TODO: get frequency from device
|
||||
//TODO: use sleeps when accuracy is better: thread::sleep(time::Duration::from_millis(10));
|
||||
let timer = time::Instant::now();
|
||||
while timer.elapsed() < time::Duration::from_millis(1) {
|
||||
thread::yield_now();
|
||||
}
|
||||
|
||||
if let Some(endpoint) = &mut endpoint_opt {
|
||||
// interrupt transfer
|
||||
endpoint
|
||||
.transfer_read(&mut report_buffer)
|
||||
.context("failed to get report")?;
|
||||
} else {
|
||||
// control transfer
|
||||
reqs::get_report(
|
||||
&handle,
|
||||
report_ty,
|
||||
report_id,
|
||||
//TODO: should this be an index into interface_descs?
|
||||
interface_num as u16,
|
||||
&mut report_buffer,
|
||||
)
|
||||
.context("failed to get report")?;
|
||||
}
|
||||
|
||||
tx.send(report_buffer.clone())
|
||||
.context("failed to send report to main thread")?;
|
||||
}
|
||||
});
|
||||
|
||||
let mut left_shift = false;
|
||||
let mut right_shift = false;
|
||||
let mut last_mouse_pos = (0, 0);
|
||||
let mut last_buttons = [false, false, false];
|
||||
let mut gamepad_state = HashMap::new();
|
||||
loop {
|
||||
//TODO: get frequency from device
|
||||
//TODO: use sleeps when accuracy is better: thread::sleep(time::Duration::from_millis(10));
|
||||
let timer = time::Instant::now();
|
||||
while timer.elapsed() < time::Duration::from_millis(1) {
|
||||
thread::yield_now();
|
||||
}
|
||||
|
||||
if let Some(endpoint) = &mut endpoint_opt {
|
||||
// interrupt transfer
|
||||
endpoint
|
||||
.transfer_read(&mut report_buffer)
|
||||
.context("failed to get report")?;
|
||||
} else {
|
||||
// control transfer
|
||||
reqs::get_report(
|
||||
&handle,
|
||||
report_ty,
|
||||
report_id,
|
||||
//TODO: should this be an index into interface_descs?
|
||||
interface_num as u16,
|
||||
&mut report_buffer,
|
||||
)
|
||||
.context("failed to get report")?;
|
||||
}
|
||||
|
||||
let mut mouse_pos = last_mouse_pos;
|
||||
let mut mouse_dx = 0i32;
|
||||
let mut mouse_dy = 0i32;
|
||||
let mut scroll_y = 0i32;
|
||||
let mut buttons = last_buttons;
|
||||
for event in handler
|
||||
.handle(&report_buffer)
|
||||
.expect("failed to parse report")
|
||||
{
|
||||
log::debug!("{}", event);
|
||||
if event.usage_page == UsagePage::GenericDesktop as u16 {
|
||||
if event.usage == GenericDesktopUsage::X as u16 {
|
||||
if event.relative {
|
||||
mouse_dx += event.value as i32;
|
||||
match rx.try_recv() {
|
||||
Ok(report_buffer) => {
|
||||
for event in handler
|
||||
.handle(&report_buffer)
|
||||
.expect("failed to parse report")
|
||||
{
|
||||
let mut gamepad = false;
|
||||
if let Some(collections) =
|
||||
report_collections.get(&(event.report_id, event.field_id))
|
||||
{
|
||||
for collection in collections {
|
||||
for usage in collection.usages() {
|
||||
match UsagePage::from_repr(usage.usage_page.into()) {
|
||||
Some(UsagePage::GenericDesktop) => {
|
||||
match GenericDesktopUsage::from_repr(usage.usage_id.into())
|
||||
{
|
||||
Some(GenericDesktopUsage::GamePad) => {
|
||||
gamepad = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mouse_pos.0 = event.value as i32;
|
||||
log::warn!(
|
||||
"collections missing for report {:?} field {}",
|
||||
event.report_id,
|
||||
event.field_id
|
||||
);
|
||||
}
|
||||
} else if event.usage == GenericDesktopUsage::Y as u16 {
|
||||
if event.relative {
|
||||
mouse_dy += event.value as i32;
|
||||
} else {
|
||||
mouse_pos.1 = event.value as i32;
|
||||
|
||||
// Special handling for gamepad
|
||||
//TODO: make this more generic
|
||||
if gamepad {
|
||||
let last = gamepad_state
|
||||
.get(&(event.usage_page, event.usage))
|
||||
.map_or(0, |x: &(i64, i64)| x.1);
|
||||
gamepad_state.insert((event.usage_page, event.usage), (event.value, last));
|
||||
continue;
|
||||
}
|
||||
} else if event.usage == GenericDesktopUsage::Wheel as u16 {
|
||||
//TODO: what is X scroll?
|
||||
if event.relative {
|
||||
scroll_y += event.value as i32;
|
||||
} else {
|
||||
log::warn!("absolute mouse wheel not supported");
|
||||
|
||||
match UsagePage::from_repr(event.usage_page) {
|
||||
Some(UsagePage::GenericDesktop) => {
|
||||
match GenericDesktopUsage::from_repr(event.usage) {
|
||||
Some(GenericDesktopUsage::X) => {
|
||||
if event.relative {
|
||||
mouse_dx += event.value as i32;
|
||||
} else {
|
||||
mouse_pos.0 = event.value as i32;
|
||||
}
|
||||
}
|
||||
Some(GenericDesktopUsage::Y) => {
|
||||
if event.relative {
|
||||
mouse_dy += event.value as i32;
|
||||
} else {
|
||||
mouse_pos.1 = event.value as i32;
|
||||
}
|
||||
}
|
||||
Some(GenericDesktopUsage::Wheel) => {
|
||||
//TODO: what is X scroll?
|
||||
if event.relative {
|
||||
scroll_y += event.value as i32;
|
||||
} else {
|
||||
log::warn!("absolute mouse wheel not supported");
|
||||
}
|
||||
}
|
||||
unsupported => {
|
||||
log::info!(
|
||||
"unsupported generic desktop usage 0x{:X}:0x{:X} ({:?}) value {}",
|
||||
event.usage_page,
|
||||
event.usage,
|
||||
unsupported,
|
||||
event.value
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(UsagePage::KeyboardOrKeypad) => {
|
||||
let (pressed, shift_opt) = if event.value != 0 {
|
||||
(true, Some(left_shift | right_shift))
|
||||
} else {
|
||||
(false, None)
|
||||
};
|
||||
if event.usage == 0xE1 {
|
||||
left_shift = pressed;
|
||||
} else if event.usage == 0xE5 {
|
||||
right_shift = pressed;
|
||||
}
|
||||
send_key_event(&mut display, event.usage_page, event.usage, pressed);
|
||||
}
|
||||
Some(UsagePage::Button) => {
|
||||
if event.usage > 0 && event.usage as usize <= buttons.len() {
|
||||
buttons[event.usage as usize - 1] = event.value != 0;
|
||||
} else {
|
||||
log::info!(
|
||||
"unsupported buttons usage 0x{:X}:0x{:X} value {}",
|
||||
event.usage_page,
|
||||
event.usage,
|
||||
event.value
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if event.usage_page >= 0xFF00 {
|
||||
// Ignore vendor defined event
|
||||
} else {
|
||||
log::info!(
|
||||
"unsupported usage 0x{:X}:0x{:X} value {}",
|
||||
event.usage_page,
|
||||
event.usage,
|
||||
event.value
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::info!(
|
||||
"unsupported generic desktop usage 0x{:X}:0x{:X} value {}",
|
||||
event.usage_page,
|
||||
event.usage,
|
||||
event.value
|
||||
);
|
||||
}
|
||||
} else if event.usage_page == UsagePage::KeyboardOrKeypad as u16 {
|
||||
let (pressed, shift_opt) = if event.value != 0 {
|
||||
(true, Some(left_shift | right_shift))
|
||||
} else {
|
||||
(false, None)
|
||||
};
|
||||
if event.usage == 0xE1 {
|
||||
left_shift = pressed;
|
||||
} else if event.usage == 0xE5 {
|
||||
right_shift = pressed;
|
||||
}
|
||||
send_key_event(&mut display, event.usage_page, event.usage, pressed);
|
||||
} else if event.usage_page == UsagePage::Button as u16 {
|
||||
if event.usage > 0 && event.usage as usize <= buttons.len() {
|
||||
buttons[event.usage as usize - 1] = event.value != 0;
|
||||
} else {
|
||||
log::info!(
|
||||
"unsupported buttons usage 0x{:X}:0x{:X} value {}",
|
||||
event.usage_page,
|
||||
event.usage,
|
||||
event.value
|
||||
);
|
||||
}
|
||||
} else if event.usage_page >= 0xFF00 {
|
||||
// Ignore vendor defined event
|
||||
} else {
|
||||
log::info!(
|
||||
"unsupported usage 0x{:X}:0x{:X} value {}",
|
||||
event.usage_page,
|
||||
event.usage,
|
||||
event.value
|
||||
);
|
||||
}
|
||||
Err(mpsc::TryRecvError::Empty) => {
|
||||
//TODO: get frequency from device
|
||||
//TODO: use sleeps when accuracy is better: thread::sleep(time::Duration::from_millis(10));
|
||||
let timer = time::Instant::now();
|
||||
while timer.elapsed() < time::Duration::from_millis(1) {
|
||||
thread::yield_now();
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
anyhow::bail!("failed to recv report buffer from thread: {:?}", err);
|
||||
}
|
||||
}
|
||||
|
||||
for (&(usage_page, usage), &(value, last)) in gamepad_state.iter() {
|
||||
let gamepad_axis = |value| {
|
||||
let deadzone = 8096;
|
||||
if value < -deadzone {
|
||||
value + deadzone
|
||||
} else if value > deadzone {
|
||||
value - deadzone
|
||||
} else {
|
||||
0
|
||||
}
|
||||
};
|
||||
let mut gamepad_key = |scancode, pressed| {
|
||||
let key_event = OrbKeyEvent {
|
||||
character: '\0',
|
||||
scancode,
|
||||
pressed,
|
||||
};
|
||||
|
||||
match display.write_event(key_event.to_event()) {
|
||||
Ok(_) => (),
|
||||
Err(err) => {
|
||||
log::warn!("failed to send key event to orbital: {}", err);
|
||||
}
|
||||
}
|
||||
};
|
||||
let mut gamepad_axis_keys = |value, last, k_neg, k_pos| {
|
||||
let axis = gamepad_axis(value);
|
||||
let press_neg = axis < 0;
|
||||
let press_pos = axis > 0;
|
||||
let last_axis = gamepad_axis(last);
|
||||
if press_neg != (last_axis < 0) {
|
||||
gamepad_key(k_neg, press_neg);
|
||||
}
|
||||
if press_pos != (last_axis > 0) {
|
||||
gamepad_key(k_pos, press_pos);
|
||||
}
|
||||
};
|
||||
match UsagePage::from_repr(usage_page) {
|
||||
Some(UsagePage::GenericDesktop) => match GenericDesktopUsage::from_repr(usage) {
|
||||
Some(GenericDesktopUsage::X) => {
|
||||
gamepad_axis_keys(value, last, orbclient::K_A, orbclient::K_D);
|
||||
}
|
||||
Some(GenericDesktopUsage::Y) => {
|
||||
gamepad_axis_keys(value, last, orbclient::K_S, orbclient::K_W);
|
||||
}
|
||||
Some(GenericDesktopUsage::Rx) => {
|
||||
mouse_dx += (gamepad_axis(value) / 4096) as i32;
|
||||
}
|
||||
Some(GenericDesktopUsage::Ry) => {
|
||||
mouse_dy -= (gamepad_axis(value) / 4096) as i32;
|
||||
}
|
||||
Some(GenericDesktopUsage::DpadLeft) => {
|
||||
if value != last {
|
||||
gamepad_key(orbclient::K_A, value != 0);
|
||||
}
|
||||
}
|
||||
Some(GenericDesktopUsage::DpadRight) => {
|
||||
if value != last {
|
||||
gamepad_key(orbclient::K_D, value != 0);
|
||||
}
|
||||
}
|
||||
Some(GenericDesktopUsage::DpadUp) => {
|
||||
if value != last {
|
||||
gamepad_key(orbclient::K_W, value != 0);
|
||||
}
|
||||
}
|
||||
Some(GenericDesktopUsage::DpadDown) => {
|
||||
if value != last {
|
||||
gamepad_key(orbclient::K_S, value != 0);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Some(UsagePage::Button) => match usage {
|
||||
// A
|
||||
1 => {
|
||||
if value != last {
|
||||
gamepad_key(orbclient::K_SEMICOLON, value != 0)
|
||||
}
|
||||
}
|
||||
// B
|
||||
2 => {
|
||||
if value != last {
|
||||
gamepad_key(orbclient::K_L, value != 0)
|
||||
}
|
||||
}
|
||||
// X
|
||||
3 => {
|
||||
if value != last {
|
||||
gamepad_key(orbclient::K_O, value != 0)
|
||||
}
|
||||
}
|
||||
// Y
|
||||
4 => {
|
||||
if value != last {
|
||||
gamepad_key(orbclient::K_K, value != 0)
|
||||
}
|
||||
}
|
||||
// LB
|
||||
5 => {
|
||||
if value != last {
|
||||
gamepad_key(orbclient::K_I, value != 0)
|
||||
}
|
||||
}
|
||||
// RB
|
||||
6 => {
|
||||
if value != last {
|
||||
gamepad_key(orbclient::K_P, value != 0)
|
||||
}
|
||||
}
|
||||
// Select
|
||||
7 => {
|
||||
if value != last {
|
||||
gamepad_key(orbclient::K_SPACE, value != 0)
|
||||
}
|
||||
}
|
||||
// Start
|
||||
8 => {
|
||||
if value != last {
|
||||
gamepad_key(orbclient::K_ENTER, value != 0)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
//log::warn!("unknown gamepad button {}", usage)
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
for (_, (value, last)) in gamepad_state.iter_mut() {
|
||||
*last = *value;
|
||||
}
|
||||
|
||||
if mouse_pos != last_mouse_pos {
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
#[rustfmt::skip]
|
||||
pub const OVERRIDES: &'static [(&'static str, &'static [u8])] = &[
|
||||
// Adapted from https://marc.info/?l=openbsd-tech&m=138227720105805&w=2
|
||||
(
|
||||
"xbox-360",
|
||||
&[
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x05, // Usage (Game Pad)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x3B, // Usage (Byte Count)
|
||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0xA1, 0x00, // Collection (Physical)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x35, 0x00, // Physical Minimum (0)
|
||||
0x45, 0x01, // Physical Maximum (1)
|
||||
0x95, 0x04, // Report Count (4)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x90, // Usage (D-pad Up)
|
||||
0x09, 0x91, // Usage (D-pad Down)
|
||||
0x09, 0x93, // Usage (D-pad Left)
|
||||
0x09, 0x92, // Usage (D-pad Right)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x35, 0x00, // Physical Minimum (0)
|
||||
0x45, 0x01, // Physical Maximum (1)
|
||||
0x95, 0x07, // Report Count (7)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x09, 0x08, // Usage (0x08)
|
||||
0x09, 0x07, // Usage (0x07)
|
||||
0x09, 0x09, // Usage (0x09)
|
||||
0x09, 0x0A, // Usage (0x0A)
|
||||
0x09, 0x05, // Usage (0x05)
|
||||
0x09, 0x06, // Usage (0x06)
|
||||
0x09, 0x0B, // Usage (0x0B)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x35, 0x00, // Physical Minimum (0)
|
||||
0x45, 0x01, // Physical Maximum (1)
|
||||
0x95, 0x04, // Report Count (4)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (0x01)
|
||||
0x29, 0x04, // Usage Maximum (0x04)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x35, 0x00, // Physical Minimum (0)
|
||||
0x46, 0xFF, 0x00, // Physical Maximum (255)
|
||||
0x95, 0x02, // Report Count (2)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x32, // Usage (Z)
|
||||
0x09, 0x35, // Usage (Rz)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x75, 0x10, // Report Size (16)
|
||||
0x16, 0x00, 0x80, // Logical Minimum (-32768)
|
||||
0x26, 0xFF, 0x7F, // Logical Maximum (32767)
|
||||
0x36, 0x00, 0x80, // Physical Minimum (-32768)
|
||||
0x46, 0xFF, 0x7F, // Physical Maximum (32767)
|
||||
0x95, 0x04, // Report Count (4)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x30, // Usage (X)
|
||||
0x09, 0x31, // Usage (Y)
|
||||
0x09, 0x33, // Usage (Rx)
|
||||
0x09, 0x34, // Usage (Ry)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x75, 0x30, // Report Size (48)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
],
|
||||
),
|
||||
];
|
||||
@@ -16,3 +16,13 @@ name = "USB HID"
|
||||
class = 3 # HID class
|
||||
subclass = -1
|
||||
command = ["usbhidd", "$SCHEME", "$PORT", "$IF_NUM"]
|
||||
|
||||
[[drivers]]
|
||||
name = "USB HID Xbox 360"
|
||||
class = 255 # Vendor specific class
|
||||
subclass = 93
|
||||
protocol = 1
|
||||
ids = { 0x045e = [
|
||||
0x028e # Xbox 360 controller
|
||||
] }
|
||||
command = ["usbhidd", "$SCHEME", "$PORT", "$IF_NUM", "xbox-360"]
|
||||
|
||||
@@ -1251,19 +1251,14 @@ impl<const N: usize> Xhci<N> {
|
||||
trace!("Spawning driver on port: {}", port);
|
||||
|
||||
//TODO: support choosing config?
|
||||
let config_desc = &ps
|
||||
.dev_desc
|
||||
.as_ref()
|
||||
.ok_or_else(|| {
|
||||
log::warn!("Missing device descriptor");
|
||||
Error::new(EBADF)
|
||||
})?
|
||||
.config_descs
|
||||
.first()
|
||||
.ok_or_else(|| {
|
||||
log::warn!("Missing config descriptor");
|
||||
Error::new(EBADF)
|
||||
})?;
|
||||
let dev_desc = ps.dev_desc.as_ref().ok_or_else(|| {
|
||||
log::warn!("Missing device descriptor");
|
||||
Error::new(EBADF)
|
||||
})?;
|
||||
let config_desc = dev_desc.config_descs.first().ok_or_else(|| {
|
||||
log::warn!("Missing config descriptor");
|
||||
Error::new(EBADF)
|
||||
})?;
|
||||
|
||||
trace!("Got config and device descriptors on port {}", port);
|
||||
|
||||
@@ -1287,11 +1282,36 @@ impl<const N: usize> Xhci<N> {
|
||||
}
|
||||
|
||||
if let Some(driver) = self.drivers_config.drivers.iter().find(|driver| {
|
||||
if let Some(ref ids) = driver.ids {
|
||||
let mut device_found = false;
|
||||
for (vendor, devices) in ids {
|
||||
let vendor_without_prefix = vendor.trim_start_matches("0x");
|
||||
let vendor = i64::from_str_radix(vendor_without_prefix, 16).unwrap() as u16;
|
||||
|
||||
if vendor != dev_desc.vendor {
|
||||
continue;
|
||||
}
|
||||
|
||||
for device in devices {
|
||||
if *device == dev_desc.product {
|
||||
device_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !device_found {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
driver.class == ifdesc.class
|
||||
&& driver
|
||||
.subclass()
|
||||
.map(|subclass| subclass == ifdesc.sub_class)
|
||||
.unwrap_or(true)
|
||||
&& driver
|
||||
.protocol
|
||||
.map(|protocol| protocol == ifdesc.protocol)
|
||||
.unwrap_or(true)
|
||||
}) {
|
||||
info!(
|
||||
"Loading subdriver \"{}\" for port {} iface {} alternate {} class {}.{} proto {}",
|
||||
@@ -1337,7 +1357,7 @@ impl<const N: usize> Xhci<N> {
|
||||
ifdesc.alternate_setting,
|
||||
ifdesc.class,
|
||||
ifdesc.sub_class,
|
||||
ifdesc.protocol
|
||||
ifdesc.protocol,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1485,6 +1505,8 @@ struct DriverConfig {
|
||||
name: String,
|
||||
class: u8,
|
||||
subclass: i16, // The subclass may be meaningless for some drivers, hence negative values (and values above 255) mean "undefined".
|
||||
protocol: Option<u8>,
|
||||
ids: Option<BTreeMap<String, Vec<u16>>>,
|
||||
command: Vec<String>,
|
||||
}
|
||||
impl DriverConfig {
|
||||
|
||||
Reference in New Issue
Block a user