@@ -4,10 +4,16 @@ use std::time::Duration;
44use std:: { io, ptr} ;
55
66use winapi:: shared:: minwindef:: * ;
7+ use winapi:: shared:: ntdef:: NULL ;
8+ use winapi:: shared:: winerror:: { ERROR_IO_PENDING , ERROR_OPERATION_ABORTED } ;
79use winapi:: um:: commapi:: * ;
10+ use winapi:: um:: errhandlingapi:: GetLastError ;
811use winapi:: um:: fileapi:: * ;
912use winapi:: um:: handleapi:: * ;
13+ use winapi:: um:: ioapiset:: GetOverlappedResult ;
14+ use winapi:: um:: minwinbase:: OVERLAPPED ;
1015use winapi:: um:: processthreadsapi:: GetCurrentProcess ;
16+ use winapi:: um:: synchapi:: CreateEventW ;
1117use winapi:: um:: winbase:: * ;
1218use winapi:: um:: winnt:: {
1319 DUPLICATE_SAME_ACCESS , FILE_ATTRIBUTE_NORMAL , GENERIC_READ , GENERIC_WRITE , HANDLE ,
@@ -19,12 +25,56 @@ use crate::{
1925 SerialPortBuilder , StopBits ,
2026} ;
2127
28+ const fn duration_to_win_timeout ( time : Duration ) -> DWORD {
29+ time. as_secs ( )
30+ . saturating_mul ( 1000 )
31+ . saturating_add ( ( time. subsec_nanos ( ) as u64 ) . saturating_div ( 1_000_000 ) ) as DWORD
32+ }
33+
34+ struct OverlappedHandle ( pub HANDLE ) ;
35+
36+ impl OverlappedHandle {
37+ #[ inline]
38+ fn new ( ) -> io:: Result < Self > {
39+ match unsafe { CreateEventW ( ptr:: null_mut ( ) , TRUE , FALSE , ptr:: null_mut ( ) ) } {
40+ NULL => Err ( io:: Error :: last_os_error ( ) ) ,
41+ handle => Ok ( Self ( handle) ) ,
42+ }
43+ }
44+
45+ #[ inline]
46+ fn close ( self ) {
47+ //drop
48+ }
49+
50+ #[ inline]
51+ fn create_overlapped ( & self ) -> OVERLAPPED {
52+ OVERLAPPED {
53+ Internal : 0 ,
54+ InternalHigh : 0 ,
55+ u : unsafe { MaybeUninit :: zeroed ( ) . assume_init ( ) } ,
56+ hEvent : self . 0 ,
57+ }
58+ }
59+ }
60+
61+ impl Drop for OverlappedHandle {
62+ #[ inline( always) ]
63+ fn drop ( & mut self ) {
64+ unsafe {
65+ CloseHandle ( self . 0 ) ;
66+ }
67+ }
68+ }
69+
2270/// A serial port implementation for Windows COM ports
2371///
2472/// The port will be closed when the value is dropped. However, this struct
2573/// should not be instantiated directly by using `COMPort::open()`, instead use
2674/// the cross-platform `serialport::open()` or
2775/// `serialport::open_with_settings()`.
76+ ///
77+ /// Port is created using `CreateFileW` syscall with following set of flags: `FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED`
2878#[ derive( Debug ) ]
2979pub struct COMPort {
3080 handle : HANDLE ,
@@ -63,7 +113,7 @@ impl COMPort {
63113 0 ,
64114 ptr:: null_mut ( ) ,
65115 OPEN_EXISTING ,
66- FILE_ATTRIBUTE_NORMAL ,
116+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED ,
67117 0 as HANDLE ,
68118 )
69119 } ;
@@ -105,27 +155,28 @@ impl COMPort {
105155 ///
106156 /// This function returns an error if the serial port couldn't be cloned.
107157 pub fn try_clone_native ( & self ) -> Result < COMPort > {
108- let process_handle: HANDLE = unsafe { GetCurrentProcess ( ) } ;
109- let mut cloned_handle: HANDLE = INVALID_HANDLE_VALUE ;
110- unsafe {
158+ // duplicate communications device handle
159+ let mut duplicate_handle = INVALID_HANDLE_VALUE ;
160+ let process = unsafe { GetCurrentProcess ( ) } ;
161+ let res = unsafe {
111162 DuplicateHandle (
112- process_handle ,
163+ process ,
113164 self . handle ,
114- process_handle ,
115- & mut cloned_handle ,
165+ process ,
166+ & mut duplicate_handle ,
116167 0 ,
117- TRUE ,
168+ FALSE ,
118169 DUPLICATE_SAME_ACCESS ,
119- ) ;
120- if cloned_handle != INVALID_HANDLE_VALUE {
121- Ok ( COMPort {
122- handle : cloned_handle ,
123- port_name : self . port_name . clone ( ) ,
124- timeout : self . timeout ,
125- } )
126- } else {
127- Err ( super :: error :: last_os_error ( ) )
128- }
170+ )
171+ } ;
172+
173+ match res {
174+ 0 => Err ( super :: error :: last_os_error ( ) ) ,
175+ _ => Ok ( COMPort {
176+ handle : duplicate_handle ,
177+ port_name : self . port_name . clone ( ) ,
178+ timeout : self . timeout ,
179+ } ) ,
129180 }
130181 }
131182
@@ -154,6 +205,33 @@ impl COMPort {
154205 port_name : None ,
155206 }
156207 }
208+
209+ ///Sets COM port timeouts.
210+ ///
211+ ///Comparing to `SerialPort::set_timeout` which only sets `read` timeout, this function allows
212+ ///to specify all available timeouts.
213+ ///
214+ ///- `data` - This timeout specifies how long to wait for next byte, since arrival of at least
215+ ///one byte. Once timeout expires, read returns with available data.
216+ ///`SerialPort::set_timeout` uses 0 which means no timeout.
217+ ///- `read` - Specifies overall timeout for `read` as `SerialPort::set_timeout`
218+ ///- `write` - Specifies overall timeout for `write` operations.
219+ pub fn set_timeouts ( & mut self , data : Duration , read : Duration , write : Duration ) -> Result < ( ) > {
220+ let mut timeouts = COMMTIMEOUTS {
221+ ReadIntervalTimeout : duration_to_win_timeout ( data) ,
222+ ReadTotalTimeoutMultiplier : 0 ,
223+ ReadTotalTimeoutConstant : duration_to_win_timeout ( read) ,
224+ WriteTotalTimeoutMultiplier : 0 ,
225+ WriteTotalTimeoutConstant : duration_to_win_timeout ( write) ,
226+ } ;
227+
228+ if unsafe { SetCommTimeouts ( self . handle , & mut timeouts) } == 0 {
229+ return Err ( super :: error:: last_os_error ( ) ) ;
230+ }
231+
232+ self . timeout = read;
233+ Ok ( ( ) )
234+ }
157235}
158236
159237impl Drop for COMPort {
@@ -178,48 +256,88 @@ impl FromRawHandle for COMPort {
178256
179257impl io:: Read for COMPort {
180258 fn read ( & mut self , buf : & mut [ u8 ] ) -> io:: Result < usize > {
181- let mut len: DWORD = 0 ;
259+ let mut read_size = buf. len ( ) ;
260+
261+ if self . timeout . as_secs ( ) == 0 && self . timeout . subsec_nanos ( ) == 0 {
262+ //If zero timeout then make sure we can read, before proceeding
263+ //Note that zero timeout will make read operation to wait until at least
264+ //1 byte becomes available.
265+ let bytes_to_read = self . bytes_to_read ( ) ? as usize ;
266+ if bytes_to_read < read_size {
267+ read_size = bytes_to_read;
268+ }
269+ if read_size == 0 {
270+ return Ok ( 0 ) ;
271+ }
272+ }
182273
183- match unsafe {
274+ let evt_handle = OverlappedHandle :: new ( ) ?;
275+ let mut overlapped = evt_handle. create_overlapped ( ) ;
276+ let mut len: DWORD = 0 ;
277+ let read_result = unsafe {
184278 ReadFile (
185279 self . handle ,
186280 buf. as_mut_ptr ( ) as LPVOID ,
187- buf . len ( ) as DWORD ,
281+ read_size as DWORD ,
188282 & mut len,
189- ptr :: null_mut ( ) ,
283+ & mut overlapped ,
190284 )
191- } {
192- 0 => Err ( io:: Error :: last_os_error ( ) ) ,
193- _ => {
194- if len != 0 {
195- Ok ( len as usize )
196- } else {
197- Err ( io:: Error :: new (
198- io:: ErrorKind :: TimedOut ,
199- "Operation timed out" ,
200- ) )
201- }
202- }
285+ } ;
286+ let last_error = unsafe { GetLastError ( ) } ;
287+ if read_result == 0
288+ && last_error != ERROR_IO_PENDING
289+ && last_error != ERROR_OPERATION_ABORTED
290+ {
291+ return Err ( io:: Error :: last_os_error ( ) ) ;
292+ }
293+ let overlapped_result =
294+ unsafe { GetOverlappedResult ( self . handle , & mut overlapped, & mut len, TRUE ) } ;
295+ evt_handle. close ( ) ;
296+ let last_error = unsafe { GetLastError ( ) } ;
297+ if overlapped_result == 0 && last_error != ERROR_OPERATION_ABORTED {
298+ return Err ( io:: Error :: last_os_error ( ) ) ;
299+ }
300+ if len != 0 {
301+ Ok ( len as usize )
302+ } else {
303+ Err ( io:: Error :: new (
304+ io:: ErrorKind :: TimedOut ,
305+ "Operation timed out" ,
306+ ) )
203307 }
204308 }
205309}
206310
207311impl io:: Write for COMPort {
208312 fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
313+ let evt_handle = OverlappedHandle :: new ( ) ?;
314+ let mut overlapped = evt_handle. create_overlapped ( ) ;
209315 let mut len: DWORD = 0 ;
210-
211- match unsafe {
316+ let write_result = unsafe {
212317 WriteFile (
213318 self . handle ,
214319 buf. as_ptr ( ) as LPVOID ,
215320 buf. len ( ) as DWORD ,
216321 & mut len,
217- ptr :: null_mut ( ) ,
322+ & mut overlapped ,
218323 )
219- } {
220- 0 => Err ( io:: Error :: last_os_error ( ) ) ,
221- _ => Ok ( len as usize ) ,
324+ } ;
325+ let last_error = unsafe { GetLastError ( ) } ;
326+ if write_result == 0
327+ && last_error != ERROR_IO_PENDING
328+ && last_error != ERROR_OPERATION_ABORTED
329+ {
330+ return Err ( io:: Error :: last_os_error ( ) ) ;
222331 }
332+ let overlapped_result =
333+ unsafe { GetOverlappedResult ( self . handle , & mut overlapped, & mut len, TRUE ) } ;
334+ evt_handle. close ( ) ;
335+
336+ let last_error = unsafe { GetLastError ( ) } ;
337+ if overlapped_result == 0 && last_error != ERROR_OPERATION_ABORTED {
338+ return Err ( io:: Error :: last_os_error ( ) ) ;
339+ }
340+ Ok ( len as usize )
223341 }
224342
225343 fn flush ( & mut self ) -> io:: Result < ( ) > {
@@ -231,31 +349,19 @@ impl io::Write for COMPort {
231349}
232350
233351impl SerialPort for COMPort {
352+ #[ inline]
234353 fn name ( & self ) -> Option < String > {
235354 self . port_name . clone ( )
236355 }
237356
357+ #[ inline]
238358 fn timeout ( & self ) -> Duration {
239359 self . timeout
240360 }
241361
362+ #[ inline]
242363 fn set_timeout ( & mut self , timeout : Duration ) -> Result < ( ) > {
243- let milliseconds = timeout. as_secs ( ) * 1000 + timeout. subsec_nanos ( ) as u64 / 1_000_000 ;
244-
245- let mut timeouts = COMMTIMEOUTS {
246- ReadIntervalTimeout : 0 ,
247- ReadTotalTimeoutMultiplier : 0 ,
248- ReadTotalTimeoutConstant : milliseconds as DWORD ,
249- WriteTotalTimeoutMultiplier : 0 ,
250- WriteTotalTimeoutConstant : 0 ,
251- } ;
252-
253- if unsafe { SetCommTimeouts ( self . handle , & mut timeouts) } == 0 {
254- return Err ( super :: error:: last_os_error ( ) ) ;
255- }
256-
257- self . timeout = timeout;
258- Ok ( ( ) )
364+ self . set_timeouts ( Duration :: from_secs ( 0 ) , timeout, Duration :: from_secs ( 0 ) )
259365 }
260366
261367 fn write_request_to_send ( & mut self , level : bool ) -> Result < ( ) > {
0 commit comments