Skip to content

Commit 6c57b1e

Browse files
committed
touchscreen: Dump firmware version and protocol
``` > framework_tool --versions [...] Touchscreen Firmware Version: v7.0.0.4.0.0.0.0 USI Protocol: false MPP Protocol: true ``` Signed-off-by: Daniel Schaefer <[email protected]>
1 parent 84a79f9 commit 6c57b1e

File tree

6 files changed

+265
-2
lines changed

6 files changed

+265
-2
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ see the [Support Matrices](support-matrices.md).
4343
- [x] PD
4444
- [x] ME (Only on Linux)
4545
- [x] Retimer
46-
- [x] Touchpad (Linux and Windows)
46+
- [x] Touchpad (Linux, Windows, FreeBSD, not UEFI)
47+
- [x] Touchscreen (Linux, Windows, FreeBSD, not UEFI)
4748
- [x] Get Expansion Card Firmware (Not on UEFI so far)
4849
- [x] HDMI Expansion Card (`--dp-hdmi-info`)
4950
- [x] DisplayPort Expansion Card (`--dp-hdmi-info`)
@@ -69,6 +70,7 @@ All of these need EC communication support in order to work.
6970
- [x] Get and set keyboard brightness (`--kblight`)
7071
- [x] Get and set battery charge limit (`--charge-limit`)
7172
- [x] Get and set fingerprint LED brightness (`--fp-brightness`)
73+
- [x] Disable/Enable touchscreen (`--touchscreen-enable`)
7274

7375
###### Communication with Embedded Controller
7476

@@ -177,6 +179,8 @@ Options:
177179
--intrusion Show status of intrusion switch
178180
--inputmodules Show status of the input modules (Framework 16 only)
179181
--kblight [<KBLIGHT>] Set keyboard backlight percentage or get, if no value provided
182+
--touchscreen-enable <TOUCHSCREEN_ENABLE>
183+
Enable/disable touchscreen [possible values: true, false]
180184
--console <CONSOLE> Get EC console, choose whether recent or to follow the output [possible values: recent, follow]
181185
--driver <DRIVER> Select which driver is used. By default portio is used [possible values: portio, cros-ec, windows]
182186
-t, --test Run self-test to check if interaction with EC is possible

framework_lib/Cargo.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ uefi = { version = "0.20", features = ["alloc"], optional = true }
6363
uefi-services = { version = "0.17", optional = true }
6464
plain = { version = "0.2.3", optional = true }
6565
spin = { version = "0.9.8", optional = false }
66-
hidapi = { version = "2.6.3", optional = true }
66+
hidapi = { version = "2.6.3", optional = true, features = [ "windows-native" ] }
6767
rusb = { version = "0.9.4", optional = true }
6868
no-std-compat = { version = "0.4.1", features = [ "alloc" ] }
6969
guid_macros = { path = "../guid_macros" }
@@ -89,5 +89,12 @@ features = [
8989
"Win32_System_IO",
9090
"Win32_System_Ioctl",
9191
"Win32_System_SystemServices",
92+
# For HID devices
93+
"Win32_Devices_DeviceAndDriverInstallation",
94+
"Win32_Devices_HumanInterfaceDevice",
95+
"Win32_Devices_Properties",
96+
"Win32_Storage_EnhancedStorage",
97+
"Win32_System_Threading",
98+
"Win32_UI_Shell_PropertiesSystem"
9299
]
93100

framework_lib/src/commandline/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ use crate::smbios::ConfigDigit0;
5252
use crate::smbios::{dmidecode_string_val, get_smbios, is_framework};
5353
#[cfg(feature = "hidapi")]
5454
use crate::touchpad::print_touchpad_fw_ver;
55+
#[cfg(feature = "hidapi")]
56+
use crate::touchscreen::print_touchscreen_fw_ver;
5557
#[cfg(feature = "uefi")]
5658
use crate::uefi::enable_page_break;
5759
use crate::util;
@@ -479,6 +481,9 @@ fn print_versions(ec: &CrosEc) {
479481

480482
#[cfg(feature = "hidapi")]
481483
let _ignore_err = print_touchpad_fw_ver();
484+
485+
#[cfg(feature = "hidapi")]
486+
let _ignore_err = print_touchscreen_fw_ver();
482487
}
483488

484489
fn print_esrt() {

framework_lib/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ pub mod audio_card;
1818
pub mod camera;
1919
#[cfg(feature = "hidapi")]
2020
pub mod touchpad;
21+
#[cfg(any(feature = "hidapi", feature = "windows"))]
22+
pub mod touchscreen;
23+
#[cfg(feature = "windows")]
24+
pub mod touchscreen_win;
2125

2226
#[cfg(feature = "uefi")]
2327
#[macro_use]

framework_lib/src/touchscreen.rs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
use hidapi::{HidApi, HidDevice};
2+
3+
#[cfg(target_os = "windows")]
4+
use crate::touchscreen_win;
5+
6+
pub const ILI_VID: u16 = 0x222A;
7+
pub const ILI_PID: u16 = 0x5539;
8+
pub const USI_BITMAP: u8 = 1 << 1;
9+
pub const MPP_BITMAP: u8 = 1 << 2;
10+
11+
struct HidapiTouchScreen {
12+
device: HidDevice,
13+
}
14+
15+
impl TouchScreen for HidapiTouchScreen {
16+
fn open_device() -> Option<HidapiTouchScreen> {
17+
debug!("Looking for touchscreen HID device");
18+
match HidApi::new() {
19+
Ok(api) => {
20+
for dev_info in api.device_list() {
21+
let vid = dev_info.vendor_id();
22+
let pid = dev_info.product_id();
23+
let usage_page = dev_info.usage_page();
24+
if vid != ILI_VID {
25+
trace!(" Skipping VID:PID. Expected {:04X}:*", ILI_VID);
26+
continue;
27+
}
28+
debug!(
29+
" Found {:04X}:{:04X} (Usage Page {:04X})",
30+
vid, pid, usage_page
31+
);
32+
if usage_page != 0xFF00 {
33+
debug!(" Skipping usage page. Expected {:04X}", 0xFF00);
34+
continue;
35+
}
36+
if pid != ILI_PID {
37+
debug!(" Warning: PID is {:04X}, expected {:04X}", pid, ILI_PID);
38+
}
39+
40+
debug!(" Found matching touchscreen HID device");
41+
debug!(" Path: {:?}", dev_info.path());
42+
debug!(" IC Type: {:04X}", pid);
43+
44+
// Unwrapping because if we can enumerate it, we should be able to open it
45+
let device = dev_info.open_device(&api).unwrap();
46+
debug!(" Opened device.");
47+
48+
return Some(HidapiTouchScreen { device });
49+
}
50+
}
51+
Err(e) => {
52+
error!("Failed to open hidapi. Error: {e}");
53+
}
54+
};
55+
56+
None
57+
}
58+
59+
fn send_message(&self, message_id: u8, read_len: usize, data: Vec<u8>) -> Option<Vec<u8>> {
60+
let report_id = 0x03;
61+
let data_len = data.len();
62+
let mut msg = [0u8; 0x40];
63+
msg[0] = report_id;
64+
msg[1] = 0xA3;
65+
msg[2] = data_len as u8;
66+
msg[3] = read_len as u8;
67+
msg[4] = message_id;
68+
for (i, b) in data.into_iter().enumerate() {
69+
msg[5 + i] = b;
70+
}
71+
72+
// Not sure why, but on Windows we just have to write an output report
73+
// HidApiError { message: "HidD_SetFeature: (0x00000057) The parameter is incorrect." }
74+
// Still doesn't work on Windows. Need to write a byte more than the buffer is long
75+
#[cfg(target_os = "windows")]
76+
let send_feature_report = false;
77+
#[cfg(not(target_os = "windows"))]
78+
let send_feature_report = true;
79+
80+
if send_feature_report {
81+
debug!(" send_feature_report {:X?}", msg);
82+
self.device.send_feature_report(&msg).ok()?;
83+
} else {
84+
debug!(" Writing {:X?}", msg);
85+
self.device.write(&msg).ok()?;
86+
};
87+
88+
if read_len == 0 {
89+
return Some(vec![]);
90+
}
91+
92+
let msg_len = 3 + data_len;
93+
let mut buf: [u8; 0x40] = [0; 0x40];
94+
debug!(" Reading");
95+
let res = self.device.read(&mut buf);
96+
debug!(" res: {:?}", res);
97+
debug!(" Read buf: {:X?}", buf);
98+
Some(buf[msg_len..msg_len + read_len].to_vec())
99+
}
100+
}
101+
102+
pub trait TouchScreen {
103+
fn open_device() -> Option<Self>
104+
where
105+
Self: std::marker::Sized;
106+
fn send_message(&self, message_id: u8, read_len: usize, data: Vec<u8>) -> Option<Vec<u8>>;
107+
108+
fn check_fw_version(&self) -> Option<()> {
109+
println!("Touchscreen");
110+
let res = self.send_message(0x42, 3, vec![0])?;
111+
let ver = res
112+
.iter()
113+
.skip(1)
114+
.fold(format!("{:02X}", res[0]), |acc, &x| {
115+
acc + "." + &format!("{:02X}", x)
116+
});
117+
// Expecting 06.00.0A
118+
debug!(" Protocol Version: v{}", ver);
119+
120+
let res = self.send_message(0x40, 8, vec![0])?;
121+
let ver = res
122+
.iter()
123+
.skip(1)
124+
.fold(res[0].to_string(), |acc, &x| acc + "." + &x.to_string());
125+
println!(" Firmware Version: v{}", ver);
126+
127+
let res = self.send_message(0x20, 16, vec![0])?;
128+
println!(" USI Protocol: {:?}", (res[15] & USI_BITMAP) > 0);
129+
println!(" MPP Protocol: {:?}", (res[15] & MPP_BITMAP) > 0);
130+
131+
Some(())
132+
}
133+
}
134+
135+
pub fn print_touchscreen_fw_ver() -> Option<()> {
136+
#[cfg(target_os = "windows")]
137+
let device = touchscreen_win::NativeWinTouchScreen::open_device()?;
138+
#[cfg(not(target_os = "windows"))]
139+
let device = HidapiTouchScreen::open_device()?;
140+
141+
device.check_fw_version()
142+
}

framework_lib/src/touchscreen_win.rs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
use crate::touchscreen::TouchScreen;
2+
#[allow(unused_imports)]
3+
use windows::{
4+
core::*,
5+
Win32::{
6+
Devices::HumanInterfaceDevice::*,
7+
Devices::Properties::*,
8+
Foundation::*,
9+
Storage::FileSystem::*,
10+
System::Threading::ResetEvent,
11+
System::IO::{CancelIoEx, DeviceIoControl},
12+
System::{Ioctl::*, IO::*},
13+
},
14+
};
15+
16+
pub struct NativeWinTouchScreen {
17+
handle: HANDLE,
18+
}
19+
20+
impl TouchScreen for NativeWinTouchScreen {
21+
fn open_device() -> Option<Self> {
22+
// TODO: I don't know if this might be different on other systems
23+
// Should enumerate and find the right one
24+
// See: https://learn.microsoft.com/en-us/windows-hardware/drivers/hid/finding-and-opening-a-hid-collection
25+
let path =
26+
w!(r"\\?\HID#ILIT2901&Col03#5&357cbf85&0&0002#{4d1e55b2-f16f-11cf-88cb-001111000030}");
27+
28+
let res = unsafe {
29+
CreateFileW(
30+
path,
31+
FILE_GENERIC_WRITE.0 | FILE_GENERIC_READ.0,
32+
FILE_SHARE_READ | FILE_SHARE_WRITE,
33+
None,
34+
OPEN_EXISTING,
35+
// hidapi-rs is using FILE_FLAG_OVERLAPPED but it doesn't look like we need that
36+
FILE_FLAGS_AND_ATTRIBUTES(0),
37+
None,
38+
)
39+
};
40+
let handle = match res {
41+
Ok(h) => h,
42+
Err(err) => {
43+
error!("Failed to open device {:?}", err);
44+
return None;
45+
}
46+
};
47+
48+
debug!("Opened {:?}", path);
49+
50+
Some(NativeWinTouchScreen { handle })
51+
}
52+
53+
fn send_message(&self, message_id: u8, read_len: usize, data: Vec<u8>) -> Option<Vec<u8>> {
54+
let report_id = 0x03;
55+
let data_len = data.len();
56+
let mut msg = [0u8; 0x40];
57+
let msg_len = 3 + data_len;
58+
msg[0] = report_id;
59+
msg[1] = 0xA3;
60+
msg[2] = data_len as u8;
61+
msg[3] = read_len as u8;
62+
msg[4] = message_id;
63+
for (i, b) in data.into_iter().enumerate() {
64+
msg[5 + i] = b;
65+
}
66+
67+
let mut buf = [0u8; 0x40];
68+
buf[0] = report_id;
69+
70+
unsafe {
71+
debug!(" HidD_SetOutputReport {:X?}", msg);
72+
let success = HidD_SetOutputReport(
73+
self.handle,
74+
// Microsoft docs says that the first byte of the message has to be the report ID.
75+
// This is normal with HID implementations.
76+
// But it seems on Windows (at least for this device's firmware) we have to set the
77+
// length as one more than the buffer is long.
78+
// Otherwise no data is returned in the read call later.
79+
msg.as_mut_ptr() as _,
80+
msg.len() as u32 + 1,
81+
);
82+
debug!(" Success: {}", success);
83+
84+
if read_len == 0 {
85+
return Some(vec![]);
86+
}
87+
88+
let mut bytes_read = 0;
89+
debug!(" ReadFile");
90+
// HidD_GetFeature doesn't work, have to use ReadFile
91+
// Microsoft does recommend that
92+
// https://learn.microsoft.com/en-us/windows-hardware/drivers/hid/obtaining-hid-reports
93+
let res = ReadFile(self.handle, Some(&mut buf), Some(&mut bytes_read), None);
94+
debug!(" Success: {:?}, Bytes: {}", res, bytes_read);
95+
debug!(" Read buf: {:X?}", buf);
96+
debug!(" Read msg: {:X?}", msg);
97+
}
98+
99+
Some(buf[msg_len..msg_len + read_len].to_vec())
100+
}
101+
}

0 commit comments

Comments
 (0)