Skip to content

touchscreen: Dump firmware version and protocol #90

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ see the [Support Matrices](support-matrices.md).
- [x] Get firmware version from system (`--versions`)
- [x] BIOS
- [x] EC
- [x] PD
- [x] PD Controller
- [x] ME (Only on Linux)
- [x] Retimer
- [x] Touchpad (Linux and Windows)
- [x] Touchpad (Linux, Windows, FreeBSD, not UEFI)
- [x] Touchscreen (Linux, Windows, FreeBSD, not UEFI)
- [x] Get Expansion Card Firmware (Not on UEFI so far)
- [x] HDMI Expansion Card (`--dp-hdmi-info`)
- [x] DisplayPort Expansion Card (`--dp-hdmi-info`)
Expand All @@ -68,7 +69,9 @@ All of these need EC communication support in order to work.

- [x] Get and set keyboard brightness (`--kblight`)
- [x] Get and set battery charge limit (`--charge-limit`)
- [x] Get and set fingerprint LED brightness (`--fp-brightness`)
- [x] Get and set fingerprint LED brightness (`--fp-brightness`, `--fp-led-level`)
- [x] Override tablet mode, instead of follow G-Sensor and hall sensor (`--tablet-mode`)
- [x] Disable/Enable touchscreen (`--touchscreen-enable`)

###### Communication with Embedded Controller

Expand Down Expand Up @@ -176,9 +179,30 @@ Options:
--h2o-capsule <H2O_CAPSULE> Parse UEFI Capsule information from binary file
--intrusion Show status of intrusion switch
--inputmodules Show status of the input modules (Framework 16 only)
--input-deck-mode <INPUT_DECK_MODE>
Set input deck power mode [possible values: auto, off, on] (Framework 16 only) [possible values: auto, off, on]
--charge-limit [<CHARGE_LIMIT>]
Get or set max charge limit
--get-gpio <GET_GPIO>
Get GPIO value by name
--fp-led-level [<FP_LED_LEVEL>]
Get or set fingerprint LED brightness level [possible values: high, medium, low, ultra-low, auto]
--fp-brightness [<FP_BRIGHTNESS>]
Get or set fingerprint LED brightness percentage
--kblight [<KBLIGHT>] Set keyboard backlight percentage or get, if no value provided
--tablet-mode <TABLET_MODE> Set tablet mode override [possible values: auto, tablet, laptop]
--touchscreen-enable <TOUCHSCREEN_ENABLE>
Enable/disable touchscreen [possible values: true, false]
--console <CONSOLE> Get EC console, choose whether recent or to follow the output [possible values: recent, follow]
--reboot-ec <REBOOT_EC> Control EC RO/RW jump [possible values: reboot, jump-ro, jump-rw, cancel-jump, disable-jump]
--hash <HASH> Hash a file of arbitrary data
--driver <DRIVER> Select which driver is used. By default portio is used [possible values: portio, cros-ec, windows]
--pd-addrs <PD_ADDRS> <PD_ADDRS>
Specify I2C addresses of the PD chips (Advanced)
--pd-ports <PD_PORTS> <PD_PORTS>
Specify I2C ports of the PD chips (Advanced)
--has-mec <HAS_MEC>
Specify the type of EC chip (MEC/MCHP or other) [possible values: true, false]
-t, --test Run self-test to check if interaction with EC is possible
-h, --help Print help information
```
Expand Down
9 changes: 8 additions & 1 deletion framework_lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ uefi = { version = "0.20", features = ["alloc"], optional = true }
uefi-services = { version = "0.17", optional = true }
plain = { version = "0.2.3", optional = true }
spin = { version = "0.9.8", optional = false }
hidapi = { version = "2.6.3", optional = true }
hidapi = { version = "2.6.3", optional = true, features = [ "windows-native" ] }
rusb = { version = "0.9.4", optional = true }
no-std-compat = { version = "0.4.1", features = [ "alloc" ] }
guid_macros = { path = "../guid_macros" }
Expand All @@ -89,5 +89,12 @@ features = [
"Win32_System_IO",
"Win32_System_Ioctl",
"Win32_System_SystemServices",
# For HID devices
"Win32_Devices_DeviceAndDriverInstallation",
"Win32_Devices_HumanInterfaceDevice",
"Win32_Devices_Properties",
"Win32_Storage_EnhancedStorage",
"Win32_System_Threading",
"Win32_UI_Shell_PropertiesSystem"
]

6 changes: 6 additions & 0 deletions framework_lib/src/commandline/clap_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@ struct ClapCli {
#[arg(long)]
tablet_mode: Option<TabletModeArg>,

/// Enable/disable touchscreen
#[clap(value_enum)]
#[arg(long)]
touchscreen_enable: Option<bool>,

/// Get EC console, choose whether recent or to follow the output
#[clap(value_enum)]
#[arg(long)]
Expand Down Expand Up @@ -284,6 +289,7 @@ pub fn parse(args: &[String]) -> Cli {
kblight: args.kblight,
rgbkbd: args.rgbkbd,
tablet_mode: args.tablet_mode,
touchscreen_enable: args.touchscreen_enable,
console: args.console,
reboot_ec: args.reboot_ec,
hash: args.hash.map(|x| x.into_os_string().into_string().unwrap()),
Expand Down
11 changes: 11 additions & 0 deletions framework_lib/src/commandline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ use crate::smbios::ConfigDigit0;
use crate::smbios::{dmidecode_string_val, get_smbios, is_framework};
#[cfg(feature = "hidapi")]
use crate::touchpad::print_touchpad_fw_ver;
#[cfg(any(feature = "hidapi", feature = "windows"))]
use crate::touchscreen;
#[cfg(feature = "uefi")]
use crate::uefi::enable_page_break;
use crate::util;
Expand Down Expand Up @@ -173,6 +175,7 @@ pub struct Cli {
pub kblight: Option<Option<u8>>,
pub rgbkbd: Vec<u64>,
pub tablet_mode: Option<TabletModeArg>,
pub touchscreen_enable: Option<bool>,
pub console: Option<ConsoleArg>,
pub reboot_ec: Option<RebootEcArg>,
pub hash: Option<String>,
Expand Down Expand Up @@ -479,6 +482,9 @@ fn print_versions(ec: &CrosEc) {

#[cfg(feature = "hidapi")]
let _ignore_err = print_touchpad_fw_ver();

#[cfg(feature = "hidapi")]
let _ignore_err = touchscreen::print_fw_ver();
}

fn print_esrt() {
Expand Down Expand Up @@ -793,6 +799,11 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 {
TabletModeArg::Laptop => TabletModeOverride::ForceClamshell,
};
ec.set_tablet_mode(mode);
} else if let Some(_enable) = &args.touchscreen_enable {
#[cfg(any(feature = "hidapi", feature = "windows"))]
if touchscreen::enable_touch(*_enable).is_none() {
error!("Failed to enable/disable touch");
}
} else if let Some(console_arg) = &args.console {
match console_arg {
ConsoleArg::Follow => {
Expand Down
1 change: 1 addition & 0 deletions framework_lib/src/commandline/uefi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ pub fn parse(args: &[String]) -> Cli {
kblight: None,
rgbkbd: vec![],
tablet_mode: None,
touchscreen_enable: None,
console: None,
reboot_ec: None,
hash: None,
Expand Down
4 changes: 4 additions & 0 deletions framework_lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ pub mod audio_card;
pub mod camera;
#[cfg(feature = "hidapi")]
pub mod touchpad;
#[cfg(any(feature = "hidapi", feature = "windows"))]
pub mod touchscreen;
#[cfg(feature = "windows")]
pub mod touchscreen_win;

#[cfg(feature = "uefi")]
#[macro_use]
Expand Down
156 changes: 156 additions & 0 deletions framework_lib/src/touchscreen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
use hidapi::{HidApi, HidDevice};

#[cfg(target_os = "windows")]
use crate::touchscreen_win;

pub const ILI_VID: u16 = 0x222A;
pub const ILI_PID: u16 = 0x5539;
pub const USI_BITMAP: u8 = 1 << 1;
pub const MPP_BITMAP: u8 = 1 << 2;

struct HidapiTouchScreen {
device: HidDevice,
}

impl TouchScreen for HidapiTouchScreen {
fn open_device() -> Option<HidapiTouchScreen> {
debug!("Looking for touchscreen HID device");
match HidApi::new() {
Ok(api) => {
for dev_info in api.device_list() {
let vid = dev_info.vendor_id();
let pid = dev_info.product_id();
let usage_page = dev_info.usage_page();
if vid != ILI_VID {
trace!(" Skipping VID:PID. Expected {:04X}:*", ILI_VID);
continue;
}
debug!(
" Found {:04X}:{:04X} (Usage Page {:04X})",
vid, pid, usage_page
);
if usage_page != 0xFF00 {
debug!(" Skipping usage page. Expected {:04X}", 0xFF00);
continue;
}
if pid != ILI_PID {
debug!(" Warning: PID is {:04X}, expected {:04X}", pid, ILI_PID);
}

debug!(" Found matching touchscreen HID device");
debug!(" Path: {:?}", dev_info.path());
debug!(" IC Type: {:04X}", pid);

// Unwrapping because if we can enumerate it, we should be able to open it
let device = dev_info.open_device(&api).unwrap();
debug!(" Opened device.");

return Some(HidapiTouchScreen { device });
}
}
Err(e) => {
error!("Failed to open hidapi. Error: {e}");
}
};

None
}

fn send_message(&self, message_id: u8, read_len: usize, data: Vec<u8>) -> Option<Vec<u8>> {
let report_id = 0x03;
let data_len = data.len();
let mut msg = [0u8; 0x40];
msg[0] = report_id;
msg[1] = 0xA3;
msg[2] = data_len as u8;
msg[3] = read_len as u8;
msg[4] = message_id;
for (i, b) in data.into_iter().enumerate() {
msg[5 + i] = b;
}

// Not sure why, but on Windows we just have to write an output report
// HidApiError { message: "HidD_SetFeature: (0x00000057) The parameter is incorrect." }
// Still doesn't work on Windows. Need to write a byte more than the buffer is long
#[cfg(target_os = "windows")]
let send_feature_report = false;
#[cfg(not(target_os = "windows"))]
let send_feature_report = true;

if send_feature_report {
debug!(" send_feature_report {:X?}", msg);
self.device.send_feature_report(&msg).ok()?;
} else {
debug!(" Writing {:X?}", msg);
self.device.write(&msg).ok()?;
};

if read_len == 0 {
return Some(vec![]);
}

let msg_len = 3 + data_len;
let mut buf: [u8; 0x40] = [0; 0x40];
debug!(" Reading");
let res = self.device.read(&mut buf);
debug!(" res: {:?}", res);
debug!(" Read buf: {:X?}", buf);
Some(buf[msg_len..msg_len + read_len].to_vec())
}
}

pub trait TouchScreen {
fn open_device() -> Option<Self>
where
Self: std::marker::Sized;
fn send_message(&self, message_id: u8, read_len: usize, data: Vec<u8>) -> Option<Vec<u8>>;

fn check_fw_version(&self) -> Option<()> {
println!("Touchscreen");
let res = self.send_message(0x42, 3, vec![0])?;
let ver = res
.iter()
.skip(1)
.fold(format!("{:02X}", res[0]), |acc, &x| {
acc + "." + &format!("{:02X}", x)
});
// Expecting 06.00.0A
debug!(" Protocol Version: v{}", ver);

let res = self.send_message(0x40, 8, vec![0])?;
let ver = res
.iter()
.skip(1)
.fold(res[0].to_string(), |acc, &x| acc + "." + &x.to_string());
println!(" Firmware Version: v{}", ver);

let res = self.send_message(0x20, 16, vec![0])?;
println!(" USI Protocol: {:?}", (res[15] & USI_BITMAP) > 0);
println!(" MPP Protocol: {:?}", (res[15] & MPP_BITMAP) > 0);

Some(())
}

fn enable_touch(&self, enable: bool) -> Option<()> {
self.send_message(0x38, 0, vec![!enable as u8, 0x00])?;
Some(())
}
}

pub fn print_fw_ver() -> Option<()> {
#[cfg(target_os = "windows")]
let device = touchscreen_win::NativeWinTouchScreen::open_device()?;
#[cfg(not(target_os = "windows"))]
let device = HidapiTouchScreen::open_device()?;

device.check_fw_version()
}

pub fn enable_touch(enable: bool) -> Option<()> {
#[cfg(target_os = "windows")]
let device = touchscreen_win::NativeWinTouchScreen::open_device()?;
#[cfg(not(target_os = "windows"))]
let device = HidapiTouchScreen::open_device()?;

device.enable_touch(enable)
}
Loading
Loading