1use super::{line_coding_bytes, Driver, ModemStatus, WRITE_TIMEOUT_MS};
4use crate::config::{DataBits, FlowControl, LineConfig, Parity, PurgeKind, StopBits};
5use crate::error::{Result, UsbSerialError};
6use crate::reader::SerialReader;
7use crate::rx_filter::XonXoffRxFilter;
8use crate::transport::{BulkIn, ControlRequest, EndpointInfo, SharedTransport};
9use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
10use std::sync::{Arc, Mutex};
11use std::thread::{self, JoinHandle};
12
13const WRITE_ENDPOINT: u8 = 0x02;
14const READ_ENDPOINT: u8 = 0x83;
15const INTERRUPT_ENDPOINT: u8 = 0x81;
16
17const VENDOR_READ_REQUEST: u8 = 0x01;
18const VENDOR_WRITE_REQUEST: u8 = 0x01;
19const VENDOR_READ_HXN_REQUEST: u8 = 0x81;
20const VENDOR_WRITE_HXN_REQUEST: u8 = 0x80;
21
22const RESET_HXN_REQUEST: u8 = 0x07;
23const FLUSH_RX_REQUEST: u8 = 0x08;
24const FLUSH_TX_REQUEST: u8 = 0x09;
25const SET_LINE_REQUEST: u8 = 0x20;
26const SET_CONTROL_REQUEST: u8 = 0x22;
27const SEND_BREAK_REQUEST: u8 = 0x23;
28const GET_CONTROL_HXN_REQUEST: u8 = 0x80;
29const GET_CONTROL_REQUEST: u8 = 0x87;
30const STATUS_NOTIFICATION: u8 = 0xa1;
31
32const RESET_HXN_RX_PIPE: u16 = 1;
33const RESET_HXN_TX_PIPE: u16 = 2;
34
35const CONTROL_DTR: u16 = 0x01;
36const CONTROL_RTS: u16 = 0x02;
37
38const GET_CONTROL_FLAG_CD: u8 = 0x02;
39const GET_CONTROL_FLAG_DSR: u8 = 0x04;
40const GET_CONTROL_FLAG_RI: u8 = 0x01;
41const GET_CONTROL_FLAG_CTS: u8 = 0x08;
42
43const GET_CONTROL_HXN_FLAG_CD: u8 = 0x40;
44const GET_CONTROL_HXN_FLAG_DSR: u8 = 0x20;
45const GET_CONTROL_HXN_FLAG_RI: u8 = 0x80;
46const GET_CONTROL_HXN_FLAG_CTS: u8 = 0x08;
47
48const STATUS_FLAG_CD: u8 = 0x01;
49const STATUS_FLAG_DSR: u8 = 0x02;
50const STATUS_FLAG_RI: u8 = 0x08;
51const STATUS_FLAG_CTS: u8 = 0x80;
52
53const STATUS_BUFFER_SIZE: usize = 10;
54const STATUS_BYTE_IDX: usize = 8;
55
56const STANDARD_BAUD_RATES: &[u32] = &[
57 75, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 14400, 19200, 28800, 38400, 57600,
58 115200, 128000, 134400, 161280, 201600, 230400, 268800, 403200, 460800, 614400, 806400, 921600,
59 1228800, 2457600, 3000000, 6000000,
60];
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63enum DeviceType {
64 Type01,
65 TypeT,
66 TypeHx,
67 TypeHxn,
68}
69
70struct ProlificEndpoints {
71 data: super::EndpointPair,
72 interrupt_ep: u8,
73 interrupt_mps: u16,
74}
75
76pub struct ProlificDriver {
77 #[allow(dead_code)]
78 port_index: usize,
79 iface: u8,
80 device_type: DeviceType,
81 control_lines: u16,
82 flow: FlowControl,
83 baud_rate: i32,
84 data_bits: i32,
85 stop_bits: i32,
86 parity: i32,
87 endpoints: Option<ProlificEndpoints>,
88 transport: Option<SharedTransport>,
89 reader: Option<SerialReader>,
90 status: Arc<AtomicU8>,
91 status_thread: Option<JoinHandle<()>>,
92 stop_status_thread: Arc<AtomicBool>,
93 status_error: Arc<Mutex<Option<String>>>,
94 status_started: bool,
95}
96
97impl ProlificDriver {
98 pub fn new(port_index: usize) -> Self {
99 Self {
100 port_index,
101 iface: 0,
102 device_type: DeviceType::TypeHx,
103 control_lines: 0,
104 flow: FlowControl::None,
105 baud_rate: -1,
106 data_bits: -1,
107 stop_bits: -1,
108 parity: -1,
109 endpoints: None,
110 transport: None,
111 reader: None,
112 status: Arc::new(AtomicU8::new(0)),
113 status_thread: None,
114 stop_status_thread: Arc::new(AtomicBool::new(false)),
115 status_error: Arc::new(Mutex::new(None)),
116 status_started: false,
117 }
118 }
119
120 fn vendor_read_request(&self) -> u8 {
121 if self.device_type == DeviceType::TypeHxn {
122 VENDOR_READ_HXN_REQUEST
123 } else {
124 VENDOR_READ_REQUEST
125 }
126 }
127
128 fn vendor_write_request(&self) -> u8 {
129 if self.device_type == DeviceType::TypeHxn {
130 VENDOR_WRITE_HXN_REQUEST
131 } else {
132 VENDOR_WRITE_REQUEST
133 }
134 }
135
136 fn vendor_in(&self, value: u16, index: u16, length: usize) -> Result<Vec<u8>> {
137 self.transport
138 .as_ref()
139 .unwrap()
140 .control_in(&ControlRequest {
141 request_type: 0xC0,
142 request: self.vendor_read_request(),
143 value,
144 index,
145 data: vec![0; length],
146 timeout_ms: WRITE_TIMEOUT_MS,
147 })
148 }
149
150 fn vendor_out(&self, value: u16, index: u16, data: Vec<u8>) -> Result<()> {
151 self.transport
152 .as_ref()
153 .unwrap()
154 .control_out(&ControlRequest {
155 request_type: 0x40,
156 request: self.vendor_write_request(),
157 value,
158 index,
159 data,
160 timeout_ms: WRITE_TIMEOUT_MS,
161 })?;
162 Ok(())
163 }
164
165 fn ctrl_out(&self, request: u8, value: u16, index: u16, data: Vec<u8>) -> Result<()> {
166 self.transport
167 .as_ref()
168 .unwrap()
169 .control_out(&ControlRequest::class_out(request, value, index, data))?;
170 Ok(())
171 }
172
173 fn detect_device_type(&mut self, transport: &SharedTransport) -> Result<()> {
174 let raw = transport.raw_descriptors();
175 let desc = if raw.len() >= 14 {
176 raw
177 } else {
178 transport.raw_device_descriptor().to_vec()
179 };
180 if desc.len() < 14 {
181 return Err(UsbSerialError::Io(
182 "could not get device descriptors".into(),
183 ));
184 }
185 let usb_version = u16::from_le_bytes([desc[2], desc[3]]);
186 let device_version = u16::from_le_bytes([desc[12], desc[13]]);
187 let max_packet_size0 = desc[7];
188 let device_class = transport.device_class();
189
190 self.device_type = if device_class == 0x02 || max_packet_size0 != 64 {
191 DeviceType::Type01
192 } else if usb_version == 0x200 {
193 if (device_version == 0x300 || device_version == 0x500) && self.test_hx_status() {
194 DeviceType::TypeT
195 } else {
196 DeviceType::TypeHxn
197 }
198 } else {
199 DeviceType::TypeHx
200 };
201 Ok(())
202 }
203
204 fn test_hx_status(&self) -> bool {
205 self.transport
206 .as_ref()
207 .unwrap()
208 .control_in(&ControlRequest {
209 request_type: 0xC0,
210 request: VENDOR_READ_REQUEST,
211 value: 0x8080,
212 index: 0,
213 data: vec![0; 1],
214 timeout_ms: WRITE_TIMEOUT_MS,
215 })
216 .is_ok()
217 }
218
219 fn reset_device(&self) -> Result<()> {
220 self.purge_hw_buffers(true, true)
221 }
222
223 fn purge_hw_buffers(&self, purge_write: bool, purge_read: bool) -> Result<()> {
224 if self.device_type == DeviceType::TypeHxn {
225 let mut index = 0u16;
226 if purge_write {
227 index |= RESET_HXN_RX_PIPE;
228 }
229 if purge_read {
230 index |= RESET_HXN_TX_PIPE;
231 }
232 if index != 0 {
233 self.vendor_out(RESET_HXN_REQUEST as u16, index, vec![])?;
234 }
235 } else {
236 if purge_write {
237 self.vendor_out(FLUSH_RX_REQUEST as u16, 0, vec![])?;
238 }
239 if purge_read {
240 self.vendor_out(FLUSH_TX_REQUEST as u16, 0, vec![])?;
241 }
242 }
243 Ok(())
244 }
245
246 fn do_black_magic(&self) -> Result<()> {
247 if self.device_type == DeviceType::TypeHxn {
248 return Ok(());
249 }
250 self.vendor_in(0x8484, 0, 1)?;
251 self.vendor_out(0x0404, 0, vec![])?;
252 self.vendor_in(0x8484, 0, 1)?;
253 self.vendor_in(0x8383, 0, 1)?;
254 self.vendor_in(0x8484, 0, 1)?;
255 self.vendor_out(0x0404, 1, vec![])?;
256 self.vendor_in(0x8484, 0, 1)?;
257 self.vendor_in(0x8383, 0, 1)?;
258 self.vendor_out(0, 1, vec![])?;
259 self.vendor_out(1, 0, vec![])?;
260 let magic = if self.device_type == DeviceType::Type01 {
261 0x24u16
262 } else {
263 0x44
264 };
265 self.vendor_out(2, magic, vec![])
266 }
267
268 fn set_control_lines(&mut self, value: u16) -> Result<()> {
269 self.ctrl_out(SET_CONTROL_REQUEST, value, 0, vec![])?;
270 self.control_lines = value;
271 Ok(())
272 }
273
274 fn open_endpoints(transport: &SharedTransport, iface: u8) -> Result<ProlificEndpoints> {
275 let eps = transport.endpoints(iface);
276 let find = |addr: u8| -> Result<EndpointInfo> {
277 eps.iter()
278 .find(|e| e.address == addr)
279 .copied()
280 .ok_or_else(|| {
281 UsbSerialError::ProbeFailed(format!("missing endpoint 0x{addr:02x}"))
282 })
283 };
284 let in_ep = find(READ_ENDPOINT)?;
285 let out_ep = find(WRITE_ENDPOINT)?;
286 let int_ep = find(INTERRUPT_ENDPOINT)?;
287 Ok(ProlificEndpoints {
288 data: super::EndpointPair::from_addresses(
289 in_ep.address,
290 out_ep.address,
291 in_ep.max_packet_size,
292 ),
293 interrupt_ep: int_ep.address,
294 interrupt_mps: int_ep.max_packet_size,
295 })
296 }
297
298 fn ensure_status_thread(&mut self) -> Result<()> {
299 if self.status_started {
300 return Ok(());
301 }
302 self.status.store(0, Ordering::Relaxed);
303
304 let initial = if self.device_type == DeviceType::TypeHxn {
305 let data = self.vendor_in(GET_CONTROL_HXN_REQUEST as u16, 0, 1)?;
306 decode_vendor_status_hxn(data[0])
307 } else {
308 let data = self.vendor_in(GET_CONTROL_REQUEST as u16, 0, 1)?;
309 decode_vendor_status(data[0])
310 };
311 self.status.store(initial, Ordering::Relaxed);
312
313 let transport = self.transport.as_ref().unwrap().clone();
314 let interrupt_ep = self.endpoints.as_ref().unwrap().interrupt_ep;
315 let interrupt_mps = self.endpoints.as_ref().unwrap().interrupt_mps;
316 let stop = self.stop_status_thread.clone();
317 let status = self.status.clone();
318 let status_error = self.status_error.clone();
319
320 stop.store(false, Ordering::Relaxed);
321 let handle = thread::spawn(move || {
322 let mut interrupt = match transport.open_interrupt_in(interrupt_ep, interrupt_mps) {
323 Ok(ep) => ep,
324 Err(e) => {
325 *status_error.lock().unwrap() = Some(e.to_string());
326 return;
327 }
328 };
329 let mut buffer = vec![0u8; STATUS_BUFFER_SIZE];
330 while !stop.load(Ordering::Relaxed) {
331 match interrupt.read(&mut buffer, 500) {
332 Ok(crate::error::ReadOutcome::Data(data)) => {
333 if data.len() != STATUS_BUFFER_SIZE {
334 *status_error.lock().unwrap() = Some(format!(
335 "invalid status notification, expected {STATUS_BUFFER_SIZE} bytes, got {}",
336 data.len()
337 ));
338 break;
339 }
340 if data[0] != STATUS_NOTIFICATION {
341 *status_error.lock().unwrap() = Some(format!(
342 "invalid status notification, expected 0x{STATUS_NOTIFICATION:02x}, got 0x{:02x}",
343 data[0]
344 ));
345 break;
346 }
347 status.store(data[STATUS_BYTE_IDX], Ordering::Relaxed);
348 }
349 Ok(crate::error::ReadOutcome::TimedOut)
350 | Ok(crate::error::ReadOutcome::Cancelled) => {}
351 Err(e) => {
352 if !stop.load(Ordering::Relaxed) {
353 *status_error.lock().unwrap() = Some(e.to_string());
354 }
355 break;
356 }
357 }
358 }
359 });
360 self.status_thread = Some(handle);
361 self.status_started = true;
362 Ok(())
363 }
364
365 fn get_status(&mut self) -> Result<u8> {
366 self.ensure_status_thread()?;
367 if let Some(msg) = self.status_error.lock().unwrap().take() {
368 return Err(UsbSerialError::Io(msg));
369 }
370 Ok(self.status.load(Ordering::Relaxed))
371 }
372
373 fn stop_status_thread(&mut self) {
374 self.stop_status_thread.store(true, Ordering::Relaxed);
375 if let Some(handle) = self.status_thread.take() {
376 let _ = handle.join();
377 }
378 self.stop_status_thread.store(false, Ordering::Relaxed);
379 self.status_started = false;
380 *self.status_error.lock().unwrap() = None;
381 }
382
383 fn filter_baud_rate(&self, baud_rate: u32) -> Result<u32> {
384 if baud_rate == 0 {
385 return Err(UsbSerialError::Unsupported(format!(
386 "invalid baud rate: {baud_rate}"
387 )));
388 }
389 if self.device_type == DeviceType::TypeHxn {
390 return Ok(baud_rate);
391 }
392 if STANDARD_BAUD_RATES.contains(&baud_rate) {
393 return Ok(baud_rate);
394 }
395
396 let baseline = 12000000u64 * 32;
397 let mut mantissa = (baseline / baud_rate as u64) as u32;
398 if mantissa == 0 {
399 return Err(UsbSerialError::Unsupported("baud rate too high".into()));
400 }
401
402 let (buf, effective) = if self.device_type == DeviceType::TypeT {
403 let mut exponent = 0u32;
404 while mantissa >= 2048 {
405 if exponent < 15 {
406 mantissa >>= 1;
407 exponent += 1;
408 } else {
409 return Err(UsbSerialError::Unsupported("baud rate too low".into()));
410 }
411 }
412 let buf = mantissa + ((exponent & !1) << 12) + ((exponent & 1) << 16) + (1 << 31);
413 let effective = (baseline / mantissa as u64) >> exponent;
414 (buf, effective)
415 } else {
416 let mut exponent = 0u32;
417 while mantissa >= 512 {
418 if exponent < 7 {
419 mantissa >>= 2;
420 exponent += 1;
421 } else {
422 return Err(UsbSerialError::Unsupported("baud rate too low".into()));
423 }
424 }
425 let buf = mantissa + (exponent << 9) + (1 << 31);
426 let effective = (baseline / mantissa as u64) >> (exponent * 2);
427 (buf, effective)
428 };
429
430 let error = (1.0 - (effective as f64 / baud_rate as f64)).abs();
431 if error >= 0.031 {
432 return Err(UsbSerialError::Unsupported(format!(
433 "baud rate deviation {:.1}% is higher than allowed 3%",
434 error * 100.0
435 )));
436 }
437 Ok(buf)
438 }
439
440 fn line_request_data(&self, cfg: &LineConfig, baud: u32) -> Result<Vec<u8>> {
441 let stop = match cfg.stop_bits {
442 StopBits::One => 0u8,
443 StopBits::OnePointFive => 1,
444 StopBits::Two => 2,
445 };
446 let parity = match cfg.parity {
447 Parity::None => 0,
448 Parity::Odd => 1,
449 Parity::Even => 2,
450 Parity::Mark => 3,
451 Parity::Space => 4,
452 };
453 let data_bits = match cfg.data_bits {
454 DataBits::Five => 5,
455 DataBits::Six => 6,
456 DataBits::Seven => 7,
457 DataBits::Eight => 8,
458 };
459 let mut line = line_coding_bytes(cfg);
460 line[0] = (baud & 0xff) as u8;
461 line[1] = ((baud >> 8) & 0xff) as u8;
462 line[2] = ((baud >> 16) & 0xff) as u8;
463 line[3] = ((baud >> 24) & 0xff) as u8;
464 line[4] = stop;
465 line[5] = parity;
466 line[6] = data_bits;
467 Ok(line.to_vec())
468 }
469}
470
471fn decode_vendor_status(byte: u8) -> u8 {
472 let mut status = 0u8;
473 if byte & GET_CONTROL_FLAG_CTS == 0 {
474 status |= STATUS_FLAG_CTS;
475 }
476 if byte & GET_CONTROL_FLAG_DSR == 0 {
477 status |= STATUS_FLAG_DSR;
478 }
479 if byte & GET_CONTROL_FLAG_CD == 0 {
480 status |= STATUS_FLAG_CD;
481 }
482 if byte & GET_CONTROL_FLAG_RI == 0 {
483 status |= STATUS_FLAG_RI;
484 }
485 status
486}
487
488fn decode_vendor_status_hxn(byte: u8) -> u8 {
489 let mut status = 0u8;
490 if byte & GET_CONTROL_HXN_FLAG_CTS == 0 {
491 status |= STATUS_FLAG_CTS;
492 }
493 if byte & GET_CONTROL_HXN_FLAG_DSR == 0 {
494 status |= STATUS_FLAG_DSR;
495 }
496 if byte & GET_CONTROL_HXN_FLAG_CD == 0 {
497 status |= STATUS_FLAG_CD;
498 }
499 if byte & GET_CONTROL_HXN_FLAG_RI == 0 {
500 status |= STATUS_FLAG_RI;
501 }
502 status
503}
504
505fn status_flag_to_modem(status: u8) -> ModemStatus {
506 ModemStatus {
507 cts: status & STATUS_FLAG_CTS != 0,
508 dsr: status & STATUS_FLAG_DSR != 0,
509 ri: status & STATUS_FLAG_RI != 0,
510 cd: status & STATUS_FLAG_CD != 0,
511 }
512}
513
514impl Driver for ProlificDriver {
515 fn open(&mut self, transport: &SharedTransport) -> Result<()> {
516 self.transport = Some(transport.clone());
517 self.iface = 0;
518 transport.claim_interface(self.iface)?;
519 self.endpoints = Some(Self::open_endpoints(transport, self.iface)?);
520 self.detect_device_type(transport)?;
521 self.reset_device()?;
522 self.do_black_magic()?;
523 self.set_control_lines(self.control_lines)?;
524 self.set_flow_control(self.flow)?;
525 Ok(())
526 }
527
528 fn close(&mut self) -> Result<()> {
529 if let Some(mut r) = self.reader.take() {
530 r.stop();
531 }
532 self.stop_status_thread();
533 let _ = self.reset_device();
534 if let Some(t) = &self.transport {
535 let _ = t.release_interface(self.iface);
536 }
537 self.endpoints = None;
538 Ok(())
539 }
540
541 fn write(&mut self, data: &[u8]) -> Result<usize> {
542 let t = self.transport.as_ref().unwrap();
543 self.endpoints.as_mut().unwrap().data.write(t, data)
544 }
545
546 fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
547 if let Some(reader) = &mut self.reader {
548 return reader.try_read(buf);
549 }
550 Ok(0)
551 }
552
553 fn set_line_config(&mut self, cfg: LineConfig) -> Result<()> {
554 let baud = self.filter_baud_rate(cfg.baud_rate)?;
555 let data_bits = match cfg.data_bits {
556 DataBits::Five => 5,
557 DataBits::Six => 6,
558 DataBits::Seven => 7,
559 DataBits::Eight => 8,
560 };
561 let stop_bits = match cfg.stop_bits {
562 StopBits::One => 1,
563 StopBits::OnePointFive => 3,
564 StopBits::Two => 2,
565 };
566 let parity = match cfg.parity {
567 Parity::None => 0,
568 Parity::Odd => 1,
569 Parity::Even => 2,
570 Parity::Mark => 3,
571 Parity::Space => 4,
572 };
573
574 if self.baud_rate == baud as i32
575 && self.data_bits == data_bits
576 && self.stop_bits == stop_bits
577 && self.parity == parity
578 {
579 return Ok(());
580 }
581
582 let line = self.line_request_data(&cfg, baud)?;
583 self.ctrl_out(SET_LINE_REQUEST, 0, 0, line)?;
584 self.reset_device()?;
585
586 self.baud_rate = baud as i32;
587 self.data_bits = data_bits;
588 self.stop_bits = stop_bits;
589 self.parity = parity;
590 Ok(())
591 }
592
593 fn set_flow_control(&mut self, flow: FlowControl) -> Result<()> {
594 match flow {
595 FlowControl::None => {
596 if self.device_type == DeviceType::TypeHxn {
597 self.vendor_out(0x0a, 0xff, vec![])?;
598 } else {
599 self.vendor_out(0, 0, vec![])?;
600 }
601 }
602 FlowControl::RtsCts => {
603 if self.device_type == DeviceType::TypeHxn {
604 self.vendor_out(0x0a, 0xfa, vec![])?;
605 } else {
606 self.vendor_out(0, 0x61, vec![])?;
607 }
608 }
609 FlowControl::XonXoffInline => {
610 if self.device_type == DeviceType::TypeHxn {
611 self.vendor_out(0x0a, 0xee, vec![])?;
612 } else {
613 self.vendor_out(0, 0xc1, vec![])?;
614 }
615 }
616 _ => return Err(UsbSerialError::Unsupported("flow control".into())),
617 }
618 self.flow = flow;
619 Ok(())
620 }
621
622 fn set_dtr(&mut self, value: bool) -> Result<()> {
623 let new_lines = if value {
624 self.control_lines | CONTROL_DTR
625 } else {
626 self.control_lines & !CONTROL_DTR
627 };
628 self.set_control_lines(new_lines)
629 }
630
631 fn set_rts(&mut self, value: bool) -> Result<()> {
632 let new_lines = if value {
633 self.control_lines | CONTROL_RTS
634 } else {
635 self.control_lines & !CONTROL_RTS
636 };
637 self.set_control_lines(new_lines)
638 }
639
640 fn set_break(&mut self, enabled: bool) -> Result<()> {
641 let value = if enabled { 0xffffu16 } else { 0 };
642 self.ctrl_out(SEND_BREAK_REQUEST, value, 0, vec![])
643 }
644
645 fn purge(&mut self, kind: PurgeKind) -> Result<()> {
646 match kind {
647 PurgeKind::Rx => self.purge_hw_buffers(true, false),
648 PurgeKind::Tx => self.purge_hw_buffers(false, true),
649 PurgeKind::Both => self.purge_hw_buffers(true, true),
650 }
651 }
652
653 fn modem_status(&mut self) -> Result<ModemStatus> {
654 let status = self.get_status()?;
655 Ok(status_flag_to_modem(status))
656 }
657
658 fn bulk_in_mps(&self) -> u16 {
659 self.endpoints.as_ref().map(|e| e.data.mps).unwrap_or(64)
660 }
661
662 fn take_bulk_in(&mut self) -> Option<Box<dyn BulkIn>> {
663 let transport = self.transport.as_ref()?;
664 self.endpoints.as_mut()?.data.take_in(transport)
665 }
666
667 fn rx_filters(&self) -> Vec<Box<dyn crate::rx_filter::RxFilter>> {
668 if self.flow == FlowControl::XonXoffInline {
669 vec![Box::new(XonXoffRxFilter::new(true))]
670 } else {
671 vec![]
672 }
673 }
674}
675
676#[cfg(test)]
677mod tests {
678 use super::*;
679
680 #[test]
681 fn filter_standard_baud() {
682 let mut d = ProlificDriver::new(0);
683 d.device_type = DeviceType::TypeHx;
684 assert_eq!(d.filter_baud_rate(115200).unwrap(), 115200);
685 }
686
687 #[test]
688 fn filter_custom_baud_hx() {
689 let mut d = ProlificDriver::new(0);
690 d.device_type = DeviceType::TypeHx;
691 let encoded = d.filter_baud_rate(500_000).unwrap();
692 assert_ne!(encoded, 500_000);
693 assert!(encoded & (1 << 31) != 0);
694 }
695}