|
| 1 | +#[allow(unused_imports)] |
| 2 | +use windows::{ |
| 3 | + core::*, |
| 4 | + Win32::{ |
| 5 | + Devices::HumanInterfaceDevice::*, |
| 6 | + Devices::Properties::*, |
| 7 | + Foundation::*, |
| 8 | + Storage::FileSystem::*, |
| 9 | + System::{Ioctl::*, IO::*}, |
| 10 | + System::Threading::ResetEvent, |
| 11 | + System::IO::{CancelIoEx, DeviceIoControl}, |
| 12 | + }, |
| 13 | +}; |
| 14 | + |
| 15 | +pub const USI_BITMAP: u8 = 1 << 1; |
| 16 | +pub const MPP_BITMAP: u8 = 1 << 2; |
| 17 | + |
| 18 | +pub fn open_device(open_rw: bool) -> Option<HANDLE> { |
| 19 | + // TODO: I don't know if this might be different on other systems |
| 20 | + // Should enumerate and find the right one |
| 21 | + // See: https://learn.microsoft.com/en-us/windows-hardware/drivers/hid/finding-and-opening-a-hid-collection |
| 22 | + let path = w!(r"\\?\HID#ILIT2901&Col03#5&357cbf85&0&0002#{4d1e55b2-f16f-11cf-88cb-001111000030}"); |
| 23 | + |
| 24 | + let res = unsafe { |
| 25 | + CreateFileW( |
| 26 | + path, |
| 27 | + match open_rw { |
| 28 | + true => FILE_GENERIC_WRITE.0 | FILE_GENERIC_READ.0, |
| 29 | + false => 0, |
| 30 | + }, |
| 31 | + FILE_SHARE_READ | FILE_SHARE_WRITE, |
| 32 | + None, |
| 33 | + OPEN_EXISTING, |
| 34 | + // hidapi-rs is using FILE_FLAG_OVERLAPPED but it doesn't look like we need that |
| 35 | + FILE_FLAGS_AND_ATTRIBUTES(0), |
| 36 | + None, |
| 37 | + ) |
| 38 | + }; |
| 39 | + let handle = match res { |
| 40 | + Ok(h) => h, |
| 41 | + Err(err) => { |
| 42 | + error!("Failed to open device {:?}", err); |
| 43 | + return None; |
| 44 | + } |
| 45 | + }; |
| 46 | + |
| 47 | + debug!("Opened {:?}", path); |
| 48 | + |
| 49 | + Some(handle) |
| 50 | +} |
| 51 | + |
| 52 | +pub fn send_message(handle: &HANDLE, message_id: u8, read_len: usize, data: Vec<u8>) -> Option<Vec<u8>> { |
| 53 | + let report_id = 0x03; |
| 54 | + let data_len = data.len(); |
| 55 | + let mut msg = [0u8; 0x40]; |
| 56 | + let msg_len = 3 + data_len; |
| 57 | + msg[0] = report_id; |
| 58 | + msg[1] = 0xA3; |
| 59 | + msg[2] = data_len as u8; |
| 60 | + msg[3] = read_len as u8; |
| 61 | + msg[4] = message_id; |
| 62 | + for (i, b) in data.into_iter().enumerate() { |
| 63 | + msg[5 + i] = b; |
| 64 | + } |
| 65 | + |
| 66 | + let mut buf = [0u8; 0x40]; |
| 67 | + buf[0] = report_id; |
| 68 | + |
| 69 | + unsafe { |
| 70 | + debug!(" HidD_SetOutputReport {:X?}", msg); |
| 71 | + let success = HidD_SetOutputReport( |
| 72 | + *handle, |
| 73 | + // Microsoft docs says that the first byte of the message has to be the report ID. |
| 74 | + // This is normal with HID implementations. |
| 75 | + // But it seems on Windows (at least for this device's firmware) we have to set the |
| 76 | + // length as one more than the buffer is long. |
| 77 | + // Otherwise no data is returned in the read call later. |
| 78 | + msg.as_mut_ptr() as _, |
| 79 | + msg.len() as u32 + 1, |
| 80 | + ); |
| 81 | + debug!(" Success: {}", success); |
| 82 | + |
| 83 | + let mut bytes_read = 0; |
| 84 | + debug!(" ReadFile"); |
| 85 | + // HidD_GetFeature doesn't work, have to use ReadFile |
| 86 | + // Microsoft does recommend that |
| 87 | + // https://learn.microsoft.com/en-us/windows-hardware/drivers/hid/obtaining-hid-reports |
| 88 | + let res = ReadFile( |
| 89 | + *handle, |
| 90 | + Some(&mut buf), |
| 91 | + Some(&mut bytes_read), |
| 92 | + None, |
| 93 | + ); |
| 94 | + debug!(" Success: {:?}, Bytes: {}", res, bytes_read); |
| 95 | + debug!(" Read buf: {:X?}", buf); |
| 96 | + debug!(" Read msg: {:X?}", msg); |
| 97 | + |
| 98 | + } |
| 99 | + |
| 100 | + Some(buf[msg_len..msg_len + read_len].to_vec()) |
| 101 | +} |
| 102 | + |
| 103 | +pub fn check_fw_version(device: &HANDLE) -> Option<()> { |
| 104 | + let res = send_message(device, 0x42, 3, vec![0])?; |
| 105 | + let ver = res |
| 106 | + .iter() |
| 107 | + .skip(1) |
| 108 | + .fold(format!("{:02X}", res[0]), |acc, &x| { |
| 109 | + acc + "." + &format!("{:02X}", x) |
| 110 | + }); |
| 111 | + // Expecting 06.00.0A |
| 112 | + debug!(" Protocol Version: v{}", ver); |
| 113 | + |
| 114 | + let res = send_message(device, 0x40, 8, vec![0])?; |
| 115 | + let ver = res |
| 116 | + .iter() |
| 117 | + .skip(1) |
| 118 | + .fold(res[0].to_string(), |acc, &x| acc + "." + &x.to_string()); |
| 119 | + println!(" Firmware Version: v{}", ver); |
| 120 | + |
| 121 | + let res = send_message(device, 0x20, 16, vec![0])?; |
| 122 | + println!(" USI Protocol: {:?}", (res[15] & USI_BITMAP) > 0); |
| 123 | + println!(" MPP Protocol: {:?}", (res[15] & MPP_BITMAP) > 0); |
| 124 | + |
| 125 | + Some(()) |
| 126 | +} |
| 127 | + |
| 128 | +pub fn print_touchscreen_fw_ver() -> Option<()> { |
| 129 | + let device = open_device(true)?; |
| 130 | + |
| 131 | + if let None = check_fw_version(&device) { |
| 132 | + error!("Failed to read touchscreen firmware version"); |
| 133 | + }; |
| 134 | + |
| 135 | + Some(()) |
| 136 | +} |
0 commit comments