diff --git a/serial.go b/serial.go index a2f7333..0cf187f 100644 --- a/serial.go +++ b/serial.go @@ -163,6 +163,8 @@ const ( ErrorEnumeratingPorts // PortClosed the port has been closed while the operation is in progress PortClosed + // Timeout the read operation timed out + Timeout // FunctionNotImplemented the requested function is not implemented FunctionNotImplemented ) @@ -192,6 +194,8 @@ func (e PortError) EncodedErrorString() string { return "Could not enumerate serial ports" case PortClosed: return "Port has been closed" + case Timeout: + return "Read timeout" case FunctionNotImplemented: return "Function not implemented" default: diff --git a/serial_unix.go b/serial_unix.go index 304f775..ad5a8d9 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -119,7 +119,8 @@ func (port *unixPort) Read(p []byte) (int, error) { if port.readTimeout == NoTimeout { continue // 无超时设置,继续等待 } - return 0, nil // 返回0字节表示超时 + // 超时应返回 Timeout 错误,而不是 (0, nil) + return 0, &PortError{code: Timeout, causedBy: unix.ETIMEDOUT} } n, err := unix.Read(port.handle, p) @@ -352,7 +353,9 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { return nil, &PortError{code: InvalidSerialPort, causedBy: fmt.Errorf("error configuring port: %w", err)} } - unix.SetNonblock(h, false) + // Set non-blocking mode to ensure read() doesn't block after select() returns + // The select() call provides the timeout mechanism, not the read() call + unix.SetNonblock(h, true) port.acquireExclusiveAccess() @@ -513,7 +516,14 @@ func setRawMode(settings *unix.Termios) { settings.Oflag &^= unix.OPOST // Block reads until at least one char is available (no timeout) - settings.Cc[unix.VMIN] = 1 + // VMIN=1: Block until at least 1 byte is available + // VTIME=0: No inter-character timeout (not used with VMIN=1) + // + // NOTE: When readTimeout is set, the select() system call provides the timeout + // and VMIN/VTIME settings are not used for timeout purposes. + // However, VMIN=1 can cause issues with select() on some platforms/ + // terminal drivers. Using VMIN=0 allows select() to work reliably. + settings.Cc[unix.VMIN] = 0 settings.Cc[unix.VTIME] = 0 }