Skip to content

Commit c86b603

Browse files
committed
--versions: Get stylus information through USI
Only works on our touchscreen because report IDs are hardcoded instead of derived from report descriptor. ``` > sudo framework_tool --versions [...] Stylus Serial Number: 28C1A00-12E71DAE Vendor ID: 32AC (Framework Computer) Product ID: 002B (Framework Stylus) Firmware Version: FF.FF [...] ``` Signed-off-by: Daniel Schaefer <[email protected]>
1 parent 60ff99f commit c86b603

File tree

3 files changed

+118
-11
lines changed

3 files changed

+118
-11
lines changed

EXAMPLES.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,19 @@ Touchscreen
3333
MPP Protocol: true
3434
```
3535

36+
### Stylus (Framework 12)
37+
38+
```
39+
> sudo framework_tool --versions
40+
[...]
41+
Stylus
42+
Serial Number: 28C1A00-12E71DAE
43+
Vendor ID: 32AC (Framework Computer)
44+
Product ID: 002B (Framework Stylus)
45+
Firmware Version: FF.FF
46+
[...]
47+
```
48+
3649
### Touchpad (Framework 12, Framework 13, Framework 16)
3750

3851
```

framework_lib/src/touchscreen.rs

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,24 @@ use crate::touchscreen_win;
55

66
pub const ILI_VID: u16 = 0x222A;
77
pub const ILI_PID: u16 = 0x5539;
8+
const VENDOR_USAGE_PAGE: u16 = 0xFF00;
89
pub const USI_BITMAP: u8 = 1 << 1;
910
pub const MPP_BITMAP: u8 = 1 << 2;
1011

12+
const REPORT_ID_FIRMWARE: u8 = 0x27;
13+
const REPORT_ID_USI_VER: u8 = 0x28;
14+
1115
struct HidapiTouchScreen {
1216
device: HidDevice,
1317
}
1418

1519
impl TouchScreen for HidapiTouchScreen {
16-
fn open_device() -> Option<HidapiTouchScreen> {
17-
debug!("Looking for touchscreen HID device");
20+
fn open_device(target_up: u16, skip: u8) -> Option<HidapiTouchScreen> {
21+
debug!(
22+
"Looking for touchscreen HID device {:X} {}",
23+
target_up, skip
24+
);
25+
let mut skip = skip;
1826
match HidApi::new() {
1927
Ok(api) => {
2028
for dev_info in api.device_list() {
@@ -29,7 +37,7 @@ impl TouchScreen for HidapiTouchScreen {
2937
" Found {:04X}:{:04X} (Usage Page {:04X})",
3038
vid, pid, usage_page
3139
);
32-
if usage_page != 0xFF00 {
40+
if usage_page != target_up {
3341
debug!(" Skipping usage page. Expected {:04X}", 0xFF00);
3442
continue;
3543
}
@@ -40,6 +48,10 @@ impl TouchScreen for HidapiTouchScreen {
4048
debug!(" Found matching touchscreen HID device");
4149
debug!(" Path: {:?}", dev_info.path());
4250
debug!(" IC Type: {:04X}", pid);
51+
if skip > 0 {
52+
skip -= 1;
53+
continue;
54+
}
4355

4456
// Unwrapping because if we can enumerate it, we should be able to open it
4557
let device = dev_info.open_device(&api).unwrap();
@@ -97,16 +109,57 @@ impl TouchScreen for HidapiTouchScreen {
97109
debug!(" Read buf: {:X?}", buf);
98110
Some(buf[msg_len..msg_len + read_len].to_vec())
99111
}
112+
113+
fn get_stylus_fw(&self) -> Option<()> {
114+
let mut msg = [0u8; 0x40];
115+
msg[0] = REPORT_ID_USI_VER;
116+
self.device.get_feature_report(&mut msg).ok()?;
117+
let usi_major = msg[2];
118+
let usi_minor = msg[3];
119+
debug!("USI version (Major.Minor): {}.{}", usi_major, usi_minor);
120+
121+
if usi_major != 2 || usi_minor != 0 {
122+
// Probably not USI mode
123+
return None;
124+
}
125+
126+
let mut msg = [0u8; 0x40];
127+
msg[0] = REPORT_ID_FIRMWARE;
128+
self.device.get_feature_report(&mut msg).ok()?;
129+
let sn_low = u32::from_le_bytes([msg[2], msg[3], msg[4], msg[5]]);
130+
let sn_high = u32::from_le_bytes([msg[6], msg[7], msg[8], msg[9]]);
131+
let vid = u16::from_le_bytes([msg[14], msg[15]]);
132+
let vendor = if vid == 0x32AC {
133+
" (Framework Computer)"
134+
} else {
135+
""
136+
};
137+
let pid = u16::from_le_bytes([msg[16], msg[17]]);
138+
let product = if pid == 0x002B {
139+
" (Framework Stylus)"
140+
} else {
141+
""
142+
};
143+
println!("Stylus");
144+
println!(" Serial Number: {:X}-{:X}", sn_high, sn_low);
145+
debug!(" Redundant SN {:X?}", &msg[10..14]);
146+
println!(" Vendor ID: {:04X}{}", vid, vendor);
147+
println!(" Product ID: {:04X}{}", pid, product);
148+
println!(" Firmware Version: {:02X}.{:02X}", &msg[18], msg[19]);
149+
150+
Some(())
151+
}
100152
}
101153

102154
pub trait TouchScreen {
103-
fn open_device() -> Option<Self>
155+
fn open_device(usage_page: u16, skip: u8) -> Option<Self>
104156
where
105157
Self: std::marker::Sized;
106158
fn send_message(&self, message_id: u8, read_len: usize, data: Vec<u8>) -> Option<Vec<u8>>;
107159

108160
fn check_fw_version(&self) -> Option<()> {
109161
println!("Touchscreen");
162+
110163
let res = self.send_message(0x42, 3, vec![0])?;
111164
let ver = res
112165
.iter()
@@ -135,22 +188,32 @@ pub trait TouchScreen {
135188
self.send_message(0x38, 0, vec![!enable as u8, 0x00])?;
136189
Some(())
137190
}
191+
192+
fn get_stylus_fw(&self) -> Option<()>;
138193
}
139194

140195
pub fn print_fw_ver() -> Option<()> {
196+
for skip in 0..5 {
197+
if let Some(device) = HidapiTouchScreen::open_device(0x000D, skip) {
198+
if device.get_stylus_fw().is_some() {
199+
break;
200+
}
201+
}
202+
}
203+
141204
#[cfg(target_os = "windows")]
142-
let device = touchscreen_win::NativeWinTouchScreen::open_device()?;
205+
let device = touchscreen_win::NativeWinTouchScreen::open_device(VENDOR_USAGE_PAGE, 0)?;
143206
#[cfg(not(target_os = "windows"))]
144-
let device = HidapiTouchScreen::open_device()?;
207+
let device = HidapiTouchScreen::open_device(VENDOR_USAGE_PAGE, 0)?;
145208

146209
device.check_fw_version()
147210
}
148211

149212
pub fn enable_touch(enable: bool) -> Option<()> {
150213
#[cfg(target_os = "windows")]
151-
let device = touchscreen_win::NativeWinTouchScreen::open_device()?;
214+
let device = touchscreen_win::NativeWinTouchScreen::open_device(VENDOR_USAGE_PAGE, 0)?;
152215
#[cfg(not(target_os = "windows"))]
153-
let device = HidapiTouchScreen::open_device()?;
216+
let device = HidapiTouchScreen::open_device(VENDOR_USAGE_PAGE, 0)?;
154217

155218
device.enable_touch(enable)
156219
}

framework_lib/src/touchscreen_win.rs

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,20 @@ use windows::{
1616
},
1717
};
1818

19+
const REPORT_ID_FIRMWARE: u8 = 0x27;
20+
const REPORT_ID_USI_VER: u8 = 0x28;
21+
1922
pub struct NativeWinTouchScreen {
2023
handle: HANDLE,
2124
}
2225

2326
impl TouchScreen for NativeWinTouchScreen {
24-
fn open_device() -> Option<Self> {
25-
debug!("Looking for touchscreen HID device");
27+
fn open_device(target_up: u16, skip: u8) -> Option<Self> {
28+
debug!(
29+
"Looking for touchscreen HID device {:X} {}",
30+
target_up, skip
31+
);
32+
let mut skip = skip;
2633
match HidApi::new() {
2734
Ok(api) => {
2835
for dev_info in api.device_list() {
@@ -37,7 +44,7 @@ impl TouchScreen for NativeWinTouchScreen {
3744
" Found {:04X}:{:04X} (Usage Page {:04X})",
3845
vid, pid, usage_page
3946
);
40-
if usage_page != 0xFF00 {
47+
if usage_page != target_up {
4148
debug!(" Skipping usage page. Expected {:04X}", 0xFF00);
4249
continue;
4350
}
@@ -48,6 +55,10 @@ impl TouchScreen for NativeWinTouchScreen {
4855
debug!(" Found matching touchscreen HID device");
4956
debug!(" Path: {:?}", dev_info.path());
5057
debug!(" IC Type: {:04X}", pid);
58+
if skip > 0 {
59+
skip -= 1;
60+
continue;
61+
}
5162

5263
// TODO: Enumerate with windows
5364
// Should enumerate and find the right one
@@ -135,4 +146,24 @@ impl TouchScreen for NativeWinTouchScreen {
135146

136147
Some(buf[msg_len..msg_len + read_len].to_vec())
137148
}
149+
150+
fn get_stylus_fw(&self) -> Option<()> {
151+
let mut msg = [0u8; 0x40];
152+
msg[0] = REPORT_ID_FIRMWARE;
153+
unsafe {
154+
let success = HidD_GetFeature(self.handle, msg.as_mut_ptr() as _, msg.len() as u32);
155+
debug!(" Success: {}", success);
156+
}
157+
println!("Stylus firmware: {:X?}", msg);
158+
159+
let mut msg = [0u8; 0x40];
160+
msg[0] = REPORT_ID_USI_VER;
161+
unsafe {
162+
let success = HidD_GetFeature(self.handle, msg.as_mut_ptr() as _, msg.len() as u32);
163+
debug!(" Success: {}", success);
164+
}
165+
println!("USI Version: {:X?}", msg);
166+
167+
None
168+
}
138169
}

0 commit comments

Comments
 (0)