@@ -19,6 +19,7 @@ package serial
1919
2020import (
2121 "errors"
22+ "strings"
2223 "sync"
2324 "syscall"
2425 "time"
@@ -28,8 +29,9 @@ import (
2829)
2930
3031type windowsPort struct {
31- mu sync.Mutex
32- handle windows.Handle
32+ mu sync.Mutex
33+ handle windows.Handle
34+ hasTimeout bool
3335}
3436
3537func nativeGetPortsList () ([]string , error ) {
@@ -44,12 +46,22 @@ func nativeGetPortsList() ([]string, error) {
4446 }
4547 defer key .Close ()
4648
47- list , err := key .ReadValueNames (0 )
49+ names , err := key .ReadValueNames (0 )
4850 if err != nil {
4951 return nil , & PortError {code : ErrorEnumeratingPorts , causedBy : err }
5052 }
5153
52- return list , nil
54+ var values []string
55+ for _ , n := range names {
56+ v , _ , err := key .GetStringValue (n )
57+ if err != nil || v == "" {
58+ continue
59+ }
60+
61+ values = append (values , v )
62+ }
63+
64+ return values , nil
5365}
5466
5567func (port * windowsPort ) Close () error {
@@ -72,26 +84,33 @@ func (port *windowsPort) Read(p []byte) (int, error) {
7284 }
7385 defer windows .CloseHandle (ev .HEvent )
7486
75- err = windows .ReadFile (port .handle , p , & readed , ev )
76- if err == windows .ERROR_IO_PENDING {
77- err = windows .GetOverlappedResult (port .handle , ev , & readed , true )
78- }
79- switch err {
80- case nil :
81- // operation completed successfully
82- case windows .ERROR_OPERATION_ABORTED :
83- // port may have been closed
84- return int (readed ), & PortError {code : PortClosed , causedBy : err }
85- default :
86- // error happened
87- return int (readed ), err
88- }
89- if readed > 0 {
90- return int (readed ), nil
91- }
87+ for {
88+ err = windows .ReadFile (port .handle , p , & readed , ev )
89+ if err == windows .ERROR_IO_PENDING {
90+ err = windows .GetOverlappedResult (port .handle , ev , & readed , true )
91+ }
92+ switch err {
93+ case nil :
94+ // operation completed successfully
95+ case windows .ERROR_OPERATION_ABORTED :
96+ // port may have been closed
97+ return int (readed ), & PortError {code : PortClosed , causedBy : err }
98+ default :
99+ // error happened
100+ return int (readed ), err
101+ }
102+ if readed > 0 {
103+ return int (readed ), nil
104+ }
92105
93- // Timeout
94- return 0 , nil
106+ // Timeout
107+ port .mu .Lock ()
108+ hasTimeout := port .hasTimeout
109+ port .mu .Unlock ()
110+ if hasTimeout {
111+ return 0 , nil
112+ }
113+ }
95114}
96115
97116func (port * windowsPort ) Write (p []byte ) (int , error ) {
@@ -275,10 +294,19 @@ func (port *windowsPort) GetModemStatusBits() (*ModemStatusBits, error) {
275294}
276295
277296func (port * windowsPort ) SetReadTimeout (timeout time.Duration ) error {
297+ // This is a brutal hack to make the CH340 chipset work properly.
298+ // Normally this value should be 0xFFFFFFFE but, after a lot of
299+ // tinkering, I discovered that any value with the highest
300+ // bit set will make the CH340 driver behave like the timeout is 0,
301+ // in the best cases leading to a spinning loop...
302+ // (could this be a wrong signed vs unsigned conversion in the driver?)
303+ // https://github.com/arduino/serial-monitor/issues/112
304+ const MaxReadTotalTimeoutConstant = 0x7FFFFFFE
305+
278306 commTimeouts := & windows.CommTimeouts {
279307 ReadIntervalTimeout : 0xFFFFFFFF ,
280308 ReadTotalTimeoutMultiplier : 0xFFFFFFFF ,
281- ReadTotalTimeoutConstant : 0xFFFFFFFE ,
309+ ReadTotalTimeoutConstant : MaxReadTotalTimeoutConstant ,
282310 WriteTotalTimeoutConstant : 0 ,
283311 WriteTotalTimeoutMultiplier : 0 ,
284312 }
@@ -287,12 +315,20 @@ func (port *windowsPort) SetReadTimeout(timeout time.Duration) error {
287315 if ms > 0xFFFFFFFE || ms < 0 {
288316 return & PortError {code : InvalidTimeoutValue }
289317 }
318+
319+ if ms > MaxReadTotalTimeoutConstant {
320+ ms = MaxReadTotalTimeoutConstant
321+ }
322+
290323 commTimeouts .ReadTotalTimeoutConstant = uint32 (ms )
291324 }
292325
326+ port .mu .Lock ()
327+ defer port .mu .Unlock ()
293328 if err := windows .SetCommTimeouts (port .handle , commTimeouts ); err != nil {
294329 return & PortError {code : InvalidTimeoutValue , causedBy : err }
295330 }
331+ port .hasTimeout = (timeout != NoTimeout )
296332
297333 return nil
298334}
@@ -317,7 +353,9 @@ func createOverlappedEvent() (*windows.Overlapped, error) {
317353}
318354
319355func nativeOpen (portName string , mode * Mode ) (* windowsPort , error ) {
320- portName = "\\ \\ .\\ " + portName
356+ if ! strings .HasPrefix (portName , `\\.\` ) {
357+ portName = `\\.\` + portName
358+ }
321359 path , err := windows .UTF16PtrFromString (portName )
322360 if err != nil {
323361 return nil , err
0 commit comments