mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-11-01 18:30:51 +08:00
fix(hid): 兼容 CH9329/CH9329F 串口响应 (#300)
支持 CH9329/CH9329F 扩展参数响应、粘包与乱序处理,保留严格校验和及有界接收缓冲。
This commit is contained in:
@@ -10,6 +10,7 @@ pub const DEFAULT_ADDR: u8 = 0x00;
|
||||
pub const DEFAULT_BAUD_RATE: u32 = 9600;
|
||||
pub const MAX_DATA_LEN: usize = 64;
|
||||
pub const MAX_PACKET_SIZE: usize = 70;
|
||||
const EXTENDED_PARAMETER_RESPONSE_SIZES: [usize; 2] = [72, 88];
|
||||
|
||||
pub mod cmd {
|
||||
pub const GET_INFO: u8 = 0x01;
|
||||
@@ -130,7 +131,8 @@ impl Response {
|
||||
|
||||
let cmd = bytes[3];
|
||||
let len = bytes[4] as usize;
|
||||
if bytes.len() < 5 + len + 1 {
|
||||
let expected_frame_len = 6 + len;
|
||||
if bytes.len() < expected_frame_len {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -139,7 +141,7 @@ impl Response {
|
||||
.iter()
|
||||
.fold(0u8, |acc, &x| acc.wrapping_add(x));
|
||||
if expected_checksum != calculated_checksum {
|
||||
tracing::warn!(
|
||||
tracing::debug!(
|
||||
"CH9329 checksum mismatch: expected {:02X}, got {:02X}",
|
||||
expected_checksum,
|
||||
calculated_checksum
|
||||
@@ -215,6 +217,11 @@ pub fn try_extract_response(buffer: &[u8]) -> Option<(Response, usize)> {
|
||||
}
|
||||
|
||||
let len = buffer[offset + 4] as usize;
|
||||
if len > MAX_DATA_LEN {
|
||||
offset += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
let frame_len = 6 + len;
|
||||
if offset + frame_len > buffer.len() {
|
||||
return None;
|
||||
@@ -225,8 +232,103 @@ pub fn try_extract_response(buffer: &[u8]) -> Option<(Response, usize)> {
|
||||
return Some((response, offset + frame_len));
|
||||
}
|
||||
|
||||
// Some CH9329F firmware appends reserved bytes to GET_PARA_CFG while
|
||||
// retaining the protocol LEN value of 50. Locate and validate the real
|
||||
// checksum, return the documented 50-byte payload, and consume the
|
||||
// complete extended frame. Other commands keep strict framing.
|
||||
let cmd = buffer[offset + 3];
|
||||
let data_start = offset + 5;
|
||||
let parameter_payload_is_plausible = cmd == expected_response_cmd(cmd::GET_PARA_CFG, false)
|
||||
&& len == 50
|
||||
&& matches!(buffer[data_start], 0x00..=0x03 | 0x80..=0x83)
|
||||
&& matches!(buffer[data_start + 1], 0x00..=0x02 | 0x80..=0x82);
|
||||
if parameter_payload_is_plausible {
|
||||
for extended_size in EXTENDED_PARAMETER_RESPONSE_SIZES {
|
||||
let extended_end = offset + extended_size;
|
||||
if buffer.len() >= extended_end {
|
||||
let checksum_index = extended_end - 1;
|
||||
if calculate_checksum(&buffer[offset..checksum_index]) != buffer[checksum_index]
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return Some((
|
||||
Response {
|
||||
cmd,
|
||||
data: buffer[data_start..data_start + len].to_vec(),
|
||||
is_error: false,
|
||||
error_code: None,
|
||||
},
|
||||
extended_end,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if buffer.len() < offset + EXTENDED_PARAMETER_RESPONSE_SIZES[1] {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parses_standard_response_and_checksum() {
|
||||
let frame = build_packet(DEFAULT_ADDR, 0x81, &[0x30, 0x01, 0x00, 0, 0, 0, 0, 0]);
|
||||
let response = Response::parse(&frame).expect("valid response");
|
||||
assert_eq!(response.cmd, 0x81);
|
||||
assert_eq!(response.data, vec![0x30, 0x01, 0x00, 0, 0, 0, 0, 0]);
|
||||
assert!(!response.is_error);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_extended_parameter_response_with_valid_trailing_checksum() {
|
||||
let payload = [
|
||||
0x80, 0x80, 0x00, 0x00, 0x00, 0x25, 0x80, 0x08, 0x00, 0x00, 0x03, 0x86, 0x1A, 0x2A,
|
||||
0xE1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
];
|
||||
for reserved_len in [16, 32] {
|
||||
let mut frame = vec![0x57, 0xAB, DEFAULT_ADDR, 0x88, 50];
|
||||
frame.extend_from_slice(&payload);
|
||||
frame.extend_from_slice(&vec![0; reserved_len]);
|
||||
frame.push(calculate_checksum(&frame));
|
||||
|
||||
assert!(Response::parse(&frame[..56]).is_none());
|
||||
let (response, consumed) = try_extract_response(&frame).expect("extended response");
|
||||
assert_eq!(response.cmd, 0x88);
|
||||
assert_eq!(response.data, payload);
|
||||
assert!(!response.is_error);
|
||||
assert_eq!(consumed, frame.len());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_bad_checksum_for_other_commands() {
|
||||
let mut frame = build_packet(DEFAULT_ADDR, 0x89, &[0x00; 50]);
|
||||
*frame.last_mut().unwrap() ^= 0xFF;
|
||||
assert!(Response::parse(&frame).is_none());
|
||||
assert!(try_extract_response(&frame).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_noise_and_adjacent_packets() {
|
||||
let first = build_packet(DEFAULT_ADDR, 0x81, &[0x30, 0x01, 0, 0, 0, 0, 0, 0]);
|
||||
let second = build_packet(DEFAULT_ADDR, 0x82, &[0x00]);
|
||||
let mut buffer = vec![0x00, 0xFF];
|
||||
buffer.extend_from_slice(&first);
|
||||
buffer.extend_from_slice(&second);
|
||||
|
||||
let (_, consumed) = try_extract_response(&buffer).expect("first response");
|
||||
assert_eq!(consumed, 2 + first.len());
|
||||
let (response, _) = try_extract_response(&buffer[consumed..]).expect("second response");
|
||||
assert_eq!(response.cmd, 0x82);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user