22
33use super :: { line_coding_bytes, Driver , EndpointPair , ModemStatus , WRITE_TIMEOUT_MS } ;
44use crate :: config:: { FlowControl , LineConfig , PurgeKind } ;
5- use crate :: error:: { Result , UsbSerialError } ;
5+ use crate :: error:: { ReadOutcome , Result , UsbSerialError } ;
66use crate :: reader:: SerialReader ;
7- use crate :: transport:: { ControlRequest , SharedTransport , USB_RECIP_INTERFACE , USB_TYPE_CLASS } ;
7+ use crate :: transport:: {
8+ BulkIn , ControlRequest , SharedTransport , USB_RECIP_INTERFACE , USB_TYPE_CLASS ,
9+ } ;
10+ use std:: sync:: atomic:: { AtomicBool , AtomicU16 , Ordering } ;
11+ use std:: sync:: { Arc , Mutex } ;
12+ use std:: thread:: { self , JoinHandle } ;
813
914const USB_SUBCLASS_ACM : u8 = 2 ;
1015const SET_LINE_CODING : u8 = 0x20 ;
1116const SET_CONTROL_LINE_STATE : u8 = 0x22 ;
1217const SEND_BREAK : u8 = 0x23 ;
18+ const NOTIFICATION_REQUEST_TYPE : u8 = 0xa1 ;
19+ const SERIAL_STATE_NOTIFICATION : u8 = 0x20 ;
20+ const SERIAL_STATE_NOTIFICATION_SIZE : usize = 10 ;
21+ const SERIAL_STATE_RX_CARRIER : u16 = 1 << 0 ;
22+ const SERIAL_STATE_TX_CARRIER : u16 = 1 << 1 ;
23+ const SERIAL_STATE_RING_SIGNAL : u16 = 1 << 3 ;
24+ const NOTIFICATION_READ_TIMEOUT_MS : u32 = 200 ;
25+
26+ struct CdcNotificationReader {
27+ state : Arc < AtomicU16 > ,
28+ error : Arc < Mutex < Option < String > > > ,
29+ stop : Arc < AtomicBool > ,
30+ thread : Option < JoinHandle < ( ) > > ,
31+ }
32+
33+ impl CdcNotificationReader {
34+ fn start ( mut endpoint : Box < dyn BulkIn > , max_packet_size : u16 ) -> Self {
35+ let state = Arc :: new ( AtomicU16 :: new ( 0 ) ) ;
36+ let error = Arc :: new ( Mutex :: new ( None ) ) ;
37+ let stop = Arc :: new ( AtomicBool :: new ( false ) ) ;
38+ let thread_state = Arc :: clone ( & state) ;
39+ let thread_error = Arc :: clone ( & error) ;
40+ let thread_stop = Arc :: clone ( & stop) ;
41+ let buffer_size = usize:: from ( max_packet_size) . max ( SERIAL_STATE_NOTIFICATION_SIZE ) ;
42+
43+ let thread = thread:: spawn ( move || {
44+ let mut buffer = vec ! [ 0 ; buffer_size] ;
45+ while !thread_stop. load ( Ordering :: Relaxed ) {
46+ match endpoint. read ( & mut buffer, NOTIFICATION_READ_TIMEOUT_MS ) {
47+ Ok ( ReadOutcome :: Data ( data) ) if !data. is_empty ( ) => {
48+ match parse_serial_state_notification ( & data) {
49+ Ok ( Some ( serial_state) ) => {
50+ thread_state. store ( serial_state, Ordering :: Relaxed ) ;
51+ }
52+ Ok ( None ) => { }
53+ Err ( error) => {
54+ * thread_error. lock ( ) . unwrap ( ) = Some ( error) ;
55+ break ;
56+ }
57+ }
58+ }
59+ Ok ( ReadOutcome :: TimedOut ) | Ok ( ReadOutcome :: Data ( _) ) => { }
60+ Ok ( ReadOutcome :: Cancelled ) => break ,
61+ Err ( error) => {
62+ if !thread_stop. load ( Ordering :: Relaxed ) {
63+ * thread_error. lock ( ) . unwrap ( ) = Some ( error. to_string ( ) ) ;
64+ }
65+ break ;
66+ }
67+ }
68+ }
69+ } ) ;
70+
71+ Self {
72+ state,
73+ error,
74+ stop,
75+ thread : Some ( thread) ,
76+ }
77+ }
78+
79+ fn modem_status ( & self ) -> Result < ModemStatus > {
80+ if let Some ( error) = self . error . lock ( ) . unwrap ( ) . take ( ) {
81+ return Err ( UsbSerialError :: Io ( error) ) ;
82+ }
83+
84+ let state = self . state . load ( Ordering :: Relaxed ) ;
85+ Ok ( ModemStatus {
86+ cts : false ,
87+ dsr : state & SERIAL_STATE_TX_CARRIER != 0 ,
88+ ri : state & SERIAL_STATE_RING_SIGNAL != 0 ,
89+ cd : state & SERIAL_STATE_RX_CARRIER != 0 ,
90+ } )
91+ }
92+
93+ fn stop ( & mut self ) {
94+ self . stop . store ( true , Ordering :: Relaxed ) ;
95+ if let Some ( thread) = self . thread . take ( ) {
96+ let _ = thread. join ( ) ;
97+ }
98+ }
99+ }
100+
101+ impl Drop for CdcNotificationReader {
102+ fn drop ( & mut self ) {
103+ self . stop ( ) ;
104+ }
105+ }
106+
107+ fn parse_serial_state_notification ( data : & [ u8 ] ) -> std:: result:: Result < Option < u16 > , String > {
108+ if data. len ( ) < 8 {
109+ return Err ( format ! (
110+ "invalid CDC notification, expected at least 8 bytes, got {}" ,
111+ data. len( )
112+ ) ) ;
113+ }
114+ if data[ 0 ] != NOTIFICATION_REQUEST_TYPE {
115+ return Err ( format ! (
116+ "invalid CDC notification request type 0x{:02x}" ,
117+ data[ 0 ]
118+ ) ) ;
119+ }
120+ if data[ 1 ] != SERIAL_STATE_NOTIFICATION {
121+ return Ok ( None ) ;
122+ }
123+
124+ let payload_size = usize:: from ( u16:: from_le_bytes ( [ data[ 6 ] , data[ 7 ] ] ) ) ;
125+ if payload_size != 2 || data. len ( ) != 8 + payload_size {
126+ return Err ( format ! (
127+ "invalid CDC serial-state notification, expected {SERIAL_STATE_NOTIFICATION_SIZE} bytes, got {}" ,
128+ data. len( )
129+ ) ) ;
130+ }
131+
132+ Ok ( Some ( u16:: from_le_bytes ( [ data[ 8 ] , data[ 9 ] ] ) ) )
133+ }
13134
14135pub struct CdcAcmDriver {
15136 port_index : usize ,
@@ -20,7 +141,10 @@ pub struct CdcAcmDriver {
20141 rts : bool ,
21142 endpoints : Option < EndpointPair > ,
22143 transport : Option < SharedTransport > ,
144+ control_claimed : bool ,
145+ data_claimed : bool ,
23146 reader : Option < SerialReader > ,
147+ notification_reader : Option < CdcNotificationReader > ,
24148}
25149
26150impl CdcAcmDriver {
@@ -34,7 +158,10 @@ impl CdcAcmDriver {
34158 rts : false ,
35159 endpoints : None ,
36160 transport : None ,
161+ control_claimed : false ,
162+ data_claimed : false ,
37163 reader : None ,
164+ notification_reader : None ,
38165 }
39166 }
40167
@@ -118,23 +245,54 @@ fn resolve_iad_pair(transport: &SharedTransport, port_index: usize) -> Option<(u
118245
119246impl Driver for CdcAcmDriver {
120247 fn open ( & mut self , transport : & SharedTransport ) -> Result < ( ) > {
121- self . transport = Some ( transport. clone ( ) ) ;
122248 self . resolve_interfaces ( transport) ?;
123- transport. claim_interface ( self . control_iface ) ?;
124- if self . data_iface != self . control_iface {
125- transport. claim_interface ( self . data_iface ) ?;
249+ self . transport = Some ( transport. clone ( ) ) ;
250+
251+ let result = ( || {
252+ transport. claim_interface ( self . control_iface ) ?;
253+ self . control_claimed = true ;
254+ if self . data_iface != self . control_iface {
255+ transport. claim_interface ( self . data_iface ) ?;
256+ self . data_claimed = true ;
257+ }
258+
259+ if let Some ( endpoint) = transport
260+ . endpoints ( self . control_iface )
261+ . into_iter ( )
262+ . find ( |endpoint| endpoint. is_interrupt_in ( ) )
263+ {
264+ let interrupt_in =
265+ transport. open_interrupt_in ( endpoint. address , endpoint. max_packet_size ) ?;
266+ self . notification_reader = Some ( CdcNotificationReader :: start (
267+ interrupt_in,
268+ endpoint. max_packet_size ,
269+ ) ) ;
270+ }
271+
272+ self . endpoints = Some ( EndpointPair :: open ( transport, self . data_iface ) ?) ;
273+ Ok ( ( ) )
274+ } ) ( ) ;
275+
276+ if result. is_err ( ) {
277+ let _ = self . close ( ) ;
126278 }
127- self . endpoints = Some ( EndpointPair :: open ( transport, self . data_iface ) ?) ;
128- Ok ( ( ) )
279+ result
129280 }
130281
131282 fn close ( & mut self ) -> Result < ( ) > {
132283 if let Some ( mut r) = self . reader . take ( ) {
133284 r. stop ( ) ;
134285 }
286+ if let Some ( mut r) = self . notification_reader . take ( ) {
287+ r. stop ( ) ;
288+ }
135289 if let Some ( t) = & self . transport {
136- let _ = t. release_interface ( self . data_iface ) ;
137- if self . control_iface != self . data_iface {
290+ if self . data_claimed {
291+ self . data_claimed = false ;
292+ let _ = t. release_interface ( self . data_iface ) ;
293+ }
294+ if self . control_claimed {
295+ self . control_claimed = false ;
138296 let _ = t. release_interface ( self . control_iface ) ;
139297 }
140298 }
@@ -187,7 +345,10 @@ impl Driver for CdcAcmDriver {
187345 }
188346
189347 fn modem_status ( & mut self ) -> Result < ModemStatus > {
190- Ok ( ModemStatus :: default ( ) )
348+ self . notification_reader
349+ . as_ref ( )
350+ . map ( CdcNotificationReader :: modem_status)
351+ . unwrap_or_else ( || Ok ( ModemStatus :: default ( ) ) )
191352 }
192353
193354 fn bulk_in_mps ( & self ) -> u16 {
0 commit comments