From 485b2f86a8bd1c26586702b6678026cde7b5d1ff Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 1 Jan 2016 23:23:20 +0100 Subject: [PATCH 001/181] Fixed a bunch of golint warnings (this needs an API change) --- doc.go | 6 +-- example_getportlist_test.go | 1 - example_serialport_test.go | 5 +-- example_test.go | 5 +-- serial.go | 80 +++++++++++++++++++++++-------------- serial_darwin.go | 8 ++-- serial_linux.go | 8 ++-- serial_unix.go | 54 ++++++++++++------------- serial_windows.go | 25 ++++++------ 9 files changed, 104 insertions(+), 88 deletions(-) diff --git a/doc.go b/doc.go index fff8acf..ef85cae 100644 --- a/doc.go +++ b/doc.go @@ -5,7 +5,7 @@ // /* -A cross-platform serial library for the go language. +Package serial is a cross-platform serial library for the go language. The canonical import for this library is go.bug.st/serial so the import line is the following: @@ -43,9 +43,9 @@ The following snippets shows how to declare a configuration for 57600_E71: mode := &serial.Mode{ BaudRate: 57600, - Parity: serial.PARITY_EVEN, + Parity: serial.EvenParity, DataBits: 7, - StopBits: serial.STOPBITS_ONE, + StopBits: serial.OneStopBit, } The configuration can be changed at any time with the SetMode function: diff --git a/example_getportlist_test.go b/example_getportlist_test.go index be34e81..79ff243 100644 --- a/example_getportlist_test.go +++ b/example_getportlist_test.go @@ -23,4 +23,3 @@ func ExampleGetPortsList() { } } } - diff --git a/example_serialport_test.go b/example_serialport_test.go index 2ee936c..c470ac9 100644 --- a/example_serialport_test.go +++ b/example_serialport_test.go @@ -17,13 +17,12 @@ func ExampleSerialPort_SetMode() { } mode := &serial.Mode{ BaudRate: 9600, - Parity: serial.PARITY_NONE, + Parity: serial.NoParity, DataBits: 8, - StopBits: serial.STOPBITS_ONE, + StopBits: serial.OneStopBit, } if err := port.SetMode(mode); err != nil { log.Fatal(err) } fmt.Println("Port set to 9600 N81") } - diff --git a/example_test.go b/example_test.go index c087746..c408e1e 100644 --- a/example_test.go +++ b/example_test.go @@ -31,9 +31,9 @@ func Example_sendAndReceive() { // Open the first serial port detected at 9600bps N81 mode := &serial.Mode{ BaudRate: 9600, - Parity: serial.PARITY_NONE, + Parity: serial.NoParity, DataBits: 8, - StopBits: serial.STOPBITS_ONE, + StopBits: serial.OneStopBit, } port, err := serial.OpenPort(ports[0], mode) if err != nil { @@ -63,4 +63,3 @@ func Example_sendAndReceive() { fmt.Printf("%v", string(buff[:n])) } } - diff --git a/serial.go b/serial.go index 9cb1052..09a7e21 100644 --- a/serial.go +++ b/serial.go @@ -6,7 +6,7 @@ package serial // import "go.bug.st/serial" -// This structure describes a serial port configuration. +// Mode describes a serial port configuration. type Mode struct { BaudRate int // The serial port bitrate (aka Baudrate) DataBits int // Size of the character (must be 5, 6, 7 or 8) @@ -14,62 +14,82 @@ type Mode struct { StopBits StopBits // Stop bits (see StopBits type for more info) } +// Parity describes a serial port parity setting type Parity int const ( - PARITY_NONE Parity = iota // No parity (default) - PARITY_ODD // Odd parity - PARITY_EVEN // Even parity - PARITY_MARK // Mark parity (always 1) - PARITY_SPACE // Space parity (always 0) + // NoParity disable parity control (default) + NoParity Parity = iota + // OddParity enable odd-parity check + OddParity + // EvenParity enable even-parity check + EvenParity + // MarkParity enable mark-parity (always 1) check + MarkParity + // SpaceParity enable space-parity (always 0) check + SpaceParity ) +// StopBits describe a serial port stop bits setting type StopBits int const ( - STOPBITS_ONE StopBits = iota // 1 Stop bit - STOPBITS_ONEPOINTFIVE // 1.5 Stop bits - STOPBITS_TWO // 2 Stop bits + // OneStopBit sets 1 stop bit (default) + OneStopBit StopBits = iota + // OnePointFiveStopBits sets 1.5 stop bits + OnePointFiveStopBits + // TwoStopBits sets 2 stop bits + TwoStopBits ) -// Platform independent error type for serial ports -type SerialPortError struct { +// PortError is a platform independent error type for serial ports +type PortError struct { err string - code int + code PortErrorCode } +// PortErrorCode is a code to easily identify the type of error +type PortErrorCode int + const ( - ERROR_PORT_BUSY = iota - ERROR_PORT_NOT_FOUND - ERROR_INVALID_SERIAL_PORT - ERROR_PERMISSION_DENIED - ERROR_INVALID_PORT_SPEED - ERROR_INVALID_PORT_DATA_BITS - ERROR_ENUMERATING_PORTS - ERROR_OTHER + // PortBusy the serial port is already in used by another process + PortBusy PortErrorCode = iota + // PortNotFound the requested port doesn't exist + PortNotFound + // InvalidSerialPort the requested port is not a serial port + InvalidSerialPort + // PermissionDenied the user doesn't have enough priviledges + PermissionDenied + // InvalidSpeed the requested speed is not valid or not supported + InvalidSpeed + // InvalidDataBits the number of data bits is not valid or not supported + InvalidDataBits + // ErrorEnumeratingPorts an error occurred while listing serial port + ErrorEnumeratingPorts ) -func (e SerialPortError) Error() string { +// Error returns a string explaining the error occurred +func (e PortError) Error() string { switch e.code { - case ERROR_PORT_BUSY: + case PortBusy: return "Serial port busy" - case ERROR_PORT_NOT_FOUND: + case PortNotFound: return "Serial port not found" - case ERROR_INVALID_SERIAL_PORT: + case InvalidSerialPort: return "Invalid serial port" - case ERROR_PERMISSION_DENIED: + case PermissionDenied: return "Permission denied" - case ERROR_INVALID_PORT_SPEED: + case InvalidSpeed: return "Invalid port speed" - case ERROR_INVALID_PORT_DATA_BITS: + case InvalidDataBits: return "Invalid port data bits" - case ERROR_ENUMERATING_PORTS: + case ErrorEnumeratingPorts: return "Could not enumerate serial ports" } return e.err } -func (e SerialPortError) Code() int { +// Code returns an identifier for the kind of error occurred +func (e PortError) Code() PortErrorCode { return e.code } - diff --git a/serial_darwin.go b/serial_darwin.go index 52742c0..7a1da90 100644 --- a/serial_darwin.go +++ b/serial_darwin.go @@ -43,12 +43,12 @@ var databitsMap = map[int]int{ 8: syscall.CS8, } -const tc_CMSPAR int = 0 // may be CMSPAR or PAREXT -const tc_IUCLC int = 0 +const tcCMSPAR int = 0 // may be CMSPAR or PAREXT +const tcIUCLC int = 0 // syscall wrappers //sys ioctl(fd int, req uint64, data uintptr) (err error) -const ioctl_tcgetattr = syscall.TIOCGETA -const ioctl_tcsetattr = syscall.TIOCSETA +const ioctlTcgetattr = syscall.TIOCGETA +const ioctlTcsetattr = syscall.TIOCSETA diff --git a/serial_linux.go b/serial_linux.go index 4376806..0369a82 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -55,8 +55,8 @@ var databitsMap = map[int]int{ 8: syscall.CS8, } -const tc_CMSPAR int = 0 // may be CMSPAR or PAREXT -const tc_IUCLC = syscall.IUCLC +const tcCMSPAR int = 0 // may be CMSPAR or PAREXT +const tcIUCLC = syscall.IUCLC func termiosMask(data int) uint32 { return uint32(data) @@ -66,5 +66,5 @@ func termiosMask(data int) uint32 { //sys ioctl(fd int, req uint64, data uintptr) (err error) -const ioctl_tcgetattr = syscall.TCGETS -const ioctl_tcsetattr = syscall.TCSETS +const ioctlTcgetattr = syscall.TCGETS +const ioctlTcsetattr = syscall.TCSETS diff --git a/serial_unix.go b/serial_unix.go index a58347c..29c0b10 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -40,8 +40,7 @@ func (port *SerialPort) Write(p []byte) (n int, err error) { return syscall.Write(port.handle, p) } -// Set all parameters of the serial port. See the Mode structure for more -// info. +// SetMode sets all parameters of the serial port func (port *SerialPort) SetMode(mode *Mode) error { settings, err := port.getTermSettings() if err != nil { @@ -62,15 +61,15 @@ func (port *SerialPort) SetMode(mode *Mode) error { return port.setTermSettings(settings) } -// Open the serial port using the specified modes +// OpenPort opens the serial port using the specified modes func OpenPort(portName string, mode *Mode) (*SerialPort, error) { h, err := syscall.Open(portName, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NDELAY, 0) if err != nil { switch err { case syscall.EBUSY: - return nil, &SerialPortError{code: ERROR_PORT_BUSY} + return nil, &PortError{code: PortBusy} case syscall.EACCES: - return nil, &SerialPortError{code: ERROR_PERMISSION_DENIED} + return nil, &PortError{code: PermissionDenied} } return nil, err } @@ -81,19 +80,19 @@ func OpenPort(portName string, mode *Mode) (*SerialPort, error) { // Setup serial port if port.SetMode(mode) != nil { port.Close() - return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT} + return nil, &PortError{code: InvalidSerialPort} } // Set raw mode settings, err := port.getTermSettings() if err != nil { port.Close() - return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT} + return nil, &PortError{code: InvalidSerialPort} } setRawMode(settings) if port.setTermSettings(settings) != nil { port.Close() - return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT} + return nil, &PortError{code: InvalidSerialPort} } syscall.SetNonblock(h, false) @@ -103,6 +102,7 @@ func OpenPort(portName string, mode *Mode) (*SerialPort, error) { return port, nil } +// GetPortsList retrieve the list of available serial ports func GetPortsList() ([]string, error) { files, err := ioutil.ReadDir(devFolder) if err != nil { @@ -131,8 +131,8 @@ func GetPortsList() ([]string, error) { if strings.HasPrefix(f.Name(), "ttyS") { port, err := OpenPort(portName, &Mode{}) if err != nil { - serr, ok := err.(*SerialPortError) - if ok && serr.Code() == ERROR_INVALID_SERIAL_PORT { + serr, ok := err.(*PortError) + if ok && serr.Code() == InvalidSerialPort { continue } } else { @@ -152,7 +152,7 @@ func GetPortsList() ([]string, error) { func setTermSettingsBaudrate(speed int, settings *syscall.Termios) error { baudrate, ok := baudrateMap[speed] if !ok { - return &SerialPortError{code: ERROR_INVALID_PORT_SPEED} + return &PortError{code: InvalidSpeed} } // revert old baudrate BAUDMASK := 0 @@ -169,23 +169,23 @@ func setTermSettingsBaudrate(speed int, settings *syscall.Termios) error { func setTermSettingsParity(parity Parity, settings *syscall.Termios) error { switch parity { - case PARITY_NONE: - settings.Cflag &= ^termiosMask(syscall.PARENB | syscall.PARODD | tc_CMSPAR) + case NoParity: + settings.Cflag &= ^termiosMask(syscall.PARENB | syscall.PARODD | tcCMSPAR) settings.Iflag &= ^termiosMask(syscall.INPCK) - case PARITY_ODD: + case OddParity: settings.Cflag |= termiosMask(syscall.PARENB | syscall.PARODD) - settings.Cflag &= ^termiosMask(tc_CMSPAR) + settings.Cflag &= ^termiosMask(tcCMSPAR) settings.Iflag |= termiosMask(syscall.INPCK) - case PARITY_EVEN: - settings.Cflag &= ^termiosMask(syscall.PARODD | tc_CMSPAR) + case EvenParity: + settings.Cflag &= ^termiosMask(syscall.PARODD | tcCMSPAR) settings.Cflag |= termiosMask(syscall.PARENB) settings.Iflag |= termiosMask(syscall.INPCK) - case PARITY_MARK: - settings.Cflag |= termiosMask(syscall.PARENB | syscall.PARODD | tc_CMSPAR) + case MarkParity: + settings.Cflag |= termiosMask(syscall.PARENB | syscall.PARODD | tcCMSPAR) settings.Iflag |= termiosMask(syscall.INPCK) - case PARITY_SPACE: + case SpaceParity: settings.Cflag &= ^termiosMask(syscall.PARODD) - settings.Cflag |= termiosMask(syscall.PARENB | tc_CMSPAR) + settings.Cflag |= termiosMask(syscall.PARENB | tcCMSPAR) settings.Iflag |= termiosMask(syscall.INPCK) } return nil @@ -194,7 +194,7 @@ func setTermSettingsParity(parity Parity, settings *syscall.Termios) error { func setTermSettingsDataBits(bits int, settings *syscall.Termios) error { databits, ok := databitsMap[bits] if !ok { - return &SerialPortError{code: ERROR_INVALID_PORT_DATA_BITS} + return &PortError{code: InvalidDataBits} } settings.Cflag &= ^termiosMask(syscall.CSIZE) settings.Cflag |= termiosMask(databits) @@ -203,9 +203,9 @@ func setTermSettingsDataBits(bits int, settings *syscall.Termios) error { func setTermSettingsStopBits(bits StopBits, settings *syscall.Termios) error { switch bits { - case STOPBITS_ONE: + case OneStopBit: settings.Cflag &= ^termiosMask(syscall.CSTOPB) - case STOPBITS_ONEPOINTFIVE, STOPBITS_TWO: + case OnePointFiveStopBits, TwoStopBits: settings.Cflag |= termiosMask(syscall.CSTOPB) } return nil @@ -220,7 +220,7 @@ func setRawMode(settings *syscall.Termios) { syscall.ECHONL | syscall.ECHOCTL | syscall.ECHOPRT | syscall.ECHOKE | syscall.ISIG | syscall.IEXTEN) settings.Iflag &= ^termiosMask(syscall.IXON | syscall.IXOFF | syscall.IXANY | syscall.INPCK | syscall.IGNPAR | syscall.PARMRK | syscall.ISTRIP | syscall.IGNBRK | syscall.BRKINT | syscall.INLCR | - syscall.IGNCR | syscall.ICRNL | tc_IUCLC) + syscall.IGNCR | syscall.ICRNL | tcIUCLC) settings.Oflag &= ^termiosMask(syscall.OPOST) // Block reads until at least one char is available (no timeout) @@ -232,12 +232,12 @@ func setRawMode(settings *syscall.Termios) { func (port *SerialPort) getTermSettings() (*syscall.Termios, error) { settings := &syscall.Termios{} - err := ioctl(port.handle, ioctl_tcgetattr, uintptr(unsafe.Pointer(settings))) + err := ioctl(port.handle, ioctlTcgetattr, uintptr(unsafe.Pointer(settings))) return settings, err } func (port *SerialPort) setTermSettings(settings *syscall.Termios) error { - return ioctl(port.handle, ioctl_tcsetattr, uintptr(unsafe.Pointer(settings))) + return ioctl(port.handle, ioctlTcsetattr, uintptr(unsafe.Pointer(settings))) } func (port *SerialPort) acquireExclusiveAccess() error { diff --git a/serial_windows.go b/serial_windows.go index 999c309..01d4ca4 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -28,18 +28,18 @@ type SerialPort struct { func GetPortsList() ([]string, error) { subKey, err := syscall.UTF16PtrFromString("HARDWARE\\DEVICEMAP\\SERIALCOMM\\") if err != nil { - return nil, &SerialPortError{code: ERROR_ENUMERATING_PORTS} + return nil, &PortError{code: ErrorEnumeratingPorts} } var h syscall.Handle if syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE, subKey, 0, syscall.KEY_READ, &h) != nil { - return nil, &SerialPortError{code: ERROR_ENUMERATING_PORTS} + return nil, &PortError{code: ErrorEnumeratingPorts} } defer syscall.RegCloseKey(h) var valuesCount uint32 if syscall.RegQueryInfoKey(h, nil, nil, nil, nil, nil, nil, &valuesCount, nil, nil, nil, nil) != nil { - return nil, &SerialPortError{code: ERROR_ENUMERATING_PORTS} + return nil, &PortError{code: ErrorEnumeratingPorts} } list := make([]string, valuesCount) @@ -49,7 +49,7 @@ func GetPortsList() ([]string, error) { var name [1024]uint16 nameSize := uint32(len(name)) if RegEnumValue(h, uint32(i), &name[0], &nameSize, nil, nil, &data[0], &dataSize) != nil { - return nil, &SerialPortError{code: ERROR_ENUMERATING_PORTS} + return nil, &PortError{code: ErrorEnumeratingPorts} } list[i] = syscall.UTF16ToString(data[:]) } @@ -175,7 +175,7 @@ func (port *SerialPort) SetMode(mode *Mode) error { params := DCB{} if GetCommState(port.handle, ¶ms) != nil { port.Close() - return &SerialPortError{code: ERROR_INVALID_SERIAL_PORT} + return &PortError{code: InvalidSerialPort} } if mode.BaudRate == 0 { params.BaudRate = 9600 // Default to 9600 @@ -191,7 +191,7 @@ func (port *SerialPort) SetMode(mode *Mode) error { params.Parity = byte(mode.Parity) if SetCommState(port.handle, ¶ms) != nil { port.Close() - return &SerialPortError{code: ERROR_INVALID_SERIAL_PORT} + return &PortError{code: InvalidSerialPort} } return nil } @@ -212,9 +212,9 @@ func OpenPort(portName string, mode *Mode) (*SerialPort, error) { if err != nil { switch err { case syscall.ERROR_ACCESS_DENIED: - return nil, &SerialPortError{code: ERROR_PORT_BUSY} + return nil, &PortError{code: PortBusy} case syscall.ERROR_FILE_NOT_FOUND: - return nil, &SerialPortError{code: ERROR_PORT_NOT_FOUND} + return nil, &PortError{code: PortNotFound} } return nil, err } @@ -226,13 +226,13 @@ func OpenPort(portName string, mode *Mode) (*SerialPort, error) { // Set port parameters if port.SetMode(mode) != nil { port.Close() - return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT} + return nil, &PortError{code: InvalidSerialPort} } params := &DCB{} if GetCommState(port.handle, params) != nil { port.Close() - return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT} + return nil, &PortError{code: InvalidSerialPort} } params.Flags |= DCB_RTS_CONTROL_ENABLE | DCB_DTR_CONTROL_ENABLE params.Flags &= ^uint32(DCB_OUT_X_CTS_FLOW) @@ -249,7 +249,7 @@ func OpenPort(portName string, mode *Mode) (*SerialPort, error) { params.XoffChar = 19 // C3 if SetCommState(port.handle, params) != nil { port.Close() - return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT} + return nil, &PortError{code: InvalidSerialPort} } // Set timeouts to 1 second @@ -262,9 +262,8 @@ func OpenPort(portName string, mode *Mode) (*SerialPort, error) { } if SetCommTimeouts(port.handle, timeouts) != nil { port.Close() - return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT} + return nil, &PortError{code: InvalidSerialPort} } return port, nil } - From 88e647775e738d7a24f7643e35ab5cbf0b312bc9 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 1 Jan 2016 23:32:28 +0100 Subject: [PATCH 002/181] Rename serial.SerialPort to serial.Port another lint suggestion. --- serial_unix.go | 24 ++++++++++++------------ serial_windows.go | 15 +++++++-------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/serial_unix.go b/serial_unix.go index 29c0b10..f308dc0 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -14,13 +14,13 @@ import "strings" import "syscall" import "unsafe" -// Opaque type that implements SerialPort interface for linux -type SerialPort struct { +// Port is the handler for a serial Port +type Port struct { handle int } // Close the serial port -func (port *SerialPort) Close() error { +func (port *Port) Close() error { port.releaseExclusiveAccess() return syscall.Close(port.handle) } @@ -30,18 +30,18 @@ func (port *SerialPort) Close() error { // // The Read function blocks until (at least) one byte is received from // the serial port or an error occurs. -func (port *SerialPort) Read(p []byte) (n int, err error) { +func (port *Port) Read(p []byte) (n int, err error) { return syscall.Read(port.handle, p) } // Send the content of the data byte array to the serial port. // Returns the number of bytes written. -func (port *SerialPort) Write(p []byte) (n int, err error) { +func (port *Port) Write(p []byte) (n int, err error) { return syscall.Write(port.handle, p) } // SetMode sets all parameters of the serial port -func (port *SerialPort) SetMode(mode *Mode) error { +func (port *Port) SetMode(mode *Mode) error { settings, err := port.getTermSettings() if err != nil { return err @@ -62,7 +62,7 @@ func (port *SerialPort) SetMode(mode *Mode) error { } // OpenPort opens the serial port using the specified modes -func OpenPort(portName string, mode *Mode) (*SerialPort, error) { +func OpenPort(portName string, mode *Mode) (*Port, error) { h, err := syscall.Open(portName, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NDELAY, 0) if err != nil { switch err { @@ -73,7 +73,7 @@ func OpenPort(portName string, mode *Mode) (*SerialPort, error) { } return nil, err } - port := &SerialPort{ + port := &Port{ handle: h, } @@ -230,20 +230,20 @@ func setRawMode(settings *syscall.Termios) { // native syscall wrapper functions -func (port *SerialPort) getTermSettings() (*syscall.Termios, error) { +func (port *Port) getTermSettings() (*syscall.Termios, error) { settings := &syscall.Termios{} err := ioctl(port.handle, ioctlTcgetattr, uintptr(unsafe.Pointer(settings))) return settings, err } -func (port *SerialPort) setTermSettings(settings *syscall.Termios) error { +func (port *Port) setTermSettings(settings *syscall.Termios) error { return ioctl(port.handle, ioctlTcsetattr, uintptr(unsafe.Pointer(settings))) } -func (port *SerialPort) acquireExclusiveAccess() error { +func (port *Port) acquireExclusiveAccess() error { return ioctl(port.handle, syscall.TIOCEXCL, 0) } -func (port *SerialPort) releaseExclusiveAccess() error { +func (port *Port) releaseExclusiveAccess() error { return ioctl(port.handle, syscall.TIOCNXCL, 0) } diff --git a/serial_windows.go b/serial_windows.go index 01d4ca4..b2cff41 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -18,8 +18,7 @@ package serial // import "go.bug.st/serial" import "syscall" -// opaque type that implements SerialPort interface for Windows -type SerialPort struct { +type Port struct { handle syscall.Handle } @@ -56,11 +55,11 @@ func GetPortsList() ([]string, error) { return list, nil } -func (port *SerialPort) Close() error { +func (port *Port) Close() error { return syscall.CloseHandle(port.handle) } -func (port *SerialPort) Read(p []byte) (int, error) { +func (port *Port) Read(p []byte) (int, error) { var readed uint32 params := &DCB{} for { @@ -83,7 +82,7 @@ func (port *SerialPort) Read(p []byte) (int, error) { } } -func (port *SerialPort) Write(p []byte) (int, error) { +func (port *Port) Write(p []byte) (int, error) { var writed uint32 err := syscall.WriteFile(port.handle, p, &writed, nil) return int(writed), err @@ -171,7 +170,7 @@ const ( TWOSTOPBITS = 2 ) -func (port *SerialPort) SetMode(mode *Mode) error { +func (port *Port) SetMode(mode *Mode) error { params := DCB{} if GetCommState(port.handle, ¶ms) != nil { port.Close() @@ -196,7 +195,7 @@ func (port *SerialPort) SetMode(mode *Mode) error { return nil } -func OpenPort(portName string, mode *Mode) (*SerialPort, error) { +func OpenPort(portName string, mode *Mode) (*Port, error) { portName = "\\\\.\\" + portName path, err := syscall.UTF16PtrFromString(portName) if err != nil { @@ -219,7 +218,7 @@ func OpenPort(portName string, mode *Mode) (*SerialPort, error) { return nil, err } // Create the serial port - port := &SerialPort{ + port := &Port{ handle: handle, } From f5be203394a6ca2110d53bf0dbe2402bc888ddd5 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 24 Apr 2016 17:19:32 +0200 Subject: [PATCH 003/181] Created interface Port. Rationalized documentation. --- doc.go | 6 +++--- example_serialport_test.go | 2 +- example_test.go | 2 +- serial.go | 30 ++++++++++++++++++++++++++++++ serial_unix.go | 38 +++++++++++++------------------------- serial_windows.go | 16 ++++++++-------- 6 files changed, 56 insertions(+), 38 deletions(-) diff --git a/doc.go b/doc.go index ef85cae..d7f6fea 100644 --- a/doc.go +++ b/doc.go @@ -26,17 +26,17 @@ GetPortsList function: fmt.Printf("Found port: %v\n", port) } -The serial port can be opened with the OpenPort function: +The serial port can be opened with the Open function: mode := &serial.Mode{ BaudRate: 115200, } - port, err := serial.OpenPort("/dev/ttyUSB0", mode) + port, err := serial.Open("/dev/ttyUSB0", mode) if err != nil { log.Fatal(err) } -The OpenPort command needs a "mode" parameter that specifies the configuration +The Open function needs a "mode" parameter that specifies the configuration options for the serial port. If not specified the default options are 9600_N81, in the example above only the speed is changed so the port is opened using 115200_N81. The following snippets shows how to declare a configuration for 57600_E71: diff --git a/example_serialport_test.go b/example_serialport_test.go index c470ac9..eb51176 100644 --- a/example_serialport_test.go +++ b/example_serialport_test.go @@ -11,7 +11,7 @@ import "log" import "go.bug.st/serial" func ExampleSerialPort_SetMode() { - port, err := serial.OpenPort("/dev/ttyACM0", &serial.Mode{}) + port, err := serial.Open("/dev/ttyACM0", &serial.Mode{}) if err != nil { log.Fatal(err) } diff --git a/example_test.go b/example_test.go index c408e1e..3805210 100644 --- a/example_test.go +++ b/example_test.go @@ -35,7 +35,7 @@ func Example_sendAndReceive() { DataBits: 8, StopBits: serial.OneStopBit, } - port, err := serial.OpenPort(ports[0], mode) + port, err := serial.Open(ports[0], mode) if err != nil { log.Fatal(err) } diff --git a/serial.go b/serial.go index 09a7e21..805f979 100644 --- a/serial.go +++ b/serial.go @@ -6,6 +6,36 @@ package serial // import "go.bug.st/serial" +// Port is the interface for a serial Port +type Port interface { + // SetMode sets all parameters of the serial port + SetMode(mode *Mode) error + + // Stores data received from the serial port into the provided byte array + // buffer. The function returns the number of bytes read. + // + // The Read function blocks until (at least) one byte is received from + // the serial port or an error occurs. + Read(p []byte) (n int, err error) + + // Send the content of the data byte array to the serial port. + // Returns the number of bytes written. + Write(p []byte) (n int, err error) + + // Close the serial port + Close() error +} + +// Open opens the serial port using the specified modes +func Open(portName string, mode *Mode) (Port, error) { + return nativeOpen(portName, mode) +} + +// GetPortsList retrieve the list of available serial ports +func GetPortsList() ([]string, error) { + return nativeGetPortsList() +} + // Mode describes a serial port configuration. type Mode struct { BaudRate int // The serial port bitrate (aka Baudrate) diff --git a/serial_unix.go b/serial_unix.go index f308dc0..357ee25 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -14,34 +14,24 @@ import "strings" import "syscall" import "unsafe" -// Port is the handler for a serial Port -type Port struct { +type unixPort struct { handle int } -// Close the serial port -func (port *Port) Close() error { +func (port *unixPort) Close() error { port.releaseExclusiveAccess() return syscall.Close(port.handle) } -// Stores data received from the serial port into the provided byte array -// buffer. The function returns the number of bytes read. -// -// The Read function blocks until (at least) one byte is received from -// the serial port or an error occurs. -func (port *Port) Read(p []byte) (n int, err error) { +func (port *unixPort) Read(p []byte) (n int, err error) { return syscall.Read(port.handle, p) } -// Send the content of the data byte array to the serial port. -// Returns the number of bytes written. -func (port *Port) Write(p []byte) (n int, err error) { +func (port *unixPort) Write(p []byte) (n int, err error) { return syscall.Write(port.handle, p) } -// SetMode sets all parameters of the serial port -func (port *Port) SetMode(mode *Mode) error { +func (port *unixPort) SetMode(mode *Mode) error { settings, err := port.getTermSettings() if err != nil { return err @@ -61,8 +51,7 @@ func (port *Port) SetMode(mode *Mode) error { return port.setTermSettings(settings) } -// OpenPort opens the serial port using the specified modes -func OpenPort(portName string, mode *Mode) (*Port, error) { +func nativeOpen(portName string, mode *Mode) (*unixPort, error) { h, err := syscall.Open(portName, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NDELAY, 0) if err != nil { switch err { @@ -73,7 +62,7 @@ func OpenPort(portName string, mode *Mode) (*Port, error) { } return nil, err } - port := &Port{ + port := &unixPort{ handle: h, } @@ -102,8 +91,7 @@ func OpenPort(portName string, mode *Mode) (*Port, error) { return port, nil } -// GetPortsList retrieve the list of available serial ports -func GetPortsList() ([]string, error) { +func nativeGetPortsList() ([]string, error) { files, err := ioutil.ReadDir(devFolder) if err != nil { return nil, err @@ -129,7 +117,7 @@ func GetPortsList() ([]string, error) { // Check if serial port is real or is a placeholder serial port "ttySxx" if strings.HasPrefix(f.Name(), "ttyS") { - port, err := OpenPort(portName, &Mode{}) + port, err := nativeOpen(portName, &Mode{}) if err != nil { serr, ok := err.(*PortError) if ok && serr.Code() == InvalidSerialPort { @@ -230,20 +218,20 @@ func setRawMode(settings *syscall.Termios) { // native syscall wrapper functions -func (port *Port) getTermSettings() (*syscall.Termios, error) { +func (port *unixPort) getTermSettings() (*syscall.Termios, error) { settings := &syscall.Termios{} err := ioctl(port.handle, ioctlTcgetattr, uintptr(unsafe.Pointer(settings))) return settings, err } -func (port *Port) setTermSettings(settings *syscall.Termios) error { +func (port *unixPort) setTermSettings(settings *syscall.Termios) error { return ioctl(port.handle, ioctlTcsetattr, uintptr(unsafe.Pointer(settings))) } -func (port *Port) acquireExclusiveAccess() error { +func (port *unixPort) acquireExclusiveAccess() error { return ioctl(port.handle, syscall.TIOCEXCL, 0) } -func (port *Port) releaseExclusiveAccess() error { +func (port *unixPort) releaseExclusiveAccess() error { return ioctl(port.handle, syscall.TIOCNXCL, 0) } diff --git a/serial_windows.go b/serial_windows.go index b2cff41..52285c5 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -18,13 +18,13 @@ package serial // import "go.bug.st/serial" import "syscall" -type Port struct { +type windowsPort struct { handle syscall.Handle } //sys RegEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) = advapi32.RegEnumValueW -func GetPortsList() ([]string, error) { +func nativeGetPortsList() ([]string, error) { subKey, err := syscall.UTF16PtrFromString("HARDWARE\\DEVICEMAP\\SERIALCOMM\\") if err != nil { return nil, &PortError{code: ErrorEnumeratingPorts} @@ -55,11 +55,11 @@ func GetPortsList() ([]string, error) { return list, nil } -func (port *Port) Close() error { +func (port *windowsPort) Close() error { return syscall.CloseHandle(port.handle) } -func (port *Port) Read(p []byte) (int, error) { +func (port *windowsPort) Read(p []byte) (int, error) { var readed uint32 params := &DCB{} for { @@ -82,7 +82,7 @@ func (port *Port) Read(p []byte) (int, error) { } } -func (port *Port) Write(p []byte) (int, error) { +func (port *windowsPort) Write(p []byte) (int, error) { var writed uint32 err := syscall.WriteFile(port.handle, p, &writed, nil) return int(writed), err @@ -170,7 +170,7 @@ const ( TWOSTOPBITS = 2 ) -func (port *Port) SetMode(mode *Mode) error { +func (port *windowsPort) SetMode(mode *Mode) error { params := DCB{} if GetCommState(port.handle, ¶ms) != nil { port.Close() @@ -195,7 +195,7 @@ func (port *Port) SetMode(mode *Mode) error { return nil } -func OpenPort(portName string, mode *Mode) (*Port, error) { +func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { portName = "\\\\.\\" + portName path, err := syscall.UTF16PtrFromString(portName) if err != nil { @@ -218,7 +218,7 @@ func OpenPort(portName string, mode *Mode) (*Port, error) { return nil, err } // Create the serial port - port := &Port{ + port := &windowsPort{ handle: handle, } From 294a68d5235782316271acbddf3bf37cfb43969a Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 24 Apr 2016 17:58:32 +0200 Subject: [PATCH 004/181] Another round of lint on Windows implementation --- serial_windows.go | 108 ++++++++++++++++++++++----------------------- syscall_windows.go | 16 ++++--- 2 files changed, 64 insertions(+), 60 deletions(-) diff --git a/serial_windows.go b/serial_windows.go index 52285c5..c211c0b 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -22,7 +22,7 @@ type windowsPort struct { handle syscall.Handle } -//sys RegEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) = advapi32.RegEnumValueW +//sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) = advapi32.RegEnumValueW func nativeGetPortsList() ([]string, error) { subKey, err := syscall.UTF16PtrFromString("HARDWARE\\DEVICEMAP\\SERIALCOMM\\") @@ -47,7 +47,7 @@ func nativeGetPortsList() ([]string, error) { dataSize := uint32(len(data)) var name [1024]uint16 nameSize := uint32(len(name)) - if RegEnumValue(h, uint32(i), &name[0], &nameSize, nil, nil, &data[0], &dataSize) != nil { + if regEnumValue(h, uint32(i), &name[0], &nameSize, nil, nil, &data[0], &dataSize) != nil { return nil, &PortError{code: ErrorEnumeratingPorts} } list[i] = syscall.UTF16ToString(data[:]) @@ -61,7 +61,7 @@ func (port *windowsPort) Close() error { func (port *windowsPort) Read(p []byte) (int, error) { var readed uint32 - params := &DCB{} + params := &dcb{} for { if err := syscall.ReadFile(port.handle, p, &readed, nil); err != nil { return int(readed), err @@ -74,8 +74,8 @@ func (port *windowsPort) Read(p []byte) (int, error) { // a serial port is alive in Windows is to check if the SetCommState // function fails. - GetCommState(port.handle, params) - if err := SetCommState(port.handle, params); err != nil { + getCommState(port.handle, params) + if err := setCommState(port.handle, params); err != nil { port.Close() return 0, err } @@ -89,27 +89,27 @@ func (port *windowsPort) Write(p []byte) (int, error) { } const ( - DCB_BINARY = 0x00000001 - DCB_PARITY = 0x00000002 - DCB_OUT_X_CTS_FLOW = 0x00000004 - DCB_OUT_X_DSR_FLOW = 0x00000008 - DCB_DTR_CONTROL_DISABLE_MASK = ^0x00000030 - DCB_DTR_CONTROL_ENABLE = 0x00000010 - DCB_DTR_CONTROL_HANDSHAKE = 0x00000020 - DCB_DSR_SENSITIVITY = 0x00000040 - DCB_TX_CONTINUE_ON_XOFF = 0x00000080 - DCB_OUT_X = 0x00000100 - DCB_IN_X = 0x00000200 - DCB_ERROR_CHAR = 0x00000400 - DCB_NULL = 0x00000800 - DCB_RTS_CONTROL_DISABLE_MASK = ^0x00003000 - DCB_RTS_CONTROL_ENABLE = 0x00001000 - DCB_RTS_CONTROL_HANDSHAKE = 0x00002000 - DCB_RTS_CONTROL_TOGGLE = 0x00003000 - DCB_ABORT_ON_ERROR = 0x00004000 + dcbBinary = 0x00000001 + dcbParity = 0x00000002 + dcbOutXCTSFlow = 0x00000004 + dcbOutXDSRFlow = 0x00000008 + dcbDTRControlDisableMask = ^0x00000030 + dcbDTRControlEnable = 0x00000010 + dcbDTRControlHandshake = 0x00000020 + dcbDSRSensitivity = 0x00000040 + dcbTXContinueOnXOFF = 0x00000080 + dcbOutX = 0x00000100 + dcbInX = 0x00000200 + dcbErrorChar = 0x00000400 + dcbNull = 0x00000800 + dcbRTSControlDisbaleMask = ^0x00003000 + dcbRTSControlEnable = 0x00001000 + dcbRTSControlHandshake = 0x00002000 + dcbRTSControlToggle = 0x00003000 + dcbAbortOnError = 0x00004000 ) -type DCB struct { +type dcb struct { DCBlength uint32 BaudRate uint32 @@ -139,12 +139,12 @@ type DCB struct { XonChar byte XoffChar byte ErrorChar byte - EofChar byte + EOFChar byte EvtChar byte wReserved1 uint16 } -type COMMTIMEOUTS struct { +type commTimeouts struct { ReadIntervalTimeout uint32 ReadTotalTimeoutMultiplier uint32 ReadTotalTimeoutConstant uint32 @@ -152,27 +152,27 @@ type COMMTIMEOUTS struct { WriteTotalTimeoutConstant uint32 } -//sys GetCommState(handle syscall.Handle, dcb *DCB) (err error) -//sys SetCommState(handle syscall.Handle, dcb *DCB) (err error) -//sys SetCommTimeouts(handle syscall.Handle, timeouts *COMMTIMEOUTS) (err error) +//sys getCommState(handle syscall.Handle, dcb *dcb) (err error) = GetCommState +//sys setCommState(handle syscall.Handle, dcb *dcb) (err error) = SetCommState +//sys setCommTimeouts(handle syscall.Handle, timeouts *commTimeouts) (err error) = SetCommTimeouts const ( - NOPARITY = 0 // Default - ODDPARITY = 1 - EVENPARITY = 2 - MARKPARITY = 3 - SPACEPARITY = 4 + noParity = 0 // Default + oddParity = 1 + evenParity = 2 + markParity = 3 + spaceParity = 4 ) const ( - ONESTOPBIT = 0 // Default - ONE5STOPBITS = 1 - TWOSTOPBITS = 2 + oneStopBit = 0 // Default + one5StopBits = 1 + twoStopBits = 2 ) func (port *windowsPort) SetMode(mode *Mode) error { - params := DCB{} - if GetCommState(port.handle, ¶ms) != nil { + params := dcb{} + if getCommState(port.handle, ¶ms) != nil { port.Close() return &PortError{code: InvalidSerialPort} } @@ -188,7 +188,7 @@ func (port *windowsPort) SetMode(mode *Mode) error { } params.StopBits = byte(mode.StopBits) params.Parity = byte(mode.Parity) - if SetCommState(port.handle, ¶ms) != nil { + if setCommState(port.handle, ¶ms) != nil { port.Close() return &PortError{code: InvalidSerialPort} } @@ -228,38 +228,38 @@ func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { return nil, &PortError{code: InvalidSerialPort} } - params := &DCB{} - if GetCommState(port.handle, params) != nil { + params := &dcb{} + if getCommState(port.handle, params) != nil { port.Close() return nil, &PortError{code: InvalidSerialPort} } - params.Flags |= DCB_RTS_CONTROL_ENABLE | DCB_DTR_CONTROL_ENABLE - params.Flags &= ^uint32(DCB_OUT_X_CTS_FLOW) - params.Flags &= ^uint32(DCB_OUT_X_DSR_FLOW) - params.Flags &= ^uint32(DCB_DSR_SENSITIVITY) - params.Flags |= DCB_TX_CONTINUE_ON_XOFF - params.Flags &= ^uint32(DCB_IN_X | DCB_OUT_X) - params.Flags &= ^uint32(DCB_ERROR_CHAR) - params.Flags &= ^uint32(DCB_NULL) - params.Flags &= ^uint32(DCB_ABORT_ON_ERROR) + params.Flags |= dcbRTSControlEnable | dcbDTRControlEnable + params.Flags &= ^uint32(dcbOutXCTSFlow) + params.Flags &= ^uint32(dcbOutXDSRFlow) + params.Flags &= ^uint32(dcbDSRSensitivity) + params.Flags |= dcbTXContinueOnXOFF + params.Flags &= ^uint32(dcbInX | dcbOutX) + params.Flags &= ^uint32(dcbErrorChar) + params.Flags &= ^uint32(dcbNull) + params.Flags &= ^uint32(dcbAbortOnError) params.XonLim = 2048 params.XoffLim = 512 params.XonChar = 17 // DC1 params.XoffChar = 19 // C3 - if SetCommState(port.handle, params) != nil { + if setCommState(port.handle, params) != nil { port.Close() return nil, &PortError{code: InvalidSerialPort} } // Set timeouts to 1 second - timeouts := &COMMTIMEOUTS{ + timeouts := &commTimeouts{ ReadIntervalTimeout: 0xFFFFFFFF, ReadTotalTimeoutMultiplier: 0xFFFFFFFF, ReadTotalTimeoutConstant: 1000, // 1 sec WriteTotalTimeoutConstant: 0, WriteTotalTimeoutMultiplier: 0, } - if SetCommTimeouts(port.handle, timeouts) != nil { + if setCommTimeouts(port.handle, timeouts) != nil { port.Close() return nil, &PortError{code: InvalidSerialPort} } diff --git a/syscall_windows.go b/syscall_windows.go index f868cb8..fd36f09 100644 --- a/syscall_windows.go +++ b/syscall_windows.go @@ -3,8 +3,12 @@ package serial -import "unsafe" -import "syscall" +import ( + "syscall" + "unsafe" +) + +var _ unsafe.Pointer var ( modadvapi32 = syscall.NewLazyDLL("advapi32.dll") @@ -16,7 +20,7 @@ var ( procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") ) -func RegEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) { +func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) { r0, _, _ := syscall.Syscall9(procRegEnumValueW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(class)), uintptr(unsafe.Pointer(value)), uintptr(unsafe.Pointer(valueLen)), 0) if r0 != 0 { regerrno = syscall.Errno(r0) @@ -24,7 +28,7 @@ func RegEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint3 return } -func GetCommState(handle syscall.Handle, dcb *DCB) (err error) { +func getCommState(handle syscall.Handle, dcb *dcb) (err error) { r1, _, e1 := syscall.Syscall(procGetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0) if r1 == 0 { if e1 != 0 { @@ -36,7 +40,7 @@ func GetCommState(handle syscall.Handle, dcb *DCB) (err error) { return } -func SetCommState(handle syscall.Handle, dcb *DCB) (err error) { +func setCommState(handle syscall.Handle, dcb *dcb) (err error) { r1, _, e1 := syscall.Syscall(procSetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0) if r1 == 0 { if e1 != 0 { @@ -48,7 +52,7 @@ func SetCommState(handle syscall.Handle, dcb *DCB) (err error) { return } -func SetCommTimeouts(handle syscall.Handle, timeouts *COMMTIMEOUTS) (err error) { +func setCommTimeouts(handle syscall.Handle, timeouts *commTimeouts) (err error) { r1, _, e1 := syscall.Syscall(procSetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0) if r1 == 0 { if e1 != 0 { From f5235118e11288b01c578baa054afb5588f2d623 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 24 Apr 2016 23:56:03 +0200 Subject: [PATCH 005/181] Made mode bits enumeration explicit in Windows implementation --- serial_windows.go | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/serial_windows.go b/serial_windows.go index c211c0b..a28bf73 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -157,19 +157,33 @@ type commTimeouts struct { //sys setCommTimeouts(handle syscall.Handle, timeouts *commTimeouts) (err error) = SetCommTimeouts const ( - noParity = 0 // Default + noParity = 0 oddParity = 1 evenParity = 2 markParity = 3 spaceParity = 4 ) +var parityMap = map[Parity]byte{ + NoParity: noParity, + OddParity: oddParity, + EvenParity: evenParity, + MarkParity: markParity, + SpaceParity: spaceParity, +} + const ( - oneStopBit = 0 // Default + oneStopBit = 0 one5StopBits = 1 twoStopBits = 2 ) +var stopBitsMap = map[StopBits]byte{ + OneStopBit: oneStopBit, + OnePointFiveStopBits: one5StopBits, + TwoStopBits: twoStopBits, +} + func (port *windowsPort) SetMode(mode *Mode) error { params := dcb{} if getCommState(port.handle, ¶ms) != nil { @@ -186,8 +200,8 @@ func (port *windowsPort) SetMode(mode *Mode) error { } else { params.ByteSize = byte(mode.DataBits) } - params.StopBits = byte(mode.StopBits) - params.Parity = byte(mode.Parity) + params.StopBits = stopBitsMap[mode.StopBits] + params.Parity = parityMap[mode.Parity] if setCommState(port.handle, ¶ms) != nil { port.Close() return &PortError{code: InvalidSerialPort} From b89deafa1c2878f148e42deea221375834aad87f Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 4 Sep 2016 23:49:24 +0200 Subject: [PATCH 006/181] Change import path to "go.bug.st/serial.v1" This is needed when there are breaking changes in the API. --- .travis.yml | 2 +- README.md | 4 ++-- doc.go | 6 +++--- example_getportlist_test.go | 2 +- example_serialport_test.go | 2 +- example_test.go | 2 +- serial.go | 2 +- serial_darwin.go | 2 +- serial_darwin_386.go | 2 +- serial_darwin_amd64.go | 2 +- serial_linux.go | 2 +- serial_unix.go | 2 +- serial_windows.go | 2 +- syscall_darwin.go | 2 +- syscall_linux.go | 2 +- 15 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2bbb46c..dda6824 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ language: go go: - 1.4.2 -go_import_path: go.bug.st/serial +go_import_path: go.bug.st/serial.v1 env: - TEST_OS=linux TEST_ARCH=386 diff --git a/README.md b/README.md index f2b1950..3ba1707 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -[![Build Status](https://travis-ci.org/bugst/go-serial.svg?branch=master)](https://travis-ci.org/bugst/go-serial) +[![Build Status](https://travis-ci.org/bugst/go-serial.svg?branch=v1)](https://travis-ci.org/bugst/go-serial) A cross-platform serial library for go-lang. -Documentation and examples: https://godoc.org/go.bug.st/serial +Documentation and examples: https://godoc.org/go.bug.st/serial.v1 License: https://github.com/bugst/go-serial/blob/master/LICENSE diff --git a/doc.go b/doc.go index d7f6fea..647e88f 100644 --- a/doc.go +++ b/doc.go @@ -7,10 +7,10 @@ /* Package serial is a cross-platform serial library for the go language. -The canonical import for this library is go.bug.st/serial so the import line +The canonical import for this library is go.bug.st/serial.v1 so the import line is the following: - import "go.bug.st/serial" + import "go.bug.st/serial.v1" It is possibile to get the list of available serial ports with the GetPortsList function: @@ -82,4 +82,4 @@ serial port: This library doesn't make use of cgo and "C" package, so it's a pure go library that can be easily cross compiled. */ -package serial // import "go.bug.st/serial" +package serial // import "go.bug.st/serial.v1" diff --git a/example_getportlist_test.go b/example_getportlist_test.go index 79ff243..5ac8244 100644 --- a/example_getportlist_test.go +++ b/example_getportlist_test.go @@ -8,7 +8,7 @@ package serial_test import "fmt" import "log" -import "go.bug.st/serial" +import "go.bug.st/serial.v1" func ExampleGetPortsList() { ports, err := serial.GetPortsList() diff --git a/example_serialport_test.go b/example_serialport_test.go index eb51176..4e6de61 100644 --- a/example_serialport_test.go +++ b/example_serialport_test.go @@ -8,7 +8,7 @@ package serial_test import "fmt" import "log" -import "go.bug.st/serial" +import "go.bug.st/serial.v1" func ExampleSerialPort_SetMode() { port, err := serial.Open("/dev/ttyACM0", &serial.Mode{}) diff --git a/example_test.go b/example_test.go index 3805210..0f39d26 100644 --- a/example_test.go +++ b/example_test.go @@ -8,7 +8,7 @@ package serial_test import "fmt" import "log" -import "go.bug.st/serial" +import "go.bug.st/serial.v1" // This example prints the list of serial ports and use the first one // to send a string "10,20,30" and prints the response on the screen. diff --git a/serial.go b/serial.go index 805f979..612e447 100644 --- a/serial.go +++ b/serial.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial" +package serial // import "go.bug.st/serial.v1" // Port is the interface for a serial Port type Port interface { diff --git a/serial_darwin.go b/serial_darwin.go index 7a1da90..ad970a8 100644 --- a/serial_darwin.go +++ b/serial_darwin.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial" +package serial // import "go.bug.st/serial.v1" import "syscall" diff --git a/serial_darwin_386.go b/serial_darwin_386.go index a88e554..d3ad4a5 100644 --- a/serial_darwin_386.go +++ b/serial_darwin_386.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial" +package serial // import "go.bug.st/serial.v1" func termiosMask(data int) uint32 { return uint32(data) diff --git a/serial_darwin_amd64.go b/serial_darwin_amd64.go index 377c614..4014596 100644 --- a/serial_darwin_amd64.go +++ b/serial_darwin_amd64.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial" +package serial // import "go.bug.st/serial.v1" // termios manipulation functions diff --git a/serial_linux.go b/serial_linux.go index 0369a82..983975f 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial" +package serial // import "go.bug.st/serial.v1" import "syscall" diff --git a/serial_unix.go b/serial_unix.go index 357ee25..773642c 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -6,7 +6,7 @@ // +build linux darwin -package serial // import "go.bug.st/serial" +package serial // import "go.bug.st/serial.v1" import "io/ioutil" import "regexp" diff --git a/serial_windows.go b/serial_windows.go index a28bf73..07cdd9c 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial" +package serial // import "go.bug.st/serial.v1" /* diff --git a/syscall_darwin.go b/syscall_darwin.go index f0b87e2..c8cd031 100644 --- a/syscall_darwin.go +++ b/syscall_darwin.go @@ -8,7 +8,7 @@ // mksyscall.pl serial_darwin.go // The generated stub is modified to make it compile under the "serial" package -package serial // import "go.bug.st/serial" +package serial // import "go.bug.st/serial.v1" import "syscall" diff --git a/syscall_linux.go b/syscall_linux.go index 79ff6d3..cb216b1 100644 --- a/syscall_linux.go +++ b/syscall_linux.go @@ -8,7 +8,7 @@ // mksyscall.pl serial_linux.go // The generated stub is modified to make it compile under the "serial" package -package serial // import "go.bug.st/serial" +package serial // import "go.bug.st/serial.v1" import "syscall" From 5eda20a9456f91c8a43f732460646c01ab610140 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 11 Sep 2016 01:09:22 +0200 Subject: [PATCH 007/181] Added a note for api change in readme --- README.md | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3ba1707..4ff3ccb 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,35 @@ [![Build Status](https://travis-ci.org/bugst/go-serial.svg?branch=v1)](https://travis-ci.org/bugst/go-serial) +# go.bug.st/serial.v1 + A cross-platform serial library for go-lang. -Documentation and examples: https://godoc.org/go.bug.st/serial.v1 +## Documentation and examples + +See the godoc here: https://godoc.org/go.bug.st/serial.v1 + +## Development + +If you want to contribute to the development of this library, you must clone this git repository directly into your `src` folder under `src/go.bug.st/serial.v1` and checkout the branch `v1`. + +``` +cd $GOPATH +mkdir -p src/go.bug.st/ +git clone https://github.com/bugst/go-serial.git -b v1 src/go.bug.st/serial.v1 +go test go.bug.st/serial.v1 +``` + +## What's new in v1 + +There are some API improvements, in particular object naming is now more idiomatic, class names are less redundant (for example `serial.SerialPort` is now called `serial.Port`), some internal class fields, constants or enumerations are now private and some methods have been moved into the proper interface. + +If you come from the version v0 and want to see the full list of API changes, please check this pull request: + +https://github.com/bugst/go-serial/pull/5/files + +## License + +The software is release under a BSD 3-clause license -License: https://github.com/bugst/go-serial/blob/master/LICENSE +https://github.com/bugst/go-serial/blob/v1/LICENSE From 01692a6f8dafe259ba516aa37fb9651407a2e994 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 17 Sep 2016 19:48:48 +0200 Subject: [PATCH 008/181] Added missing speeds to freebsd port --- serial_freebsd.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/serial_freebsd.go b/serial_freebsd.go index 536dfc0..318e25c 100644 --- a/serial_freebsd.go +++ b/serial_freebsd.go @@ -33,6 +33,8 @@ var baudrateMap = map[int]int{ 57600: syscall.B57600, 115200: syscall.B115200, 230400: syscall.B230400, + 460800: syscall.B460800, + 921600: syscall.B921600, } var databitsMap = map[int]int{ From fe0dfe76d3463c2e7ff0050db7995659bf82454e Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 12 Oct 2016 16:17:33 +0200 Subject: [PATCH 009/181] Improved PortError Removed useless "err" field. Added "causedBy" field to wrap nested error. --- serial.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/serial.go b/serial.go index 612e447..6fb2ef2 100644 --- a/serial.go +++ b/serial.go @@ -74,8 +74,8 @@ const ( // PortError is a platform independent error type for serial ports type PortError struct { - err string - code PortErrorCode + code PortErrorCode + causedBy error } // PortErrorCode is a code to easily identify the type of error @@ -98,8 +98,8 @@ const ( ErrorEnumeratingPorts ) -// Error returns a string explaining the error occurred -func (e PortError) Error() string { +// EncodedErrorString returns a string explaining the error code +func (e PortError) EncodedErrorString() string { switch e.code { case PortBusy: return "Serial port busy" @@ -115,8 +115,17 @@ func (e PortError) Error() string { return "Invalid port data bits" case ErrorEnumeratingPorts: return "Could not enumerate serial ports" + default: + return "Other error" + } +} + +// Error returns the complete error code with details on the cause of the error +func (e PortError) Error() string { + if e.causedBy != nil { + return e.EncodedErrorString() + ": " + e.causedBy.Error() } - return e.err + return e.EncodedErrorString() } // Code returns an identifier for the kind of error occurred From 7178181a2821732c7f2d5c3da2d411ef043554be Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 20 Oct 2016 23:17:26 +0200 Subject: [PATCH 010/181] Disable RTS/CTS handshake by default Fix #3 --- serial_darwin.go | 5 +++++ serial_freebsd.go | 5 +++++ serial_linux.go | 2 ++ serial_unix.go | 3 +++ 4 files changed, 15 insertions(+) diff --git a/serial_darwin.go b/serial_darwin.go index ad970a8..a9c8109 100644 --- a/serial_darwin.go +++ b/serial_darwin.go @@ -46,6 +46,11 @@ var databitsMap = map[int]int{ const tcCMSPAR int = 0 // may be CMSPAR or PAREXT const tcIUCLC int = 0 +const tcCCTS_OFLOW int = 0x00010000 +const tcCRTS_IFLOW int = 0x00020000 + +const tcCRTSCTS int = (tcCCTS_OFLOW | tcCRTS_IFLOW) + // syscall wrappers //sys ioctl(fd int, req uint64, data uintptr) (err error) diff --git a/serial_freebsd.go b/serial_freebsd.go index 318e25c..55b02f8 100644 --- a/serial_freebsd.go +++ b/serial_freebsd.go @@ -48,6 +48,11 @@ var databitsMap = map[int]int{ const tcCMSPAR int = 0 // may be CMSPAR or PAREXT const tcIUCLC int = 0 +const tcCCTS_OFLOW int = 0x00010000 +const tcCRTS_IFLOW int = 0x00020000 + +const tcCRTSCTS int = tcCCTS_OFLOW + func termiosMask(data int) uint32 { return uint32(data) } diff --git a/serial_linux.go b/serial_linux.go index 983975f..9308ace 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -58,6 +58,8 @@ var databitsMap = map[int]int{ const tcCMSPAR int = 0 // may be CMSPAR or PAREXT const tcIUCLC = syscall.IUCLC +const tcCRTSCTS int = 0x80000000 + func termiosMask(data int) uint32 { return uint32(data) } diff --git a/serial_unix.go b/serial_unix.go index d03c4bd..c71dc53 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -203,6 +203,9 @@ func setRawMode(settings *syscall.Termios) { // Set local mode settings.Cflag |= termiosMask(syscall.CREAD | syscall.CLOCAL) + // Explicitly disable RTS/CTS flow control + settings.Cflag &= ^termiosMask(tcCRTSCTS) + // Set raw mode settings.Lflag &= ^termiosMask(syscall.ICANON | syscall.ECHO | syscall.ECHOE | syscall.ECHOK | syscall.ECHONL | syscall.ECHOCTL | syscall.ECHOPRT | syscall.ECHOKE | syscall.ISIG | syscall.IEXTEN) From 9398f35b6912ecddc65ebed70c7997331ac5bd65 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 20 Oct 2016 23:55:06 +0200 Subject: [PATCH 011/181] Use unsigned values for termios setting This allows the last (most significant) bit to be set without overflow errors. The error becomes visible after adding the CRTSCTS constant on linux/386: const tcCRTSCTS uint = 0x80000000 --- serial_darwin.go | 14 +++++++------- serial_darwin_386.go | 2 +- serial_darwin_amd64.go | 2 +- serial_freebsd.go | 16 ++++++++-------- serial_linux.go | 10 +++++----- serial_unix.go | 2 +- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/serial_darwin.go b/serial_darwin.go index a9c8109..e30c1a4 100644 --- a/serial_darwin.go +++ b/serial_darwin.go @@ -13,7 +13,7 @@ const regexFilter = "^(cu|tty)\\..*" // termios manipulation functions -var baudrateMap = map[int]int{ +var baudrateMap = map[int]uint{ 0: syscall.B9600, // Default to 9600 50: syscall.B50, 75: syscall.B75, @@ -35,7 +35,7 @@ var baudrateMap = map[int]int{ 230400: syscall.B230400, } -var databitsMap = map[int]int{ +var databitsMap = map[int]uint{ 0: syscall.CS8, // Default to 8 bits 5: syscall.CS5, 6: syscall.CS6, @@ -43,13 +43,13 @@ var databitsMap = map[int]int{ 8: syscall.CS8, } -const tcCMSPAR int = 0 // may be CMSPAR or PAREXT -const tcIUCLC int = 0 +const tcCMSPAR uint = 0 // may be CMSPAR or PAREXT +const tcIUCLC uint = 0 -const tcCCTS_OFLOW int = 0x00010000 -const tcCRTS_IFLOW int = 0x00020000 +const tcCCTS_OFLOW uint = 0x00010000 +const tcCRTS_IFLOW uint = 0x00020000 -const tcCRTSCTS int = (tcCCTS_OFLOW | tcCRTS_IFLOW) +const tcCRTSCTS uint = (tcCCTS_OFLOW | tcCRTS_IFLOW) // syscall wrappers diff --git a/serial_darwin_386.go b/serial_darwin_386.go index d3ad4a5..43f0375 100644 --- a/serial_darwin_386.go +++ b/serial_darwin_386.go @@ -6,6 +6,6 @@ package serial // import "go.bug.st/serial.v1" -func termiosMask(data int) uint32 { +func termiosMask(data uint) uint32 { return uint32(data) } diff --git a/serial_darwin_amd64.go b/serial_darwin_amd64.go index 4014596..0e70ce5 100644 --- a/serial_darwin_amd64.go +++ b/serial_darwin_amd64.go @@ -8,6 +8,6 @@ package serial // import "go.bug.st/serial.v1" // termios manipulation functions -func termiosMask(data int) uint64 { +func termiosMask(data uint) uint64 { return uint64(data) } diff --git a/serial_freebsd.go b/serial_freebsd.go index 55b02f8..046c489 100644 --- a/serial_freebsd.go +++ b/serial_freebsd.go @@ -13,7 +13,7 @@ const regexFilter = "^(cu|tty)\\..*" // termios manipulation functions -var baudrateMap = map[int]int{ +var baudrateMap = map[int]uint{ 0: syscall.B9600, // Default to 9600 50: syscall.B50, 75: syscall.B75, @@ -37,7 +37,7 @@ var baudrateMap = map[int]int{ 921600: syscall.B921600, } -var databitsMap = map[int]int{ +var databitsMap = map[int]uint{ 0: syscall.CS8, // Default to 8 bits 5: syscall.CS5, 6: syscall.CS6, @@ -45,15 +45,15 @@ var databitsMap = map[int]int{ 8: syscall.CS8, } -const tcCMSPAR int = 0 // may be CMSPAR or PAREXT -const tcIUCLC int = 0 +const tcCMSPAR uint = 0 // may be CMSPAR or PAREXT +const tcIUCLC uint = 0 -const tcCCTS_OFLOW int = 0x00010000 -const tcCRTS_IFLOW int = 0x00020000 +const tcCCTS_OFLOW uint = 0x00010000 +const tcCRTS_IFLOW uint = 0x00020000 -const tcCRTSCTS int = tcCCTS_OFLOW +const tcCRTSCTS uint = tcCCTS_OFLOW -func termiosMask(data int) uint32 { +func termiosMask(data uint) uint32 { return uint32(data) } diff --git a/serial_linux.go b/serial_linux.go index 9308ace..1490a5e 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -13,7 +13,7 @@ const regexFilter = "(ttyS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO)[0-9]{1,3}" // termios manipulation functions -var baudrateMap = map[int]int{ +var baudrateMap = map[int]uint{ 0: syscall.B9600, // Default to 9600 50: syscall.B50, 75: syscall.B75, @@ -47,7 +47,7 @@ var baudrateMap = map[int]int{ 4000000: syscall.B4000000, } -var databitsMap = map[int]int{ +var databitsMap = map[int]uint{ 0: syscall.CS8, // Default to 8 bits 5: syscall.CS5, 6: syscall.CS6, @@ -55,12 +55,12 @@ var databitsMap = map[int]int{ 8: syscall.CS8, } -const tcCMSPAR int = 0 // may be CMSPAR or PAREXT +const tcCMSPAR uint = 0 // may be CMSPAR or PAREXT const tcIUCLC = syscall.IUCLC -const tcCRTSCTS int = 0x80000000 +const tcCRTSCTS uint = 0x80000000 -func termiosMask(data int) uint32 { +func termiosMask(data uint) uint32 { return uint32(data) } diff --git a/serial_unix.go b/serial_unix.go index c71dc53..060cf94 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -143,7 +143,7 @@ func setTermSettingsBaudrate(speed int, settings *syscall.Termios) error { return &PortError{code: InvalidSpeed} } // revert old baudrate - BAUDMASK := 0 + var BAUDMASK uint for _, rate := range baudrateMap { BAUDMASK |= rate } From 50993cc77b942c131ab2a65b701301e3b2c3f038 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 20 Oct 2016 23:57:09 +0200 Subject: [PATCH 012/181] Factorize a function to enable/disable of RTS/CTS handshake for unix --- serial_unix.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/serial_unix.go b/serial_unix.go index 060cf94..c481ffb 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -72,13 +72,18 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { return nil, &PortError{code: InvalidSerialPort} } - // Set raw mode settings, err := port.getTermSettings() if err != nil { port.Close() return nil, &PortError{code: InvalidSerialPort} } + + // Set raw mode setRawMode(settings) + + // Explicitly disable RTS/CTS flow control + setTermSettingsCtsRts(false, settings) + if port.setTermSettings(settings) != nil { port.Close() return nil, &PortError{code: InvalidSerialPort} @@ -199,13 +204,18 @@ func setTermSettingsStopBits(bits StopBits, settings *syscall.Termios) error { return nil } +func setTermSettingsCtsRts(enable bool, settings *syscall.Termios) { + if enable { + settings.Cflag |= termiosMask(tcCRTSCTS) + } else { + settings.Cflag &= ^termiosMask(tcCRTSCTS) + } +} + func setRawMode(settings *syscall.Termios) { // Set local mode settings.Cflag |= termiosMask(syscall.CREAD | syscall.CLOCAL) - // Explicitly disable RTS/CTS flow control - settings.Cflag &= ^termiosMask(tcCRTSCTS) - // Set raw mode settings.Lflag &= ^termiosMask(syscall.ICANON | syscall.ECHO | syscall.ECHOE | syscall.ECHOK | syscall.ECHONL | syscall.ECHOCTL | syscall.ECHOPRT | syscall.ECHOKE | syscall.ISIG | syscall.IEXTEN) From a3909960d32f490b79b8e23631aeb55c4896d483 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 22 Oct 2016 15:39:46 +0200 Subject: [PATCH 013/181] Removed termiosMask(..) and use the correct bitmask type This requires a bit of copying in darwin 386/amd64 but simplifies the overall design. --- serial_darwin.go | 42 ------------------- serial_darwin_386.go | 42 ++++++++++++++++++- serial_darwin_amd64.go | 40 +++++++++++++++++- serial_freebsd.go | 20 ++++----- serial_linux.go | 14 ++----- serial_unix.go | 92 +++++++++++++++++++++++++++--------------- 6 files changed, 149 insertions(+), 101 deletions(-) diff --git a/serial_darwin.go b/serial_darwin.go index e30c1a4..393236d 100644 --- a/serial_darwin.go +++ b/serial_darwin.go @@ -11,48 +11,6 @@ import "syscall" const devFolder = "/dev" const regexFilter = "^(cu|tty)\\..*" -// termios manipulation functions - -var baudrateMap = map[int]uint{ - 0: syscall.B9600, // Default to 9600 - 50: syscall.B50, - 75: syscall.B75, - 110: syscall.B110, - 134: syscall.B134, - 150: syscall.B150, - 200: syscall.B200, - 300: syscall.B300, - 600: syscall.B600, - 1200: syscall.B1200, - 1800: syscall.B1800, - 2400: syscall.B2400, - 4800: syscall.B4800, - 9600: syscall.B9600, - 19200: syscall.B19200, - 38400: syscall.B38400, - 57600: syscall.B57600, - 115200: syscall.B115200, - 230400: syscall.B230400, -} - -var databitsMap = map[int]uint{ - 0: syscall.CS8, // Default to 8 bits - 5: syscall.CS5, - 6: syscall.CS6, - 7: syscall.CS7, - 8: syscall.CS8, -} - -const tcCMSPAR uint = 0 // may be CMSPAR or PAREXT -const tcIUCLC uint = 0 - -const tcCCTS_OFLOW uint = 0x00010000 -const tcCRTS_IFLOW uint = 0x00020000 - -const tcCRTSCTS uint = (tcCCTS_OFLOW | tcCRTS_IFLOW) - -// syscall wrappers - //sys ioctl(fd int, req uint64, data uintptr) (err error) const ioctlTcgetattr = syscall.TIOCGETA diff --git a/serial_darwin_386.go b/serial_darwin_386.go index 43f0375..a1a6771 100644 --- a/serial_darwin_386.go +++ b/serial_darwin_386.go @@ -6,6 +6,44 @@ package serial // import "go.bug.st/serial.v1" -func termiosMask(data uint) uint32 { - return uint32(data) +import "syscall" + +// termios manipulation functions + +var baudrateMap = map[int]uint32{ + 0: syscall.B9600, // Default to 9600 + 50: syscall.B50, + 75: syscall.B75, + 110: syscall.B110, + 134: syscall.B134, + 150: syscall.B150, + 200: syscall.B200, + 300: syscall.B300, + 600: syscall.B600, + 1200: syscall.B1200, + 1800: syscall.B1800, + 2400: syscall.B2400, + 4800: syscall.B4800, + 9600: syscall.B9600, + 19200: syscall.B19200, + 38400: syscall.B38400, + 57600: syscall.B57600, + 115200: syscall.B115200, + 230400: syscall.B230400, +} + +var databitsMap = map[int]uint32{ + 0: syscall.CS8, // Default to 8 bits + 5: syscall.CS5, + 6: syscall.CS6, + 7: syscall.CS7, + 8: syscall.CS8, } + +const tcCMSPAR uint32 = 0 // may be CMSPAR or PAREXT +const tcIUCLC uint32 = 0 + +const tcCCTS_OFLOW uint32 = 0x00010000 +const tcCRTS_IFLOW uint32 = 0x00020000 + +const tcCRTSCTS uint32 = (tcCCTS_OFLOW | tcCRTS_IFLOW) diff --git a/serial_darwin_amd64.go b/serial_darwin_amd64.go index 0e70ce5..b721d78 100644 --- a/serial_darwin_amd64.go +++ b/serial_darwin_amd64.go @@ -6,8 +6,44 @@ package serial // import "go.bug.st/serial.v1" +import "syscall" + // termios manipulation functions -func termiosMask(data uint) uint64 { - return uint64(data) +var baudrateMap = map[int]uint64{ + 0: syscall.B9600, // Default to 9600 + 50: syscall.B50, + 75: syscall.B75, + 110: syscall.B110, + 134: syscall.B134, + 150: syscall.B150, + 200: syscall.B200, + 300: syscall.B300, + 600: syscall.B600, + 1200: syscall.B1200, + 1800: syscall.B1800, + 2400: syscall.B2400, + 4800: syscall.B4800, + 9600: syscall.B9600, + 19200: syscall.B19200, + 38400: syscall.B38400, + 57600: syscall.B57600, + 115200: syscall.B115200, + 230400: syscall.B230400, +} + +var databitsMap = map[int]uint64{ + 0: syscall.CS8, // Default to 8 bits + 5: syscall.CS5, + 6: syscall.CS6, + 7: syscall.CS7, + 8: syscall.CS8, } + +const tcCMSPAR uint64 = 0 // may be CMSPAR or PAREXT +const tcIUCLC uint64 = 0 + +const tcCCTS_OFLOW uint64 = 0x00010000 +const tcCRTS_IFLOW uint64 = 0x00020000 + +const tcCRTSCTS uint64 = (tcCCTS_OFLOW | tcCRTS_IFLOW) diff --git a/serial_freebsd.go b/serial_freebsd.go index 046c489..1e09349 100644 --- a/serial_freebsd.go +++ b/serial_freebsd.go @@ -13,7 +13,7 @@ const regexFilter = "^(cu|tty)\\..*" // termios manipulation functions -var baudrateMap = map[int]uint{ +var baudrateMap = map[int]uint32{ 0: syscall.B9600, // Default to 9600 50: syscall.B50, 75: syscall.B75, @@ -37,7 +37,7 @@ var baudrateMap = map[int]uint{ 921600: syscall.B921600, } -var databitsMap = map[int]uint{ +var databitsMap = map[int]uint32{ 0: syscall.CS8, // Default to 8 bits 5: syscall.CS5, 6: syscall.CS6, @@ -45,19 +45,13 @@ var databitsMap = map[int]uint{ 8: syscall.CS8, } -const tcCMSPAR uint = 0 // may be CMSPAR or PAREXT -const tcIUCLC uint = 0 +const tcCMSPAR uint32 = 0 // may be CMSPAR or PAREXT +const tcIUCLC uint32 = 0 -const tcCCTS_OFLOW uint = 0x00010000 -const tcCRTS_IFLOW uint = 0x00020000 +const tcCCTS_OFLOW uint32 = 0x00010000 +const tcCRTS_IFLOW uint32 = 0x00020000 -const tcCRTSCTS uint = tcCCTS_OFLOW - -func termiosMask(data uint) uint32 { - return uint32(data) -} - -// syscall wrappers +const tcCRTSCTS uint32 = tcCCTS_OFLOW //sys ioctl(fd int, req uint64, data uintptr) (err error) diff --git a/serial_linux.go b/serial_linux.go index 1490a5e..89f3aed 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -13,7 +13,7 @@ const regexFilter = "(ttyS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO)[0-9]{1,3}" // termios manipulation functions -var baudrateMap = map[int]uint{ +var baudrateMap = map[int]uint32{ 0: syscall.B9600, // Default to 9600 50: syscall.B50, 75: syscall.B75, @@ -47,7 +47,7 @@ var baudrateMap = map[int]uint{ 4000000: syscall.B4000000, } -var databitsMap = map[int]uint{ +var databitsMap = map[int]uint32{ 0: syscall.CS8, // Default to 8 bits 5: syscall.CS5, 6: syscall.CS6, @@ -55,16 +55,10 @@ var databitsMap = map[int]uint{ 8: syscall.CS8, } -const tcCMSPAR uint = 0 // may be CMSPAR or PAREXT +const tcCMSPAR uint32 = 0 // may be CMSPAR or PAREXT const tcIUCLC = syscall.IUCLC -const tcCRTSCTS uint = 0x80000000 - -func termiosMask(data uint) uint32 { - return uint32(data) -} - -// syscall wrappers +const tcCRTSCTS uint32 = 0x80000000 //sys ioctl(fd int, req uint64, data uintptr) (err error) diff --git a/serial_unix.go b/serial_unix.go index c481ffb..b5cf703 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -148,38 +148,43 @@ func setTermSettingsBaudrate(speed int, settings *syscall.Termios) error { return &PortError{code: InvalidSpeed} } // revert old baudrate - var BAUDMASK uint for _, rate := range baudrateMap { - BAUDMASK |= rate + settings.Cflag &^= rate } - settings.Cflag &= ^termiosMask(BAUDMASK) // set new baudrate - settings.Cflag |= termiosMask(baudrate) - settings.Ispeed = termiosMask(baudrate) - settings.Ospeed = termiosMask(baudrate) + settings.Cflag |= baudrate + settings.Ispeed = baudrate + settings.Ospeed = baudrate return nil } func setTermSettingsParity(parity Parity, settings *syscall.Termios) error { switch parity { case NoParity: - settings.Cflag &= ^termiosMask(syscall.PARENB | syscall.PARODD | tcCMSPAR) - settings.Iflag &= ^termiosMask(syscall.INPCK) + settings.Cflag &^= syscall.PARENB + settings.Cflag &^= syscall.PARODD + settings.Cflag &^= tcCMSPAR + settings.Iflag &^= syscall.INPCK case OddParity: - settings.Cflag |= termiosMask(syscall.PARENB | syscall.PARODD) - settings.Cflag &= ^termiosMask(tcCMSPAR) - settings.Iflag |= termiosMask(syscall.INPCK) + settings.Cflag |= syscall.PARENB + settings.Cflag |= syscall.PARODD + settings.Cflag &^= tcCMSPAR + settings.Iflag |= syscall.INPCK case EvenParity: - settings.Cflag &= ^termiosMask(syscall.PARODD | tcCMSPAR) - settings.Cflag |= termiosMask(syscall.PARENB) - settings.Iflag |= termiosMask(syscall.INPCK) + settings.Cflag |= syscall.PARENB + settings.Cflag &^= syscall.PARODD + settings.Cflag &^= tcCMSPAR + settings.Iflag |= syscall.INPCK case MarkParity: - settings.Cflag |= termiosMask(syscall.PARENB | syscall.PARODD | tcCMSPAR) - settings.Iflag |= termiosMask(syscall.INPCK) + settings.Cflag |= syscall.PARENB + settings.Cflag |= syscall.PARODD + settings.Cflag |= tcCMSPAR + settings.Iflag |= syscall.INPCK case SpaceParity: - settings.Cflag &= ^termiosMask(syscall.PARODD) - settings.Cflag |= termiosMask(syscall.PARENB | tcCMSPAR) - settings.Iflag |= termiosMask(syscall.INPCK) + settings.Cflag |= syscall.PARENB + settings.Cflag &^= syscall.PARODD + settings.Cflag |= tcCMSPAR + settings.Iflag |= syscall.INPCK } return nil } @@ -189,40 +194,63 @@ func setTermSettingsDataBits(bits int, settings *syscall.Termios) error { if !ok { return &PortError{code: InvalidDataBits} } - settings.Cflag &= ^termiosMask(syscall.CSIZE) - settings.Cflag |= termiosMask(databits) + // Remove previous databits setting + settings.Cflag &^= syscall.CSIZE + // Set requested databits + settings.Cflag |= databits return nil } func setTermSettingsStopBits(bits StopBits, settings *syscall.Termios) error { switch bits { case OneStopBit: - settings.Cflag &= ^termiosMask(syscall.CSTOPB) + settings.Cflag &^= syscall.CSTOPB case OnePointFiveStopBits, TwoStopBits: - settings.Cflag |= termiosMask(syscall.CSTOPB) + settings.Cflag |= syscall.CSTOPB } return nil } func setTermSettingsCtsRts(enable bool, settings *syscall.Termios) { if enable { - settings.Cflag |= termiosMask(tcCRTSCTS) + settings.Cflag |= tcCRTSCTS } else { - settings.Cflag &= ^termiosMask(tcCRTSCTS) + settings.Cflag &^= tcCRTSCTS } } func setRawMode(settings *syscall.Termios) { // Set local mode - settings.Cflag |= termiosMask(syscall.CREAD | syscall.CLOCAL) + settings.Cflag |= syscall.CREAD + settings.Cflag |= syscall.CLOCAL // Set raw mode - settings.Lflag &= ^termiosMask(syscall.ICANON | syscall.ECHO | syscall.ECHOE | syscall.ECHOK | - syscall.ECHONL | syscall.ECHOCTL | syscall.ECHOPRT | syscall.ECHOKE | syscall.ISIG | syscall.IEXTEN) - settings.Iflag &= ^termiosMask(syscall.IXON | syscall.IXOFF | syscall.IXANY | syscall.INPCK | - syscall.IGNPAR | syscall.PARMRK | syscall.ISTRIP | syscall.IGNBRK | syscall.BRKINT | syscall.INLCR | - syscall.IGNCR | syscall.ICRNL | tcIUCLC) - settings.Oflag &= ^termiosMask(syscall.OPOST) + settings.Lflag &^= syscall.ICANON + settings.Lflag &^= syscall.ECHO + settings.Lflag &^= syscall.ECHOE + settings.Lflag &^= syscall.ECHOK + settings.Lflag &^= syscall.ECHONL + settings.Lflag &^= syscall.ECHOCTL + settings.Lflag &^= syscall.ECHOPRT + settings.Lflag &^= syscall.ECHOKE + settings.Lflag &^= syscall.ISIG + settings.Lflag &^= syscall.IEXTEN + + settings.Iflag &^= syscall.IXON + settings.Iflag &^= syscall.IXOFF + settings.Iflag &^= syscall.IXANY + settings.Iflag &^= syscall.INPCK + settings.Iflag &^= syscall.IGNPAR + settings.Iflag &^= syscall.PARMRK + settings.Iflag &^= syscall.ISTRIP + settings.Iflag &^= syscall.IGNBRK + settings.Iflag &^= syscall.BRKINT + settings.Iflag &^= syscall.INLCR + settings.Iflag &^= syscall.IGNCR + settings.Iflag &^= syscall.ICRNL + settings.Iflag &^= tcIUCLC + + settings.Oflag &^= syscall.OPOST // Block reads until at least one char is available (no timeout) settings.Cc[syscall.VMIN] = 1 From c534e6fb455042237ad9044cfcd50ce2ac3ea668 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 22 Oct 2016 15:48:43 +0200 Subject: [PATCH 014/181] Improved error reporting for parity settings --- serial.go | 4 ++++ serial_unix.go | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/serial.go b/serial.go index 6fb2ef2..96a9b09 100644 --- a/serial.go +++ b/serial.go @@ -94,6 +94,8 @@ const ( InvalidSpeed // InvalidDataBits the number of data bits is not valid or not supported InvalidDataBits + // InvalidParity the selected parity is not valid or not supported + InvalidParity // ErrorEnumeratingPorts an error occurred while listing serial port ErrorEnumeratingPorts ) @@ -113,6 +115,8 @@ func (e PortError) EncodedErrorString() string { return "Invalid port speed" case InvalidDataBits: return "Invalid port data bits" + case InvalidParity: + return "Port parity invalid or not supported" case ErrorEnumeratingPorts: return "Could not enumerate serial ports" default: diff --git a/serial_unix.go b/serial_unix.go index b5cf703..5c20b68 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -176,15 +176,23 @@ func setTermSettingsParity(parity Parity, settings *syscall.Termios) error { settings.Cflag &^= tcCMSPAR settings.Iflag |= syscall.INPCK case MarkParity: + if tcCMSPAR == 0 { + return &PortError{code: InvalidParity} + } settings.Cflag |= syscall.PARENB settings.Cflag |= syscall.PARODD settings.Cflag |= tcCMSPAR settings.Iflag |= syscall.INPCK case SpaceParity: + if tcCMSPAR == 0 { + return &PortError{code: InvalidParity} + } settings.Cflag |= syscall.PARENB settings.Cflag &^= syscall.PARODD settings.Cflag |= tcCMSPAR settings.Iflag |= syscall.INPCK + default: + return &PortError{code: InvalidParity} } return nil } From f78e270591687fd4e536dd1bfae1f00b8b79ae6b Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 22 Oct 2016 15:50:10 +0200 Subject: [PATCH 015/181] Slightly improved error messages --- serial.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serial.go b/serial.go index 96a9b09..7b82518 100644 --- a/serial.go +++ b/serial.go @@ -112,9 +112,9 @@ func (e PortError) EncodedErrorString() string { case PermissionDenied: return "Permission denied" case InvalidSpeed: - return "Invalid port speed" + return "Port speed invalid or not supported" case InvalidDataBits: - return "Invalid port data bits" + return "Port data bits invalid or not supported" case InvalidParity: return "Port parity invalid or not supported" case ErrorEnumeratingPorts: From cb166b5b850dee60bc5f9929301cf6e180399868 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 22 Oct 2016 15:52:27 +0200 Subject: [PATCH 016/181] Improved error reporting on setting stop bits --- serial.go | 4 ++++ serial_unix.go | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/serial.go b/serial.go index 7b82518..6b932d0 100644 --- a/serial.go +++ b/serial.go @@ -96,6 +96,8 @@ const ( InvalidDataBits // InvalidParity the selected parity is not valid or not supported InvalidParity + // InvalidStopBits the selected number of stop bits is not valid or not supported + InvalidStopBits // ErrorEnumeratingPorts an error occurred while listing serial port ErrorEnumeratingPorts ) @@ -117,6 +119,8 @@ func (e PortError) EncodedErrorString() string { return "Port data bits invalid or not supported" case InvalidParity: return "Port parity invalid or not supported" + case InvalidStopBits: + return "Port stop bits invalid or not supported" case ErrorEnumeratingPorts: return "Could not enumerate serial ports" default: diff --git a/serial_unix.go b/serial_unix.go index 5c20b68..2368cdc 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -213,8 +213,12 @@ func setTermSettingsStopBits(bits StopBits, settings *syscall.Termios) error { switch bits { case OneStopBit: settings.Cflag &^= syscall.CSTOPB - case OnePointFiveStopBits, TwoStopBits: + case OnePointFiveStopBits: + return &PortError{code: InvalidStopBits} + case TwoStopBits: settings.Cflag |= syscall.CSTOPB + default: + return &PortError{code: InvalidStopBits} } return nil } From b93c8f2fea77853ed879eb1e9ed1cb73cae40e6e Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 13 Oct 2016 21:52:13 +0200 Subject: [PATCH 017/181] Use go:generate to create syscall_windows.go This dramatically improves maintainability of generated files. This will be eventually implemented on other generated files as well. --- .travis.yml | 1 + extras/mksyscall_windows.go | 871 ++++++++++++++++++++++++++++++++++++ serial_windows.go | 2 + syscall_windows.go | 9 +- 4 files changed, 879 insertions(+), 4 deletions(-) create mode 100755 extras/mksyscall_windows.go diff --git a/.travis.yml b/.travis.yml index ae20143..9e7e380 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,7 @@ before_install: - ( cd $GOROOT/src; GOOS=$TEST_OS GOARCH=$TEST_ARCH ./make.bash ) script: + - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go get golang.org/x/sys/windows - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go build -v ./... - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go test -c -v ./... diff --git a/extras/mksyscall_windows.go b/extras/mksyscall_windows.go new file mode 100755 index 0000000..1e0d940 --- /dev/null +++ b/extras/mksyscall_windows.go @@ -0,0 +1,871 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build ignore + +/* +mksyscall_windows generates windows system call bodies + +It parses all files specified on command line containing function +prototypes (like syscall_windows.go) and prints system call bodies +to standard output. + +The prototypes are marked by lines beginning with "//sys" and read +like func declarations if //sys is replaced by func, but: + +* The parameter lists must give a name for each argument. This + includes return parameters. + +* The parameter lists must give a type for each argument: + the (x, y, z int) shorthand is not allowed. + +* If the return parameter is an error number, it must be named err. + +* If go func name needs to be different from it's winapi dll name, + the winapi name could be specified at the end, after "=" sign, like + //sys LoadLibrary(libname string) (handle uint32, err error) = LoadLibraryA + +* Each function that returns err needs to supply a condition, that + return value of winapi will be tested against to detect failure. + This would set err to windows "last-error", otherwise it will be nil. + The value can be provided at end of //sys declaration, like + //sys LoadLibrary(libname string) (handle uint32, err error) [failretval==-1] = LoadLibraryA + and is [failretval==0] by default. + +Usage: + mksyscall_windows [flags] [path ...] + +The flags are: + -output + Specify output file name (outputs to console if blank). + -trace + Generate print statement after every syscall. +*/ +package main + +import ( + "bufio" + "bytes" + "errors" + "flag" + "fmt" + "go/format" + "go/parser" + "go/token" + "io" + "io/ioutil" + "log" + "os" + "path/filepath" + "runtime" + "sort" + "strconv" + "strings" + "text/template" +) + +var ( + filename = flag.String("output", "", "output file name (standard output if omitted)") + printTraceFlag = flag.Bool("trace", false, "generate print statement after every syscall") + systemDLL = flag.Bool("systemdll", true, "whether all DLLs should be loaded from the Windows system directory") +) + +func trim(s string) string { + return strings.Trim(s, " \t") +} + +var packageName string + +func packagename() string { + return packageName +} + +func syscalldot() string { + if packageName == "syscall" { + return "" + } + return "syscall." +} + +// Param is function parameter +type Param struct { + Name string + Type string + fn *Fn + tmpVarIdx int +} + +// tmpVar returns temp variable name that will be used to represent p during syscall. +func (p *Param) tmpVar() string { + if p.tmpVarIdx < 0 { + p.tmpVarIdx = p.fn.curTmpVarIdx + p.fn.curTmpVarIdx++ + } + return fmt.Sprintf("_p%d", p.tmpVarIdx) +} + +// BoolTmpVarCode returns source code for bool temp variable. +func (p *Param) BoolTmpVarCode() string { + const code = `var %s uint32 + if %s { + %s = 1 + } else { + %s = 0 + }` + tmp := p.tmpVar() + return fmt.Sprintf(code, tmp, p.Name, tmp, tmp) +} + +// SliceTmpVarCode returns source code for slice temp variable. +func (p *Param) SliceTmpVarCode() string { + const code = `var %s *%s + if len(%s) > 0 { + %s = &%s[0] + }` + tmp := p.tmpVar() + return fmt.Sprintf(code, tmp, p.Type[2:], p.Name, tmp, p.Name) +} + +// StringTmpVarCode returns source code for string temp variable. +func (p *Param) StringTmpVarCode() string { + errvar := p.fn.Rets.ErrorVarName() + if errvar == "" { + errvar = "_" + } + tmp := p.tmpVar() + const code = `var %s %s + %s, %s = %s(%s)` + s := fmt.Sprintf(code, tmp, p.fn.StrconvType(), tmp, errvar, p.fn.StrconvFunc(), p.Name) + if errvar == "-" { + return s + } + const morecode = ` + if %s != nil { + return + }` + return s + fmt.Sprintf(morecode, errvar) +} + +// TmpVarCode returns source code for temp variable. +func (p *Param) TmpVarCode() string { + switch { + case p.Type == "bool": + return p.BoolTmpVarCode() + case strings.HasPrefix(p.Type, "[]"): + return p.SliceTmpVarCode() + default: + return "" + } +} + +// TmpVarHelperCode returns source code for helper's temp variable. +func (p *Param) TmpVarHelperCode() string { + if p.Type != "string" { + return "" + } + return p.StringTmpVarCode() +} + +// SyscallArgList returns source code fragments representing p parameter +// in syscall. Slices are translated into 2 syscall parameters: pointer to +// the first element and length. +func (p *Param) SyscallArgList() []string { + t := p.HelperType() + var s string + switch { + case t[0] == '*': + s = fmt.Sprintf("unsafe.Pointer(%s)", p.Name) + case t == "bool": + s = p.tmpVar() + case strings.HasPrefix(t, "[]"): + return []string{ + fmt.Sprintf("uintptr(unsafe.Pointer(%s))", p.tmpVar()), + fmt.Sprintf("uintptr(len(%s))", p.Name), + } + default: + s = p.Name + } + return []string{fmt.Sprintf("uintptr(%s)", s)} +} + +// IsError determines if p parameter is used to return error. +func (p *Param) IsError() bool { + return p.Name == "err" && p.Type == "error" +} + +// HelperType returns type of parameter p used in helper function. +func (p *Param) HelperType() string { + if p.Type == "string" { + return p.fn.StrconvType() + } + return p.Type +} + +// join concatenates parameters ps into a string with sep separator. +// Each parameter is converted into string by applying fn to it +// before conversion. +func join(ps []*Param, fn func(*Param) string, sep string) string { + if len(ps) == 0 { + return "" + } + a := make([]string, 0) + for _, p := range ps { + a = append(a, fn(p)) + } + return strings.Join(a, sep) +} + +// Rets describes function return parameters. +type Rets struct { + Name string + Type string + ReturnsError bool + FailCond string +} + +// ErrorVarName returns error variable name for r. +func (r *Rets) ErrorVarName() string { + if r.ReturnsError { + return "err" + } + if r.Type == "error" { + return r.Name + } + return "" +} + +// ToParams converts r into slice of *Param. +func (r *Rets) ToParams() []*Param { + ps := make([]*Param, 0) + if len(r.Name) > 0 { + ps = append(ps, &Param{Name: r.Name, Type: r.Type}) + } + if r.ReturnsError { + ps = append(ps, &Param{Name: "err", Type: "error"}) + } + return ps +} + +// List returns source code of syscall return parameters. +func (r *Rets) List() string { + s := join(r.ToParams(), func(p *Param) string { return p.Name + " " + p.Type }, ", ") + if len(s) > 0 { + s = "(" + s + ")" + } + return s +} + +// PrintList returns source code of trace printing part correspondent +// to syscall return values. +func (r *Rets) PrintList() string { + return join(r.ToParams(), func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `) +} + +// SetReturnValuesCode returns source code that accepts syscall return values. +func (r *Rets) SetReturnValuesCode() string { + if r.Name == "" && !r.ReturnsError { + return "" + } + retvar := "r0" + if r.Name == "" { + retvar = "r1" + } + errvar := "_" + if r.ReturnsError { + errvar = "e1" + } + return fmt.Sprintf("%s, _, %s := ", retvar, errvar) +} + +func (r *Rets) useLongHandleErrorCode(retvar string) string { + const code = `if %s { + if e1 != 0 { + err = error(e1) + } else { + err = %sEINVAL + } + }` + cond := retvar + " == 0" + if r.FailCond != "" { + cond = strings.Replace(r.FailCond, "failretval", retvar, 1) + } + return fmt.Sprintf(code, cond, syscalldot()) +} + +// SetErrorCode returns source code that sets return parameters. +func (r *Rets) SetErrorCode() string { + const code = `if r0 != 0 { + %s = %sErrno(r0) + }` + if r.Name == "" && !r.ReturnsError { + return "" + } + if r.Name == "" { + return r.useLongHandleErrorCode("r1") + } + if r.Type == "error" { + return fmt.Sprintf(code, r.Name, syscalldot()) + } + s := "" + switch { + case r.Type[0] == '*': + s = fmt.Sprintf("%s = (%s)(unsafe.Pointer(r0))", r.Name, r.Type) + case r.Type == "bool": + s = fmt.Sprintf("%s = r0 != 0", r.Name) + default: + s = fmt.Sprintf("%s = %s(r0)", r.Name, r.Type) + } + if !r.ReturnsError { + return s + } + return s + "\n\t" + r.useLongHandleErrorCode(r.Name) +} + +// Fn describes syscall function. +type Fn struct { + Name string + Params []*Param + Rets *Rets + PrintTrace bool + dllname string + dllfuncname string + src string + // TODO: get rid of this field and just use parameter index instead + curTmpVarIdx int // insure tmp variables have uniq names +} + +// extractParams parses s to extract function parameters. +func extractParams(s string, f *Fn) ([]*Param, error) { + s = trim(s) + if s == "" { + return nil, nil + } + a := strings.Split(s, ",") + ps := make([]*Param, len(a)) + for i := range ps { + s2 := trim(a[i]) + b := strings.Split(s2, " ") + if len(b) != 2 { + b = strings.Split(s2, "\t") + if len(b) != 2 { + return nil, errors.New("Could not extract function parameter from \"" + s2 + "\"") + } + } + ps[i] = &Param{ + Name: trim(b[0]), + Type: trim(b[1]), + fn: f, + tmpVarIdx: -1, + } + } + return ps, nil +} + +// extractSection extracts text out of string s starting after start +// and ending just before end. found return value will indicate success, +// and prefix, body and suffix will contain correspondent parts of string s. +func extractSection(s string, start, end rune) (prefix, body, suffix string, found bool) { + s = trim(s) + if strings.HasPrefix(s, string(start)) { + // no prefix + body = s[1:] + } else { + a := strings.SplitN(s, string(start), 2) + if len(a) != 2 { + return "", "", s, false + } + prefix = a[0] + body = a[1] + } + a := strings.SplitN(body, string(end), 2) + if len(a) != 2 { + return "", "", "", false + } + return prefix, a[0], a[1], true +} + +// newFn parses string s and return created function Fn. +func newFn(s string) (*Fn, error) { + s = trim(s) + f := &Fn{ + Rets: &Rets{}, + src: s, + PrintTrace: *printTraceFlag, + } + // function name and args + prefix, body, s, found := extractSection(s, '(', ')') + if !found || prefix == "" { + return nil, errors.New("Could not extract function name and parameters from \"" + f.src + "\"") + } + f.Name = prefix + var err error + f.Params, err = extractParams(body, f) + if err != nil { + return nil, err + } + // return values + _, body, s, found = extractSection(s, '(', ')') + if found { + r, err := extractParams(body, f) + if err != nil { + return nil, err + } + switch len(r) { + case 0: + case 1: + if r[0].IsError() { + f.Rets.ReturnsError = true + } else { + f.Rets.Name = r[0].Name + f.Rets.Type = r[0].Type + } + case 2: + if !r[1].IsError() { + return nil, errors.New("Only last windows error is allowed as second return value in \"" + f.src + "\"") + } + f.Rets.ReturnsError = true + f.Rets.Name = r[0].Name + f.Rets.Type = r[0].Type + default: + return nil, errors.New("Too many return values in \"" + f.src + "\"") + } + } + // fail condition + _, body, s, found = extractSection(s, '[', ']') + if found { + f.Rets.FailCond = body + } + // dll and dll function names + s = trim(s) + if s == "" { + return f, nil + } + if !strings.HasPrefix(s, "=") { + return nil, errors.New("Could not extract dll name from \"" + f.src + "\"") + } + s = trim(s[1:]) + a := strings.Split(s, ".") + switch len(a) { + case 1: + f.dllfuncname = a[0] + case 2: + f.dllname = a[0] + f.dllfuncname = a[1] + default: + return nil, errors.New("Could not extract dll name from \"" + f.src + "\"") + } + return f, nil +} + +// DLLName returns DLL name for function f. +func (f *Fn) DLLName() string { + if f.dllname == "" { + return "kernel32" + } + return f.dllname +} + +// DLLName returns DLL function name for function f. +func (f *Fn) DLLFuncName() string { + if f.dllfuncname == "" { + return f.Name + } + return f.dllfuncname +} + +// ParamList returns source code for function f parameters. +func (f *Fn) ParamList() string { + return join(f.Params, func(p *Param) string { return p.Name + " " + p.Type }, ", ") +} + +// HelperParamList returns source code for helper function f parameters. +func (f *Fn) HelperParamList() string { + return join(f.Params, func(p *Param) string { return p.Name + " " + p.HelperType() }, ", ") +} + +// ParamPrintList returns source code of trace printing part correspondent +// to syscall input parameters. +func (f *Fn) ParamPrintList() string { + return join(f.Params, func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `) +} + +// ParamCount return number of syscall parameters for function f. +func (f *Fn) ParamCount() int { + n := 0 + for _, p := range f.Params { + n += len(p.SyscallArgList()) + } + return n +} + +// SyscallParamCount determines which version of Syscall/Syscall6/Syscall9/... +// to use. It returns parameter count for correspondent SyscallX function. +func (f *Fn) SyscallParamCount() int { + n := f.ParamCount() + switch { + case n <= 3: + return 3 + case n <= 6: + return 6 + case n <= 9: + return 9 + case n <= 12: + return 12 + case n <= 15: + return 15 + default: + panic("too many arguments to system call") + } +} + +// Syscall determines which SyscallX function to use for function f. +func (f *Fn) Syscall() string { + c := f.SyscallParamCount() + if c == 3 { + return syscalldot() + "Syscall" + } + return syscalldot() + "Syscall" + strconv.Itoa(c) +} + +// SyscallParamList returns source code for SyscallX parameters for function f. +func (f *Fn) SyscallParamList() string { + a := make([]string, 0) + for _, p := range f.Params { + a = append(a, p.SyscallArgList()...) + } + for len(a) < f.SyscallParamCount() { + a = append(a, "0") + } + return strings.Join(a, ", ") +} + +// HelperCallParamList returns source code of call into function f helper. +func (f *Fn) HelperCallParamList() string { + a := make([]string, 0, len(f.Params)) + for _, p := range f.Params { + s := p.Name + if p.Type == "string" { + s = p.tmpVar() + } + a = append(a, s) + } + return strings.Join(a, ", ") +} + +// IsUTF16 is true, if f is W (utf16) function. It is false +// for all A (ascii) functions. +func (f *Fn) IsUTF16() bool { + s := f.DLLFuncName() + return s[len(s)-1] == 'W' +} + +// StrconvFunc returns name of Go string to OS string function for f. +func (f *Fn) StrconvFunc() string { + if f.IsUTF16() { + return syscalldot() + "UTF16PtrFromString" + } + return syscalldot() + "BytePtrFromString" +} + +// StrconvType returns Go type name used for OS string for f. +func (f *Fn) StrconvType() string { + if f.IsUTF16() { + return "*uint16" + } + return "*byte" +} + +// HasStringParam is true, if f has at least one string parameter. +// Otherwise it is false. +func (f *Fn) HasStringParam() bool { + for _, p := range f.Params { + if p.Type == "string" { + return true + } + } + return false +} + +// HelperName returns name of function f helper. +func (f *Fn) HelperName() string { + if !f.HasStringParam() { + return f.Name + } + return "_" + f.Name +} + +// Source files and functions. +type Source struct { + Funcs []*Fn + Files []string + StdLibImports []string + ExternalImports []string +} + +func (src *Source) Import(pkg string) { + src.StdLibImports = append(src.StdLibImports, pkg) + sort.Strings(src.StdLibImports) +} + +func (src *Source) ExternalImport(pkg string) { + src.ExternalImports = append(src.ExternalImports, pkg) + sort.Strings(src.ExternalImports) +} + +// ParseFiles parses files listed in fs and extracts all syscall +// functions listed in sys comments. It returns source files +// and functions collection *Source if successful. +func ParseFiles(fs []string) (*Source, error) { + src := &Source{ + Funcs: make([]*Fn, 0), + Files: make([]string, 0), + StdLibImports: []string{ + "unsafe", + }, + ExternalImports: make([]string, 0), + } + for _, file := range fs { + if err := src.ParseFile(file); err != nil { + return nil, err + } + } + return src, nil +} + +// DLLs return dll names for a source set src. +func (src *Source) DLLs() []string { + uniq := make(map[string]bool) + r := make([]string, 0) + for _, f := range src.Funcs { + name := f.DLLName() + if _, found := uniq[name]; !found { + uniq[name] = true + r = append(r, name) + } + } + return r +} + +// ParseFile adds additional file path to a source set src. +func (src *Source) ParseFile(path string) error { + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + + s := bufio.NewScanner(file) + for s.Scan() { + t := trim(s.Text()) + if len(t) < 7 { + continue + } + if !strings.HasPrefix(t, "//sys") { + continue + } + t = t[5:] + if !(t[0] == ' ' || t[0] == '\t') { + continue + } + f, err := newFn(t[1:]) + if err != nil { + return err + } + src.Funcs = append(src.Funcs, f) + } + if err := s.Err(); err != nil { + return err + } + src.Files = append(src.Files, path) + + // get package name + fset := token.NewFileSet() + _, err = file.Seek(0, 0) + if err != nil { + return err + } + pkg, err := parser.ParseFile(fset, "", file, parser.PackageClauseOnly) + if err != nil { + return err + } + packageName = pkg.Name.Name + + return nil +} + +// IsStdRepo returns true if src is part of standard library. +func (src *Source) IsStdRepo() (bool, error) { + if len(src.Files) == 0 { + return false, errors.New("no input files provided") + } + abspath, err := filepath.Abs(src.Files[0]) + if err != nil { + return false, err + } + goroot := runtime.GOROOT() + if runtime.GOOS == "windows" { + abspath = strings.ToLower(abspath) + goroot = strings.ToLower(goroot) + } + return strings.HasPrefix(abspath, goroot), nil +} + +// Generate output source file from a source set src. +func (src *Source) Generate(w io.Writer) error { + const ( + pkgStd = iota // any package in std library + pkgXSysWindows // x/sys/windows package + pkgOther + ) + isStdRepo, err := src.IsStdRepo() + if err != nil { + return err + } + var pkgtype int + switch { + case isStdRepo: + pkgtype = pkgStd + case packageName == "windows": + // TODO: this needs better logic than just using package name + pkgtype = pkgXSysWindows + default: + pkgtype = pkgOther + } + if *systemDLL { + switch pkgtype { + case pkgStd: + src.Import("internal/syscall/windows/sysdll") + case pkgXSysWindows: + default: + src.ExternalImport("golang.org/x/sys/windows") + } + } + if packageName != "syscall" { + src.Import("syscall") + } + funcMap := template.FuncMap{ + "packagename": packagename, + "syscalldot": syscalldot, + "newlazydll": func(dll string) string { + arg := "\"" + dll + ".dll\"" + if !*systemDLL { + return syscalldot() + "NewLazyDLL(" + arg + ")" + } + switch pkgtype { + case pkgStd: + return syscalldot() + "NewLazyDLL(sysdll.Add(" + arg + "))" + case pkgXSysWindows: + return "NewLazySystemDLL(" + arg + ")" + default: + return "windows.NewLazySystemDLL(" + arg + ")" + } + }, + } + t := template.Must(template.New("main").Funcs(funcMap).Parse(srcTemplate)) + err = t.Execute(w, src) + if err != nil { + return errors.New("Failed to execute template: " + err.Error()) + } + return nil +} + +func usage() { + fmt.Fprintf(os.Stderr, "usage: mksyscall_windows [flags] [path ...]\n") + flag.PrintDefaults() + os.Exit(1) +} + +func main() { + flag.Usage = usage + flag.Parse() + if len(flag.Args()) <= 0 { + fmt.Fprintf(os.Stderr, "no files to parse provided\n") + usage() + } + + src, err := ParseFiles(flag.Args()) + if err != nil { + log.Fatal(err) + } + + var buf bytes.Buffer + if err := src.Generate(&buf); err != nil { + log.Fatal(err) + } + + data, err := format.Source(buf.Bytes()) + if err != nil { + log.Fatal(err) + } + if *filename == "" { + _, err = os.Stdout.Write(data) + } else { + err = ioutil.WriteFile(*filename, data, 0644) + } + if err != nil { + log.Fatal(err) + } +} + +// TODO: use println instead to print in the following template +const srcTemplate = ` + +{{define "main"}}// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT + +package {{packagename}} + +import ( +{{range .StdLibImports}}"{{.}}" +{{end}} + +{{range .ExternalImports}}"{{.}}" +{{end}} +) + +var _ unsafe.Pointer + +var ( +{{template "dlls" .}} +{{template "funcnames" .}}) +{{range .Funcs}}{{if .HasStringParam}}{{template "helperbody" .}}{{end}}{{template "funcbody" .}}{{end}} +{{end}} + +{{/* help functions */}} + +{{define "dlls"}}{{range .DLLs}} mod{{.}} = {{newlazydll .}} +{{end}}{{end}} + +{{define "funcnames"}}{{range .Funcs}} proc{{.DLLFuncName}} = mod{{.DLLName}}.NewProc("{{.DLLFuncName}}") +{{end}}{{end}} + +{{define "helperbody"}} +func {{.Name}}({{.ParamList}}) {{template "results" .}}{ +{{template "helpertmpvars" .}} return {{.HelperName}}({{.HelperCallParamList}}) +} +{{end}} + +{{define "funcbody"}} +func {{.HelperName}}({{.HelperParamList}}) {{template "results" .}}{ +{{template "tmpvars" .}} {{template "syscall" .}} +{{template "seterror" .}}{{template "printtrace" .}} return +} +{{end}} + +{{define "helpertmpvars"}}{{range .Params}}{{if .TmpVarHelperCode}} {{.TmpVarHelperCode}} +{{end}}{{end}}{{end}} + +{{define "tmpvars"}}{{range .Params}}{{if .TmpVarCode}} {{.TmpVarCode}} +{{end}}{{end}}{{end}} + +{{define "results"}}{{if .Rets.List}}{{.Rets.List}} {{end}}{{end}} + +{{define "syscall"}}{{.Rets.SetReturnValuesCode}}{{.Syscall}}(proc{{.DLLFuncName}}.Addr(), {{.ParamCount}}, {{.SyscallParamList}}){{end}} + +{{define "seterror"}}{{if .Rets.SetErrorCode}} {{.Rets.SetErrorCode}} +{{end}}{{end}} + +{{define "printtrace"}}{{if .PrintTrace}} print("SYSCALL: {{.Name}}(", {{.ParamPrintList}}") (", {{.Rets.PrintList}}")\n") +{{end}}{{end}} + +` diff --git a/serial_windows.go b/serial_windows.go index 07cdd9c..a660841 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -22,6 +22,8 @@ type windowsPort struct { handle syscall.Handle } +//go:generate go run extras/mksyscall_windows.go -output syscall_windows.go serial_windows.go + //sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) = advapi32.RegEnumValueW func nativeGetPortsList() ([]string, error) { diff --git a/syscall_windows.go b/syscall_windows.go index fd36f09..17b0f8c 100644 --- a/syscall_windows.go +++ b/syscall_windows.go @@ -1,18 +1,19 @@ -// go build mksyscall_windows.go && ./mksyscall_windows serial_windows.go -// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT +// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT package serial import ( "syscall" "unsafe" + + "golang.org/x/sys/windows" ) var _ unsafe.Pointer var ( - modadvapi32 = syscall.NewLazyDLL("advapi32.dll") - modkernel32 = syscall.NewLazyDLL("kernel32.dll") + modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") procGetCommState = modkernel32.NewProc("GetCommState") From 04302f262ca3879de7bba554937f21805e65d8a3 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 22 Oct 2016 22:17:56 +0200 Subject: [PATCH 018/181] Update testing golang version to 1.7.2 --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9e7e380..461c930 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: go go: - - 1.4.2 + - 1.7.2 go_import_path: go.bug.st/serial.v1 @@ -21,8 +21,6 @@ matrix: - env: TEST_OS=dragonfly TEST_ARCH=amd64 before_install: -# bootstrap go tools for the specific OS/Arch - - ( cd $GOROOT/src; GOOS=$TEST_OS GOARCH=$TEST_ARCH ./make.bash ) script: - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go get golang.org/x/sys/windows From 5be0087bd589daea6b46e4e9e8678c8d4a0740ab Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 29 Oct 2016 22:18:24 +0200 Subject: [PATCH 019/181] Added interface methods for RS232 signals --- example_modem_bits_test.go | 67 ++++++++++++++++++++++++++++++++++++++ serial.go | 19 +++++++++++ serial_unix.go | 12 +++++++ serial_windows.go | 12 +++++++ 4 files changed, 110 insertions(+) create mode 100644 example_modem_bits_test.go diff --git a/example_modem_bits_test.go b/example_modem_bits_test.go new file mode 100644 index 0000000..1673aaf --- /dev/null +++ b/example_modem_bits_test.go @@ -0,0 +1,67 @@ +// +// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +package serial_test + +import "log" +import "go.bug.st/serial.v1" +import "fmt" +import "time" + +func ExampleGetSetModemBits() { + // Open the first serial port detected at 9600bps N81 + mode := &serial.Mode{ + BaudRate: 9600, + Parity: serial.NoParity, + DataBits: 8, + StopBits: serial.OneStopBit, + } + port, err := serial.Open("/dev/ttyACM1", mode) + if err != nil { + log.Fatal(err) + } + defer port.Close() + + count := 0 + for count < 25 { + status, err := port.GetStatus() + if err != nil { + log.Fatal(err) + } + fmt.Printf("Status: %+v\n", status) + + time.Sleep(time.Second) + count++ + if count == 5 { + err := port.SetDTR(false) + if err != nil { + log.Fatal(err) + } + fmt.Println("Set DTR OFF") + } + if count == 10 { + err := port.SetDTR(true) + if err != nil { + log.Fatal(err) + } + fmt.Println("Set DTR ON") + } + if count == 15 { + err := port.SetRTS(false) + if err != nil { + log.Fatal(err) + } + fmt.Println("Set RTS OFF") + } + if count == 20 { + err := port.SetRTS(true) + if err != nil { + log.Fatal(err) + } + fmt.Println("Set RTS ON") + } + } +} diff --git a/serial.go b/serial.go index 6b932d0..dd218d6 100644 --- a/serial.go +++ b/serial.go @@ -22,10 +22,29 @@ type Port interface { // Returns the number of bytes written. Write(p []byte) (n int, err error) + // SetDTR sets the modem status bit DataTerminalReady + SetDTR(dtr bool) error + + // SetRTS sets the modem status bit RequestToSend + SetRTS(rts bool) error + + // GetModemStatusBits returns a ModemStatusBits structure containing the + // modem status bits for the serial port (CTS, DSR, etc...) + GetModemStatusBits() (*ModemStatusBits, error) + // Close the serial port Close() error } +// ModemStatusBits contains all the modem status bits for a serial port (CTS, DSR, etc...). +// It can be retrieved with the Port.GetModemStatusBits() method. +type ModemStatusBits struct { + CTS bool // ClearToSend status + DSR bool // DataSetReady status + RI bool // RingIndicator status + DCD bool // DataCarrierDetect status +} + // Open opens the serial port using the specified modes func Open(portName string, mode *Mode) (Port, error) { return nativeOpen(portName, mode) diff --git a/serial_unix.go b/serial_unix.go index 2368cdc..857686f 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -51,6 +51,18 @@ func (port *unixPort) SetMode(mode *Mode) error { return port.setTermSettings(settings) } +func (port *unixPort) SetDTR(dtr bool) error { + return &PortError{} +} + +func (port *unixPort) SetRTS(rts bool) error { + return &PortError{} +} + +func (port *unixPort) GetModemStatusBits() (*ModemStatusBits, error) { + return nil, &PortError{} +} + func nativeOpen(portName string, mode *Mode) (*unixPort, error) { h, err := syscall.Open(portName, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NDELAY, 0) if err != nil { diff --git a/serial_windows.go b/serial_windows.go index dc380f5..9aef79d 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -211,6 +211,18 @@ func (port *windowsPort) SetMode(mode *Mode) error { return nil } +func (port *windowsPort) SetDTR(dtr bool) error { + return &PortError{} +} + +func (port *windowsPort) SetRTS(rts bool) error { + return &PortError{} +} + +func (port *windowsPort) GetModemStatusBits() (*ModemStatusBits, error) { + return nil, &PortError{} +} + func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { portName = "\\\\.\\" + portName path, err := syscall.UTF16PtrFromString(portName) From a736b8f7b9613adf04940c2342825fd6633a1593 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 29 Oct 2016 23:19:13 +0200 Subject: [PATCH 020/181] Modem signals implementation (posix) --- serial_unix.go | 43 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/serial_unix.go b/serial_unix.go index 857686f..9389992 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -52,15 +52,42 @@ func (port *unixPort) SetMode(mode *Mode) error { } func (port *unixPort) SetDTR(dtr bool) error { - return &PortError{} + status, err := port.getModemBitsStatus() + if err != nil { + return err + } + if dtr { + status |= syscall.TIOCM_DTR + } else { + status &^= syscall.TIOCM_DTR + } + return port.setModemBitsStatus(status) } func (port *unixPort) SetRTS(rts bool) error { - return &PortError{} + status, err := port.getModemBitsStatus() + if err != nil { + return err + } + if rts { + status |= syscall.TIOCM_RTS + } else { + status &^= syscall.TIOCM_RTS + } + return port.setModemBitsStatus(status) } func (port *unixPort) GetModemStatusBits() (*ModemStatusBits, error) { - return nil, &PortError{} + status, err := port.getModemBitsStatus() + if err != nil { + return nil, err + } + return &ModemStatusBits{ + CTS: (status & syscall.TIOCM_CTS) != 0, + DCD: (status & syscall.TIOCM_CD) != 0, + DSR: (status & syscall.TIOCM_DSR) != 0, + RI: (status & syscall.TIOCM_RI) != 0, + }, nil } func nativeOpen(portName string, mode *Mode) (*unixPort, error) { @@ -293,6 +320,16 @@ func (port *unixPort) setTermSettings(settings *syscall.Termios) error { return ioctl(port.handle, ioctlTcsetattr, uintptr(unsafe.Pointer(settings))) } +func (port *unixPort) getModemBitsStatus() (int, error) { + var status int + err := ioctl(port.handle, syscall.TIOCMGET, uintptr(unsafe.Pointer(&status))) + return status, err +} + +func (port *unixPort) setModemBitsStatus(status int) error { + return ioctl(port.handle, syscall.TIOCMSET, uintptr(unsafe.Pointer(&status))) +} + func (port *unixPort) acquireExclusiveAccess() error { return ioctl(port.handle, syscall.TIOCEXCL, 0) } From aa77894c35f819b19ed68501856381166336bbe6 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 29 Oct 2016 23:42:44 +0200 Subject: [PATCH 021/181] Modem signals implementaions (windows) --- serial_windows.go | 78 ++++++++++++++++++++++++++++++++++++++++++++-- syscall_windows.go | 22 ++++++++++--- 2 files changed, 93 insertions(+), 7 deletions(-) diff --git a/serial_windows.go b/serial_windows.go index 9aef79d..ed04e6b 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -10,6 +10,7 @@ package serial // import "go.bug.st/serial.v1" // MSDN article on Serial Communications: // http://msdn.microsoft.com/en-us/library/ff802693.aspx +// (alternative link) https://msdn.microsoft.com/en-us/library/ms810467.aspx // Arduino Playground article on serial communication with Windows API: // http://playground.arduino.cc/Interfacing/CPPWindows @@ -186,6 +187,28 @@ var stopBitsMap = map[StopBits]byte{ TwoStopBits: twoStopBits, } +//sys escapeCommFunction(handle syscall.Handle, function uint32) (res bool) = EscapeCommFunction + +const ( + commFunctionSetXOFF = 1 + commFunctionSetXON = 2 + commFunctionSetRTS = 3 + commFunctionClrRTS = 4 + commFunctionSetDTR = 5 + commFunctionClrDTR = 6 + commFunctionSetBreak = 8 + commFunctionClrBreak = 9 +) + +//sys getCommModemStatus(handle syscall.Handle, bits *uint32) (res bool) = GetCommModemStatus + +const ( + msCTSOn = 0x0010 + msDSROn = 0x0020 + msRingOn = 0x0040 + msRLSDOn = 0x0080 +) + func (port *windowsPort) SetMode(mode *Mode) error { params := dcb{} if getCommState(port.handle, ¶ms) != nil { @@ -212,15 +235,64 @@ func (port *windowsPort) SetMode(mode *Mode) error { } func (port *windowsPort) SetDTR(dtr bool) error { - return &PortError{} + var res bool + if dtr { + res = escapeCommFunction(port.handle, commFunctionSetDTR) + } else { + res = escapeCommFunction(port.handle, commFunctionClrDTR) + } + if !res { + return &PortError{} + } + return nil } func (port *windowsPort) SetRTS(rts bool) error { - return &PortError{} + // It seems that there is a bug in the Windows VCP driver: + // it doesn't send USB control message when the RTS bit is + // changed, so the following code not always works with + // USB-to-serial adapters. + + /* + var res bool + if rts { + res = escapeCommFunction(port.handle, commFunctionSetRTS) + } else { + res = escapeCommFunction(port.handle, commFunctionClrRTS) + } + if !res { + return &PortError{} + } + return nil + */ + + // The following seems a more reliable way to do it + + params := &dcb{} + if err := getCommState(port.handle, params); err != nil { + return &PortError{causedBy: err} + } + params.Flags &= dcbRTSControlDisbaleMask + if rts { + params.Flags |= dcbRTSControlEnable + } + if err := setCommState(port.handle, params); err != nil { + return &PortError{causedBy: err} + } + return nil } func (port *windowsPort) GetModemStatusBits() (*ModemStatusBits, error) { - return nil, &PortError{} + var bits uint32 + if !getCommModemStatus(port.handle, &bits) { + return nil, &PortError{} + } + return &ModemStatusBits{ + CTS: (bits & msCTSOn) != 0, + DCD: (bits & msRLSDOn) != 0, + DSR: (bits & msDSROn) != 0, + RI: (bits & msRingOn) != 0, + }, nil } func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { diff --git a/syscall_windows.go b/syscall_windows.go index 17b0f8c..dfa3f84 100644 --- a/syscall_windows.go +++ b/syscall_windows.go @@ -15,10 +15,12 @@ var ( modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") modkernel32 = windows.NewLazySystemDLL("kernel32.dll") - procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") - procGetCommState = modkernel32.NewProc("GetCommState") - procSetCommState = modkernel32.NewProc("SetCommState") - procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") + procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") + procGetCommState = modkernel32.NewProc("GetCommState") + procSetCommState = modkernel32.NewProc("SetCommState") + procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") + procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction") + procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus") ) func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) { @@ -64,3 +66,15 @@ func setCommTimeouts(handle syscall.Handle, timeouts *commTimeouts) (err error) } return } + +func escapeCommFunction(handle syscall.Handle, function uint32) (res bool) { + r0, _, _ := syscall.Syscall(procEscapeCommFunction.Addr(), 2, uintptr(handle), uintptr(function), 0) + res = r0 != 0 + return +} + +func getCommModemStatus(handle syscall.Handle, bits *uint32) (res bool) { + r0, _, _ := syscall.Syscall(procGetCommModemStatus.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(bits)), 0) + res = r0 != 0 + return +} From f56a50621e8bb8b65991c3c6e16af6abde8565c4 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 29 Oct 2016 23:34:04 +0200 Subject: [PATCH 022/181] Slighlty improved bits handling in windows --- serial_windows.go | 56 +++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/serial_windows.go b/serial_windows.go index a660841..dc380f5 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -91,24 +91,24 @@ func (port *windowsPort) Write(p []byte) (int, error) { } const ( - dcbBinary = 0x00000001 - dcbParity = 0x00000002 - dcbOutXCTSFlow = 0x00000004 - dcbOutXDSRFlow = 0x00000008 - dcbDTRControlDisableMask = ^0x00000030 - dcbDTRControlEnable = 0x00000010 - dcbDTRControlHandshake = 0x00000020 - dcbDSRSensitivity = 0x00000040 - dcbTXContinueOnXOFF = 0x00000080 - dcbOutX = 0x00000100 - dcbInX = 0x00000200 - dcbErrorChar = 0x00000400 - dcbNull = 0x00000800 - dcbRTSControlDisbaleMask = ^0x00003000 - dcbRTSControlEnable = 0x00001000 - dcbRTSControlHandshake = 0x00002000 - dcbRTSControlToggle = 0x00003000 - dcbAbortOnError = 0x00004000 + dcbBinary uint32 = 0x00000001 + dcbParity = 0x00000002 + dcbOutXCTSFlow = 0x00000004 + dcbOutXDSRFlow = 0x00000008 + dcbDTRControlDisableMask = ^uint32(0x00000030) + dcbDTRControlEnable = 0x00000010 + dcbDTRControlHandshake = 0x00000020 + dcbDSRSensitivity = 0x00000040 + dcbTXContinueOnXOFF = 0x00000080 + dcbOutX = 0x00000100 + dcbInX = 0x00000200 + dcbErrorChar = 0x00000400 + dcbNull = 0x00000800 + dcbRTSControlDisbaleMask = ^uint32(0x00003000) + dcbRTSControlEnable = 0x00001000 + dcbRTSControlHandshake = 0x00002000 + dcbRTSControlToggle = 0x00003000 + dcbAbortOnError = 0x00004000 ) type dcb struct { @@ -249,15 +249,19 @@ func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { port.Close() return nil, &PortError{code: InvalidSerialPort} } - params.Flags |= dcbRTSControlEnable | dcbDTRControlEnable - params.Flags &= ^uint32(dcbOutXCTSFlow) - params.Flags &= ^uint32(dcbOutXDSRFlow) - params.Flags &= ^uint32(dcbDSRSensitivity) + params.Flags &= dcbRTSControlDisbaleMask + params.Flags |= dcbRTSControlEnable + params.Flags &= dcbDTRControlDisableMask + params.Flags |= dcbDTRControlEnable + params.Flags &^= dcbOutXCTSFlow + params.Flags &^= dcbOutXDSRFlow + params.Flags &^= dcbDSRSensitivity params.Flags |= dcbTXContinueOnXOFF - params.Flags &= ^uint32(dcbInX | dcbOutX) - params.Flags &= ^uint32(dcbErrorChar) - params.Flags &= ^uint32(dcbNull) - params.Flags &= ^uint32(dcbAbortOnError) + params.Flags &^= dcbInX + params.Flags &^= dcbOutX + params.Flags &^= dcbErrorChar + params.Flags &^= dcbNull + params.Flags &^= dcbAbortOnError params.XonLim = 2048 params.XoffLim = 512 params.XonChar = 17 // DC1 From 43fc80d807eea385cf4d60be922b6a2f61508340 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 31 Oct 2016 18:11:46 +0100 Subject: [PATCH 023/181] Fixed typo... oops --- example_modem_bits_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example_modem_bits_test.go b/example_modem_bits_test.go index 1673aaf..1532616 100644 --- a/example_modem_bits_test.go +++ b/example_modem_bits_test.go @@ -27,7 +27,7 @@ func ExampleGetSetModemBits() { count := 0 for count < 25 { - status, err := port.GetStatus() + status, err := port.GetModemStatusBits() if err != nil { log.Fatal(err) } From f8c1f56fd31f0f8f066ae4b7677f475336875f0c Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 6 Nov 2016 18:30:40 +0100 Subject: [PATCH 024/181] Added unix-pipe wrapper --- pipe_unix.go | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 pipe_unix.go diff --git a/pipe_unix.go b/pipe_unix.go new file mode 100644 index 0000000..066f258 --- /dev/null +++ b/pipe_unix.go @@ -0,0 +1,53 @@ +// +// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +// +build linux darwin freebsd + +package serial // import "go.bug.st/serial.v1" + +import "syscall" + +// pipe is a small wrapper around unix-pipe syscall functions +type pipe struct { + rd int + wr int +} + +func newPipe() (*pipe, error) { + fds := []int{0, 0} + if err := syscall.Pipe(fds); err != nil { + return nil, err + } + return &pipe{rd: fds[0], wr: fds[1]}, nil +} + +func (p *pipe) ReadFD() int { + return p.rd +} + +func (p *pipe) WriteFD() int { + return p.wr +} + +func (p *pipe) Write(data []byte) (int, error) { + return syscall.Write(p.wr, data) +} + +func (p *pipe) Read(data []byte) (int, error) { + return syscall.Read(p.rd, data) +} + +func (p *pipe) Close() error { + err1 := syscall.Close(p.rd) + err2 := syscall.Close(p.wr) + if err1 != nil { + return err1 + } + if err2 != nil { + return err2 + } + return nil +} From 12cc48dd24f43c4d721faae88768247aa291c86b Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 6 Nov 2016 18:30:49 +0100 Subject: [PATCH 025/181] unix: avoid Read blocking forever if called before Close --- serial_unix.go | 61 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/serial_unix.go b/serial_unix.go index 2368cdc..bbbcbce 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -8,22 +8,63 @@ package serial // import "go.bug.st/serial.v1" -import "io/ioutil" -import "regexp" -import "strings" -import "syscall" -import "unsafe" +import ( + "io/ioutil" + "regexp" + "strings" + "sync" + "syscall" + "unsafe" + + "github.com/creack/goselect" +) type unixPort struct { handle int + + closeLock sync.RWMutex + closeSignal *pipe } func (port *unixPort) Close() error { + // Close port port.releaseExclusiveAccess() - return syscall.Close(port.handle) + if err := syscall.Close(port.handle); err != nil { + return err + } + + // Send close signal to all pending reads (if any) + port.closeSignal.Write([]byte{0}) + + // Wait for all readers to complete + port.closeLock.Lock() + defer port.closeLock.Unlock() + + // Close signaling pipe + if err := port.closeSignal.Close(); err != nil { + return err + } + return nil } func (port *unixPort) Read(p []byte) (n int, err error) { + port.closeLock.RLock() + defer port.closeLock.RUnlock() + + r := &goselect.FDSet{} + r.Set(uintptr(port.handle)) + r.Set(uintptr(port.closeSignal.ReadFD())) + e := &goselect.FDSet{} + e.Set(uintptr(port.handle)) + e.Set(uintptr(port.closeSignal.ReadFD())) + + max := port.closeSignal.ReadFD() + if port.handle > max { + max = port.handle + } + if err = goselect.Select(max+1, r, nil, e, -1); err != nil { + return 0, err + } return syscall.Read(port.handle, p) } @@ -93,6 +134,14 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { port.acquireExclusiveAccess() + // This pipe is used as a signal to cancel blocking Read or Write + pipe, err := newPipe() + if err != nil { + port.Close() + return nil, &PortError{code: InvalidSerialPort, causedBy: err} + } + port.closeSignal = pipe + return port, nil } From eee219b43f92eb113ddceeaf6f411c17e08fe909 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 8 Nov 2016 12:31:18 +0100 Subject: [PATCH 026/181] posix: added check for port closing --- serial_unix.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/serial_unix.go b/serial_unix.go index bbbcbce..84488b5 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -33,16 +33,18 @@ func (port *unixPort) Close() error { return err } - // Send close signal to all pending reads (if any) - port.closeSignal.Write([]byte{0}) + if port.closeSignal != nil { + // Send close signal to all pending reads (if any) + port.closeSignal.Write([]byte{0}) - // Wait for all readers to complete - port.closeLock.Lock() - defer port.closeLock.Unlock() + // Wait for all readers to complete + port.closeLock.Lock() + defer port.closeLock.Unlock() - // Close signaling pipe - if err := port.closeSignal.Close(); err != nil { - return err + // Close signaling pipe + if err := port.closeSignal.Close(); err != nil { + return err + } } return nil } From 152558831e919803fd60169bceaa301518f47627 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 13 Nov 2016 23:44:13 +0100 Subject: [PATCH 027/181] Moved Pipe{} into own sub-package It may be moved into a separate project at some point, who knows... --- pipe_unix.go | 53 ------------------------------- serial_unix.go | 8 +++-- unixutils/pipe.go | 80 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 56 deletions(-) delete mode 100644 pipe_unix.go create mode 100644 unixutils/pipe.go diff --git a/pipe_unix.go b/pipe_unix.go deleted file mode 100644 index 066f258..0000000 --- a/pipe_unix.go +++ /dev/null @@ -1,53 +0,0 @@ -// -// Copyright 2014-2016 Cristian Maglie. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -// +build linux darwin freebsd - -package serial // import "go.bug.st/serial.v1" - -import "syscall" - -// pipe is a small wrapper around unix-pipe syscall functions -type pipe struct { - rd int - wr int -} - -func newPipe() (*pipe, error) { - fds := []int{0, 0} - if err := syscall.Pipe(fds); err != nil { - return nil, err - } - return &pipe{rd: fds[0], wr: fds[1]}, nil -} - -func (p *pipe) ReadFD() int { - return p.rd -} - -func (p *pipe) WriteFD() int { - return p.wr -} - -func (p *pipe) Write(data []byte) (int, error) { - return syscall.Write(p.wr, data) -} - -func (p *pipe) Read(data []byte) (int, error) { - return syscall.Read(p.rd, data) -} - -func (p *pipe) Close() error { - err1 := syscall.Close(p.rd) - err2 := syscall.Close(p.wr) - if err1 != nil { - return err1 - } - if err2 != nil { - return err2 - } - return nil -} diff --git a/serial_unix.go b/serial_unix.go index 84488b5..a4ca1ee 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -16,6 +16,8 @@ import ( "syscall" "unsafe" + "go.bug.st/serial.v1/unixutils" + "github.com/creack/goselect" ) @@ -23,7 +25,7 @@ type unixPort struct { handle int closeLock sync.RWMutex - closeSignal *pipe + closeSignal *unixutils.Pipe } func (port *unixPort) Close() error { @@ -137,8 +139,8 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { port.acquireExclusiveAccess() // This pipe is used as a signal to cancel blocking Read or Write - pipe, err := newPipe() - if err != nil { + pipe := &unixutils.Pipe{} + if err := pipe.Open(); err != nil { port.Close() return nil, &PortError{code: InvalidSerialPort, causedBy: err} } diff --git a/unixutils/pipe.go b/unixutils/pipe.go new file mode 100644 index 0000000..567c739 --- /dev/null +++ b/unixutils/pipe.go @@ -0,0 +1,80 @@ +// +// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +// +build linux darwin freebsd + +package unixutils // import "go.bug.st/serial.v1/unixutils" + +import "syscall" +import "fmt" + +// Pipe represents a unix-pipe +type Pipe struct { + opened bool + rd int + wr int +} + +// Open creates a new pipe +func (p *Pipe) Open() error { + fds := []int{0, 0} + if err := syscall.Pipe(fds); err != nil { + return err + } + p.rd = fds[0] + p.wr = fds[1] + p.opened = true + return nil +} + +// ReadFD returns the file handle for the read side of the pipe. +func (p *Pipe) ReadFD() int { + if !p.opened { + return -1 + } + return p.rd +} + +// WriteFD returns the flie handle for the write side of the pipe. +func (p *Pipe) WriteFD() int { + if !p.opened { + return -1 + } + return p.wr +} + +// Write to the pipe the content of data. Returns the numbre of bytes written. +func (p *Pipe) Write(data []byte) (int, error) { + if !p.opened { + return 0, fmt.Errorf("Pipe not opened") + } + return syscall.Write(p.wr, data) +} + +// Read from the pipe into the data array. Returns the number of bytes read. +func (p *Pipe) Read(data []byte) (int, error) { + if !p.opened { + return 0, fmt.Errorf("Pipe not opened") + } + return syscall.Read(p.rd, data) +} + +// Close the pipe +func (p *Pipe) Close() error { + if !p.opened { + return fmt.Errorf("Pipe not opened") + } + err1 := syscall.Close(p.rd) + err2 := syscall.Close(p.wr) + p.opened = false + if err1 != nil { + return err1 + } + if err2 != nil { + return err2 + } + return nil +} From 6eedab17b4eed27850ea899bd122d04b2e800f38 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 14 Nov 2016 00:33:28 +0100 Subject: [PATCH 028/181] Added wrapper around goselect library. This simplifies handling of select syscall. --- serial_unix.go | 17 ++------ unixutils/select.go | 101 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 14 deletions(-) create mode 100644 unixutils/select.go diff --git a/serial_unix.go b/serial_unix.go index a4ca1ee..696b3b5 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -17,8 +17,6 @@ import ( "unsafe" "go.bug.st/serial.v1/unixutils" - - "github.com/creack/goselect" ) type unixPort struct { @@ -55,18 +53,9 @@ func (port *unixPort) Read(p []byte) (n int, err error) { port.closeLock.RLock() defer port.closeLock.RUnlock() - r := &goselect.FDSet{} - r.Set(uintptr(port.handle)) - r.Set(uintptr(port.closeSignal.ReadFD())) - e := &goselect.FDSet{} - e.Set(uintptr(port.handle)) - e.Set(uintptr(port.closeSignal.ReadFD())) - - max := port.closeSignal.ReadFD() - if port.handle > max { - max = port.handle - } - if err = goselect.Select(max+1, r, nil, e, -1); err != nil { + fds := unixutils.NewFDSet(port.handle, port.closeSignal.ReadFD()) + res, err := unixutils.Select(fds, nil, fds, -1) + if err != nil { return 0, err } return syscall.Read(port.handle, p) diff --git a/unixutils/select.go b/unixutils/select.go new file mode 100644 index 0000000..bb73b1b --- /dev/null +++ b/unixutils/select.go @@ -0,0 +1,101 @@ +// +// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +// +build linux darwin freebsd + +package unixutils // "go.bug.st/serial.v1/unixutils" + +import ( + "time" + + "github.com/creack/goselect" +) + +// FDSet is a set of file descriptors suitable for a select call +type FDSet struct { + set goselect.FDSet + max uintptr +} + +// NewFDSet creates a set of file descriptors suitable for a Select call. +func NewFDSet(fds ...int) *FDSet { + s := &FDSet{} + s.Add(fds...) + return s +} + +// Add adds the file descriptors passed as parameter to the FDSet. +func (s *FDSet) Add(fds ...int) { + for _, fd := range fds { + f := uintptr(fd) + s.set.Set(f) + if f > s.max { + s.max = f + } + } +} + +// FDResultSets contains the result of a Select operation. +type FDResultSets struct { + readable *goselect.FDSet + writeable *goselect.FDSet + errors *goselect.FDSet +} + +// IsReadable test if a file descriptor is ready to be read. +func (r *FDResultSets) IsReadable(fd int) bool { + return r.readable.IsSet(uintptr(fd)) +} + +// IsWritable test if a file descriptor is ready to be written. +func (r *FDResultSets) IsWritable(fd int) bool { + return r.writeable.IsSet(uintptr(fd)) +} + +// IsError test if a file descriptor is in error state. +func (r *FDResultSets) IsError(fd int) bool { + return r.errors.IsSet(uintptr(fd)) +} + +// Select performs a select system call, +// file descriptors in the rd set are tested for read-events, +// file descriptors in the wd set are tested for write-events and +// file descriptors in the er set are tested for error-events. +// The function will block until an event happens or the timeout expires. +// The function return an FDResultSets that contains all the file descriptor +// that have a pending read/write/error event. +func Select(rd, wr, er *FDSet, timeout time.Duration) (*FDResultSets, error) { + max := uintptr(0) + res := &FDResultSets{} + if rd != nil { + // fdsets are copied so the parameters are left untouched + copyOfRd := rd.set + res.readable = ©OfRd + // Determine max fd. + max = rd.max + } + if wr != nil { + // fdsets are copied so the parameters are left untouched + copyOfWr := wr.set + res.writeable = ©OfWr + // Determine max fd. + if wr.max > max { + max = wr.max + } + } + if er != nil { + // fdsets are copied so the parameters are left untouched + copyOfEr := er.set + res.errors = ©OfEr + // Determine max fd. + if er.max > max { + max = er.max + } + } + + err := goselect.Select(int(max+1), res.readable, res.writeable, res.errors, timeout) + return res, err +} From d50de5cbafa06d8f9396fd4507b07be65a786d56 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 14 Nov 2016 00:31:01 +0100 Subject: [PATCH 029/181] unix: Fixed race condition while closing serial port. Added PortClosed error --- serial.go | 4 ++++ serial_unix.go | 11 ++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/serial.go b/serial.go index 6b932d0..eb8a301 100644 --- a/serial.go +++ b/serial.go @@ -100,6 +100,8 @@ const ( InvalidStopBits // ErrorEnumeratingPorts an error occurred while listing serial port ErrorEnumeratingPorts + // PortClosed the port has been closed while the operation is in progress + PortClosed ) // EncodedErrorString returns a string explaining the error code @@ -123,6 +125,8 @@ func (e PortError) EncodedErrorString() string { return "Port stop bits invalid or not supported" case ErrorEnumeratingPorts: return "Could not enumerate serial ports" + case PortClosed: + return "Port has been closed" default: return "Other error" } diff --git a/serial_unix.go b/serial_unix.go index 696b3b5..49f5903 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -24,6 +24,7 @@ type unixPort struct { closeLock sync.RWMutex closeSignal *unixutils.Pipe + opened bool } func (port *unixPort) Close() error { @@ -32,6 +33,7 @@ func (port *unixPort) Close() error { if err := syscall.Close(port.handle); err != nil { return err } + port.opened = false if port.closeSignal != nil { // Send close signal to all pending reads (if any) @@ -52,12 +54,18 @@ func (port *unixPort) Close() error { func (port *unixPort) Read(p []byte) (n int, err error) { port.closeLock.RLock() defer port.closeLock.RUnlock() + if !port.opened { + return 0, &PortError{code: PortClosed} + } fds := unixutils.NewFDSet(port.handle, port.closeSignal.ReadFD()) res, err := unixutils.Select(fds, nil, fds, -1) if err != nil { return 0, err } + if res.IsReadable(port.closeSignal.ReadFD()) { + return 0, &PortError{code: PortClosed} + } return syscall.Read(port.handle, p) } @@ -98,6 +106,7 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { } port := &unixPort{ handle: h, + opened: true, } // Setup serial port @@ -127,7 +136,7 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { port.acquireExclusiveAccess() - // This pipe is used as a signal to cancel blocking Read or Write + // This pipe is used as a signal to cancel blocking Read pipe := &unixutils.Pipe{} if err := pipe.Open(); err != nil { port.Close() From 018968a9e6253ae036160d28decea76cad622ec0 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 14 Nov 2016 00:51:48 +0100 Subject: [PATCH 030/181] Fixed travis build --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 461c930..2783c85 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ before_install: script: - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go get golang.org/x/sys/windows - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go build -v ./... - - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go test -c -v ./... + - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go test -c -v go.bug.st/serial.v1 notifications: email: From 5050c501856544bdfc7e145c49347600f133a81a Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 8 Oct 2016 11:15:50 +0200 Subject: [PATCH 031/181] Added USB API --- example_getdetailedportlist_test.go | 29 +++++++++++++++++++++++++++++ serial.go | 23 +++++++++++++++++++++++ usb_darwin.go | 12 ++++++++++++ usb_freebsd.go | 12 ++++++++++++ usb_linux.go | 12 ++++++++++++ usb_windows.go | 12 ++++++++++++ 6 files changed, 100 insertions(+) create mode 100644 example_getdetailedportlist_test.go create mode 100644 usb_darwin.go create mode 100644 usb_freebsd.go create mode 100644 usb_linux.go create mode 100644 usb_windows.go diff --git a/example_getdetailedportlist_test.go b/example_getdetailedportlist_test.go new file mode 100644 index 0000000..1c7a491 --- /dev/null +++ b/example_getdetailedportlist_test.go @@ -0,0 +1,29 @@ +// +// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +package serial_test + +import "fmt" +import "log" +import "go.bug.st/serial.v1" + +func ExampleGetDetailedPortsList() { + ports, err := serial.GetDetailedPortsList() + if err != nil { + log.Fatal(err) + } + if len(ports) == 0 { + fmt.Println("No serial ports found!") + return + } + for _, port := range ports { + fmt.Printf("Found port: %s\n", port.Name) + if port.IsUSB { + fmt.Printf(" USB ID %s:%s\n", port.VID, port.PID) + fmt.Printf(" USB serial %s\n", port.SerialNumber) + } + } +} diff --git a/serial.go b/serial.go index 331db34..91b3332 100644 --- a/serial.go +++ b/serial.go @@ -55,6 +55,25 @@ func GetPortsList() ([]string, error) { return nativeGetPortsList() } +// PortDetails contains detailed information about USB serial port. +// Use GetDetailedPortsList function to retrieve it. +type PortDetails struct { + Name string + IsUSB bool + VID string + PID string + SerialNumber string + Manufacturer string + Product string +} + +// GetDetailedPortsList retrieve ports details like USB VID/PID. +// Please note that this function may not be available on all OS: +// in that case a FunctionNotImplemented error is returned. +func GetDetailedPortsList() ([]*PortDetails, error) { + return nativeGetDetailedPortsList() +} + // Mode describes a serial port configuration. type Mode struct { BaudRate int // The serial port bitrate (aka Baudrate) @@ -121,6 +140,8 @@ const ( ErrorEnumeratingPorts // PortClosed the port has been closed while the operation is in progress PortClosed + // FunctionNotImplemented the requested function is not implemented + FunctionNotImplemented ) // EncodedErrorString returns a string explaining the error code @@ -146,6 +167,8 @@ func (e PortError) EncodedErrorString() string { return "Could not enumerate serial ports" case PortClosed: return "Port has been closed" + case FunctionNotImplemented: + return "Function not implemented" default: return "Other error" } diff --git a/usb_darwin.go b/usb_darwin.go new file mode 100644 index 0000000..8a60a19 --- /dev/null +++ b/usb_darwin.go @@ -0,0 +1,12 @@ +// +// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +package serial // import "go.bug.st/serial.v1" + +func nativeGetDetailedPortsList() ([]*PortDetails, error) { + // TODO + return nil, &PortError{code: FunctionNotImplemented} +} diff --git a/usb_freebsd.go b/usb_freebsd.go new file mode 100644 index 0000000..8a60a19 --- /dev/null +++ b/usb_freebsd.go @@ -0,0 +1,12 @@ +// +// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +package serial // import "go.bug.st/serial.v1" + +func nativeGetDetailedPortsList() ([]*PortDetails, error) { + // TODO + return nil, &PortError{code: FunctionNotImplemented} +} diff --git a/usb_linux.go b/usb_linux.go new file mode 100644 index 0000000..8a60a19 --- /dev/null +++ b/usb_linux.go @@ -0,0 +1,12 @@ +// +// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +package serial // import "go.bug.st/serial.v1" + +func nativeGetDetailedPortsList() ([]*PortDetails, error) { + // TODO + return nil, &PortError{code: FunctionNotImplemented} +} diff --git a/usb_windows.go b/usb_windows.go new file mode 100644 index 0000000..8a60a19 --- /dev/null +++ b/usb_windows.go @@ -0,0 +1,12 @@ +// +// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +package serial // import "go.bug.st/serial.v1" + +func nativeGetDetailedPortsList() ([]*PortDetails, error) { + // TODO + return nil, &PortError{code: FunctionNotImplemented} +} From 2d0a54e6c886e4d0fcea823f799d535f999aa366 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 12 Oct 2016 16:18:58 +0200 Subject: [PATCH 032/181] Added USB discovery for Linux --- usb_linux.go | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 2 deletions(-) diff --git a/usb_linux.go b/usb_linux.go index 8a60a19..35355bb 100644 --- a/usb_linux.go +++ b/usb_linux.go @@ -6,7 +6,102 @@ package serial // import "go.bug.st/serial.v1" +import ( + "bufio" + "fmt" + "os" + "path/filepath" +) + func nativeGetDetailedPortsList() ([]*PortDetails, error) { - // TODO - return nil, &PortError{code: FunctionNotImplemented} + // Retrieve the port list + ports, err := nativeGetPortsList() + if err != nil { + return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + } + + var res []*PortDetails + for _, port := range ports { + details, err := nativeGetPortDetails(port) + if err != nil { + return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + } + res = append(res, details) + } + return res, nil +} + +func nativeGetPortDetails(portPath string) (*PortDetails, error) { + portName := filepath.Base(portPath) + devicePath := fmt.Sprintf("/sys/class/tty/%s/device", portName) + if _, err := os.Stat(devicePath); err != nil { + return &PortDetails{}, nil + } + realDevicePath, err := filepath.EvalSymlinks(devicePath) + if err != nil { + return nil, fmt.Errorf("Can't determine real path of %s: %s", devicePath, err.Error()) + } + subSystemPath, err := filepath.EvalSymlinks(filepath.Join(realDevicePath, "subsystem")) + if err != nil { + return nil, fmt.Errorf("Can't determine real path of %s: %s", filepath.Join(realDevicePath, "subsystem"), err.Error()) + } + subSystem := filepath.Base(subSystemPath) + + result := &PortDetails{Name: portPath} + switch subSystem { + case "usb-serial": + err := parseUSBSysFS(filepath.Dir(filepath.Dir(realDevicePath)), result) + return result, err + case "usb": + err := parseUSBSysFS(filepath.Dir(realDevicePath), result) + return result, err + // TODO: other cases? + default: + return result, nil + } +} + +func parseUSBSysFS(usbDevicePath string, details *PortDetails) error { + vid, err := readLine(filepath.Join(usbDevicePath, "idVendor")) + if err != nil { + return err + } + pid, err := readLine(filepath.Join(usbDevicePath, "idProduct")) + if err != nil { + return err + } + serial, err := readLine(filepath.Join(usbDevicePath, "serial")) + if err != nil { + return err + } + manufacturer, err := readLine(filepath.Join(usbDevicePath, "manufacturer")) + if err != nil { + return err + } + product, err := readLine(filepath.Join(usbDevicePath, "product")) + if err != nil { + return err + } + + details.IsUSB = true + details.VID = vid + details.PID = pid + details.SerialNumber = serial + details.Manufacturer = manufacturer + details.Product = product + return nil +} + +func readLine(filename string) (string, error) { + file, err := os.Open(filename) + if os.IsNotExist(err) { + return "", nil + } + if err != nil { + return "", err + } + defer file.Close() + reader := bufio.NewReader(file) + line, _, err := reader.ReadLine() + return string(line), err } From ad7966a4346876652598745876d0f6412c3935f8 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 22 Oct 2016 18:36:35 +0200 Subject: [PATCH 033/181] USB detection for Windows (based on setupapi) --- .travis.yml | 1 + serial_windows.go | 2 +- syscall_windows.go | 109 ++++++++++++++++- usb_windows.go | 291 +++++++++++++++++++++++++++++++++++++++++++- usb_windows_test.go | 49 ++++++++ 5 files changed, 443 insertions(+), 9 deletions(-) create mode 100644 usb_windows_test.go diff --git a/.travis.yml b/.travis.yml index 2783c85..3ad5c64 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,7 @@ matrix: before_install: script: + - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go get github.com/stretchr/testify/require - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go get golang.org/x/sys/windows - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go build -v ./... - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go test -c -v go.bug.st/serial.v1 diff --git a/serial_windows.go b/serial_windows.go index ed04e6b..500e85a 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -23,7 +23,7 @@ type windowsPort struct { handle syscall.Handle } -//go:generate go run extras/mksyscall_windows.go -output syscall_windows.go serial_windows.go +//go:generate go run extras/mksyscall_windows.go -output syscall_windows.go serial_windows.go usb_windows.go //sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) = advapi32.RegEnumValueW diff --git a/syscall_windows.go b/syscall_windows.go index dfa3f84..add2cce 100644 --- a/syscall_windows.go +++ b/syscall_windows.go @@ -14,13 +14,21 @@ var _ unsafe.Pointer var ( modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + modsetupapi = windows.NewLazySystemDLL("setupapi.dll") - procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") - procGetCommState = modkernel32.NewProc("GetCommState") - procSetCommState = modkernel32.NewProc("SetCommState") - procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") - procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction") - procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus") + procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") + procGetCommState = modkernel32.NewProc("GetCommState") + procSetCommState = modkernel32.NewProc("SetCommState") + procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") + procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction") + procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus") + procSetupDiClassGuidsFromNameW = modsetupapi.NewProc("SetupDiClassGuidsFromNameW") + procSetupDiGetClassDevsW = modsetupapi.NewProc("SetupDiGetClassDevsW") + procSetupDiDestroyDeviceInfoList = modsetupapi.NewProc("SetupDiDestroyDeviceInfoList") + procSetupDiEnumDeviceInfo = modsetupapi.NewProc("SetupDiEnumDeviceInfo") + procSetupDiGetDeviceInstanceIdW = modsetupapi.NewProc("SetupDiGetDeviceInstanceIdW") + procSetupDiOpenDevRegKey = modsetupapi.NewProc("SetupDiOpenDevRegKey") + procSetupDiGetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiGetDeviceRegistryPropertyW") ) func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) { @@ -78,3 +86,92 @@ func getCommModemStatus(handle syscall.Handle, bits *uint32) (res bool) { res = r0 != 0 return } + +func setupDiClassGuidsFromNameInternal(class string, guid *guid, guidSize uint32, requiredSize *uint32) (err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(class) + if err != nil { + return + } + return _setupDiClassGuidsFromNameInternal(_p0, guid, guidSize, requiredSize) +} + +func _setupDiClassGuidsFromNameInternal(class *uint16, guid *guid, guidSize uint32, requiredSize *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procSetupDiClassGuidsFromNameW.Addr(), 4, uintptr(unsafe.Pointer(class)), uintptr(unsafe.Pointer(guid)), uintptr(guidSize), uintptr(unsafe.Pointer(requiredSize)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setupDiGetClassDevs(guid *guid, enumerator *string, hwndParent uintptr, flags uint32) (set devicesSet, err error) { + r0, _, e1 := syscall.Syscall6(procSetupDiGetClassDevsW.Addr(), 4, uintptr(unsafe.Pointer(guid)), uintptr(unsafe.Pointer(enumerator)), uintptr(hwndParent), uintptr(flags), 0, 0) + set = devicesSet(r0) + if set == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setupDiDestroyDeviceInfoList(set devicesSet) (err error) { + r1, _, e1 := syscall.Syscall(procSetupDiDestroyDeviceInfoList.Addr(), 1, uintptr(set), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setupDiEnumDeviceInfo(set devicesSet, index uint32, info *devInfoData) (err error) { + r1, _, e1 := syscall.Syscall(procSetupDiEnumDeviceInfo.Addr(), 3, uintptr(set), uintptr(index), uintptr(unsafe.Pointer(info))) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setupDiGetDeviceInstanceId(set devicesSet, devInfo *devInfoData, devInstanceId unsafe.Pointer, devInstanceIdSize uint32, requiredSize *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procSetupDiGetDeviceInstanceIdW.Addr(), 5, uintptr(set), uintptr(unsafe.Pointer(devInfo)), uintptr(devInstanceId), uintptr(devInstanceIdSize), uintptr(unsafe.Pointer(requiredSize)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setupDiOpenDevRegKey(set devicesSet, devInfo *devInfoData, scope dicsScope, hwProfile uint32, keyType uint32, samDesired regsam) (hkey syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall6(procSetupDiOpenDevRegKey.Addr(), 6, uintptr(set), uintptr(unsafe.Pointer(devInfo)), uintptr(scope), uintptr(hwProfile), uintptr(keyType), uintptr(samDesired)) + hkey = syscall.Handle(r0) + if hkey == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setupDiGetDeviceRegistryProperty(set devicesSet, devInfo *devInfoData, property deviceProperty, propertyType *uint32, outValue *byte, outSize *uint32, reqSize *uint32) (res bool) { + r0, _, _ := syscall.Syscall9(procSetupDiGetDeviceRegistryPropertyW.Addr(), 7, uintptr(set), uintptr(unsafe.Pointer(devInfo)), uintptr(property), uintptr(unsafe.Pointer(propertyType)), uintptr(unsafe.Pointer(outValue)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(reqSize)), 0, 0) + res = r0 != 0 + return +} diff --git a/usb_windows.go b/usb_windows.go index 8a60a19..32d78e2 100644 --- a/usb_windows.go +++ b/usb_windows.go @@ -6,7 +6,294 @@ package serial // import "go.bug.st/serial.v1" +import ( + "fmt" + "regexp" + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +func parseDeviceID(deviceID string, details *PortDetails) { + // Windows stock USB-CDC driver + if len(deviceID) >= 3 && deviceID[:3] == "USB" { + re := regexp.MustCompile("VID_(....)&PID_(....)(\\\\(\\w+)$)?").FindAllStringSubmatch(deviceID, -1) + if re == nil || len(re[0]) < 2 { + // Silently ignore unparsable strings + return + } + details.IsUSB = true + details.VID = re[0][1] + details.PID = re[0][2] + if len(re[0]) >= 4 { + details.SerialNumber = re[0][4] + } + return + } + + // FTDI driver + if len(deviceID) >= 7 && deviceID[:7] == "FTDIBUS" { + re := regexp.MustCompile("VID_(....)\\+PID_(....)(\\+(\\w+))?").FindAllStringSubmatch(deviceID, -1) + if re == nil || len(re[0]) < 2 { + // Silently ignore unparsable strings + return + } + details.IsUSB = true + details.VID = re[0][1] + details.PID = re[0][2] + if len(re[0]) >= 4 { + details.SerialNumber = re[0][4] + } + return + } + + // Other unidentified device type +} + +//sys setupDiClassGuidsFromNameInternal(class string, guid *guid, guidSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiClassGuidsFromNameW +//sys setupDiGetClassDevs(guid *guid, enumerator *string, hwndParent uintptr, flags uint32) (set devicesSet, err error) = setupapi.SetupDiGetClassDevsW +//sys setupDiDestroyDeviceInfoList(set devicesSet) (err error) = setupapi.SetupDiDestroyDeviceInfoList +//sys setupDiEnumDeviceInfo(set devicesSet, index uint32, info *devInfoData) (err error) = setupapi.SetupDiEnumDeviceInfo +//sys setupDiGetDeviceInstanceId(set devicesSet, devInfo *devInfoData, devInstanceId unsafe.Pointer, devInstanceIdSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiGetDeviceInstanceIdW +//sys setupDiOpenDevRegKey(set devicesSet, devInfo *devInfoData, scope dicsScope, hwProfile uint32, keyType uint32, samDesired regsam) (hkey syscall.Handle, err error) = setupapi.SetupDiOpenDevRegKey +//sys setupDiGetDeviceRegistryProperty(set devicesSet, devInfo *devInfoData, property deviceProperty, propertyType *uint32, outValue *byte, outSize *uint32, reqSize *uint32) (res bool) = setupapi.SetupDiGetDeviceRegistryPropertyW + +// Device registry property codes +// (Codes marked as read-only (R) may only be used for +// SetupDiGetDeviceRegistryProperty) +// +// These values should cover the same set of registry properties +// as defined by the CM_DRP codes in cfgmgr32.h. +// +// Note that SPDRP codes are zero based while CM_DRP codes are one based! +type deviceProperty uint32 + +const ( + spdrpDeviceDesc deviceProperty = 0x00000000 // DeviceDesc = R/W + spdrpHardwareID = 0x00000001 // HardwareID = R/W + spdrpCompatibleIDS = 0x00000002 // CompatibleIDs = R/W + spdrpUnused0 = 0x00000003 // Unused + spdrpService = 0x00000004 // Service = R/W + spdrpUnused1 = 0x00000005 // Unused + spdrpUnused2 = 0x00000006 // Unused + spdrpClass = 0x00000007 // Class = R--tied to ClassGUID + spdrpClassGUID = 0x00000008 // ClassGUID = R/W + spdrpDriver = 0x00000009 // Driver = R/W + spdrpConfigFlags = 0x0000000A // ConfigFlags = R/W + spdrpMFG = 0x0000000B // Mfg = R/W + spdrpFriendlyName = 0x0000000C // FriendlyName = R/W + spdrpLocationIinformation = 0x0000000D // LocationInformation = R/W + spdrpPhysicalDeviceObjectName = 0x0000000E // PhysicalDeviceObjectName = R + spdrpCapabilities = 0x0000000F // Capabilities = R + spdrpUINumber = 0x00000010 // UiNumber = R + spdrpUpperFilters = 0x00000011 // UpperFilters = R/W + spdrpLowerFilters = 0x00000012 // LowerFilters = R/W + spdrpBusTypeGUID = 0x00000013 // BusTypeGUID = R + spdrpLegactBusType = 0x00000014 // LegacyBusType = R + spdrpBusNumber = 0x00000015 // BusNumber = R + spdrpEnumeratorName = 0x00000016 // Enumerator Name = R + spdrpSecurity = 0x00000017 // Security = R/W, binary form + spdrpSecuritySDS = 0x00000018 // Security = W, SDS form + spdrpDevType = 0x00000019 // Device Type = R/W + spdrpExclusive = 0x0000001A // Device is exclusive-access = R/W + spdrpCharacteristics = 0x0000001B // Device Characteristics = R/W + spdrpAddress = 0x0000001C // Device Address = R + spdrpUINumberDescFormat = 0X0000001D // UiNumberDescFormat = R/W + spdrpDevicePowerData = 0x0000001E // Device Power Data = R + spdrpRemovalPolicy = 0x0000001F // Removal Policy = R + spdrpRemovalPolicyHWDefault = 0x00000020 // Hardware Removal Policy = R + spdrpRemovalPolicyOverride = 0x00000021 // Removal Policy Override = RW + spdrpInstallState = 0x00000022 // Device Install State = R + spdrpLocationPaths = 0x00000023 // Device Location Paths = R + spdrpBaseContainerID = 0x00000024 // Base ContainerID = R + + spdrpMaximumProperty = 0x00000025 // Upper bound on ordinals +) + +// Values specifying the scope of a device property change +type dicsScope uint32 + +const ( + dicsFlagGlobal dicsScope = 0x00000001 // make change in all hardware profiles + dicsFlagConfigSspecific = 0x00000002 // make change in specified profile only + dicsFlagConfigGeneral = 0x00000004 // 1 or more hardware profile-specific +) + +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724878(v=vs.85).aspx +type regsam uint32 + +const ( + keyAllAccess regsam = 0xF003F + keyCreateLink = 0x00020 + keyCreateSubKey = 0x00004 + keyEnumerateSubKeys = 0x00008 + keyExecute = 0x20019 + keyNotify = 0x00010 + keyQueryValue = 0x00001 + keyRead = 0x20019 + keySetValue = 0x00002 + keyWOW64_32key = 0x00200 + keyWOW64_64key = 0x00100 + keyWrite = 0x20006 +) + +// KeyType values for SetupDiCreateDevRegKey, SetupDiOpenDevRegKey, and +// SetupDiDeleteDevRegKey. +const ( + diregDev = 0x00000001 // Open/Create/Delete device key + diregDrv = 0x00000002 // Open/Create/Delete driver key + diregBoth = 0x00000004 // Delete both driver and Device key +) + +// https://msdn.microsoft.com/it-it/library/windows/desktop/aa373931(v=vs.85).aspx +type guid struct { + data1 uint32 + data2 uint16 + data3 uint16 + data4 [8]byte +} + +func (g guid) String() string { + return fmt.Sprintf("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + g.data1, g.data2, g.data3, + g.data4[0], g.data4[1], g.data4[2], g.data4[3], + g.data4[4], g.data4[5], g.data4[6], g.data4[7]) +} + +func classGuidsFromName(className string) ([]guid, error) { + // Determine the number of GUIDs for className + n := uint32(0) + if err := setupDiClassGuidsFromNameInternal(className, nil, 0, &n); err != nil { + // ignore error: UIDs array size too small + } + + res := make([]guid, n) + err := setupDiClassGuidsFromNameInternal(className, &res[0], n, &n) + return res, err +} + +const ( + digcfDefault = 0x00000001 // only valid with digcfDeviceInterface + digcfPresent = 0x00000002 + digcfAllClasses = 0x00000004 + digcfProfile = 0x00000008 + digcfDeviceInterface = 0x00000010 +) + +type devicesSet syscall.Handle + +func (g *guid) getDevicesSet() (devicesSet, error) { + return setupDiGetClassDevs(g, nil, 0, digcfPresent) +} + +func (set devicesSet) destroy() { + setupDiDestroyDeviceInfoList(set) +} + +// https://msdn.microsoft.com/en-us/library/windows/hardware/ff552344(v=vs.85).aspx +type devInfoData struct { + size uint32 + guid guid + devInst uint32 + reserved uintptr +} + +type deviceInfo struct { + set devicesSet + data devInfoData +} + +func (set devicesSet) getDeviceInfo(index int) (*deviceInfo, error) { + result := &deviceInfo{set: set} + + result.data.size = uint32(unsafe.Sizeof(result.data)) + err := setupDiEnumDeviceInfo(set, uint32(index), &result.data) + return result, err +} + +func (dev *deviceInfo) getInstanceID() (string, error) { + n := uint32(0) + setupDiGetDeviceInstanceId(dev.set, &dev.data, nil, 0, &n) + buff := make([]uint16, n) + if err := setupDiGetDeviceInstanceId(dev.set, &dev.data, unsafe.Pointer(&buff[0]), uint32(len(buff)), &n); err != nil { + return "", err + } + return windows.UTF16ToString(buff[:]), nil +} + +func (dev *deviceInfo) openDevRegKey(scope dicsScope, hwProfile uint32, keyType uint32, samDesired regsam) (syscall.Handle, error) { + return setupDiOpenDevRegKey(dev.set, &dev.data, scope, hwProfile, keyType, samDesired) +} + func nativeGetDetailedPortsList() ([]*PortDetails, error) { - // TODO - return nil, &PortError{code: FunctionNotImplemented} + guids, err := classGuidsFromName("Ports") + if err != nil { + return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + } + + var res []*PortDetails + for _, g := range guids { + devsSet, err := g.getDevicesSet() + if err != nil { + return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + } + defer devsSet.destroy() + + for i := 0; ; i++ { + device, err := devsSet.getDeviceInfo(i) + if err != nil { + break + } + details := &PortDetails{} + portName, err := retrievePortNameFromDevInfo(device) + if err != nil { + continue + } + if len(portName) < 3 || portName[0:3] != "COM" { + // Accept only COM ports + continue + } + details.Name = portName + + if err := retrievePortDetailsFromDevInfo(device, details); err != nil { + return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + } + res = append(res, details) + } + } + return res, nil +} + +func retrievePortNameFromDevInfo(device *deviceInfo) (string, error) { + h, err := device.openDevRegKey(dicsFlagGlobal, 0, diregDev, keyRead) + if err != nil { + return "", err + } + defer syscall.RegCloseKey(h) + + var name [1024]uint16 + nameP := (*byte)(unsafe.Pointer(&name[0])) + nameSize := uint32(len(name) * 2) + if err := syscall.RegQueryValueEx(h, syscall.StringToUTF16Ptr("PortName"), nil, nil, nameP, &nameSize); err != nil { + return "", err + } + return syscall.UTF16ToString(name[:]), nil +} + +func retrievePortDetailsFromDevInfo(device *deviceInfo, details *PortDetails) error { + deviceID, err := device.getInstanceID() + if err != nil { + return err + } + parseDeviceID(deviceID, details) + + var friendlyName [1024]uint16 + friendlyNameP := (*byte)(unsafe.Pointer(&friendlyName[0])) + friendlyNameSize := uint32(len(friendlyName) * 2) + if setupDiGetDeviceRegistryProperty(device.set, &device.data, spdrpDeviceDesc /* spdrpFriendlyName */, nil, friendlyNameP, &friendlyNameSize, nil) { + details.Product = syscall.UTF16ToString(friendlyName[:]) + } + + return nil } diff --git a/usb_windows_test.go b/usb_windows_test.go new file mode 100644 index 0000000..ee58d47 --- /dev/null +++ b/usb_windows_test.go @@ -0,0 +1,49 @@ +// +// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +package serial // import "go.bug.st/serial.v1" + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func parseAndReturnDeviceID(deviceID string) *PortDetails { + res := &PortDetails{} + parseDeviceID(deviceID, res) + return res +} + +func TestParseDeviceID(t *testing.T) { + r := require.New(t) + test := func(deviceId, vid, pid, serialNo string) { + res := parseAndReturnDeviceID(deviceId) + r.True(res.IsUSB) + r.Equal(vid, res.VID) + r.Equal(pid, res.PID) + r.Equal(serialNo, res.SerialNumber) + } + + test("FTDIBUS\\VID_0403+PID_6001+A6004CCFA\\0000", "0403", "6001", "A6004CCFA") + test("USB\\VID_16C0&PID_0483\\12345", "16C0", "0483", "12345") + test("USB\\VID_2341&PID_0000\\64936333936351400000", "2341", "0000", "64936333936351400000") + test("USB\\VID_2341&PID_0000\\6493234373835191F1F1", "2341", "0000", "6493234373835191F1F1") + test("USB\\VID_2341&PID_804E&MI_00\\6&279A3900&0&0000", "2341", "804E", "") + test("USB\\VID_2341&PID_004E\\5&C3DC240&0&1", "2341", "004E", "") + test("USB\\VID_03EB&PID_2111&MI_01\\6&21F3553F&0&0001", "03EB", "2111", "") // Atmel EDBG + test("USB\\VID_2341&PID_804D&MI_00\\6&1026E213&0&0000", "2341", "804D", "") + test("USB\\VID_2341&PID_004D\\5&C3DC240&0&1", "2341", "004D", "") + test("USB\\VID_067B&PID_2303\\6&2C4CB384&0&3", "067B", "2303", "") // PL2303 +} + +func TestParseDeviceIDWithInvalidStrings(t *testing.T) { + r := require.New(t) + res := parseAndReturnDeviceID("ABC") + r.False(res.IsUSB) + res2 := parseAndReturnDeviceID("USB") + r.False(res2.IsUSB) +} From 3d6dd00ccbd524165fcd48da75ceb6463d31344c Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 22 Oct 2016 18:55:05 +0200 Subject: [PATCH 034/181] USB detection for Windows (alternative implementation with OLE) --- usb_ole_windows.go | 113 +++++++++++++++++++++++++++++++++++++++++++++ usb_windows.go | 3 ++ 2 files changed, 116 insertions(+) create mode 100644 usb_ole_windows.go diff --git a/usb_ole_windows.go b/usb_ole_windows.go new file mode 100644 index 0000000..128aa86 --- /dev/null +++ b/usb_ole_windows.go @@ -0,0 +1,113 @@ +// +// Copyright 2014-2016 Lars Knudsen, Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +// +build ignore + +package serial + +import ( + "log" + "regexp" + + ole "github.com/go-ole/go-ole" + "github.com/go-ole/go-ole/oleutil" +) + +func init() { + err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED) + if err != nil { + log.Fatal("Init error: ", err) + } +} + +func nativeGetDetailedPortsList() ([]*PortDetails, error) { + unknown, err := oleutil.CreateObject("WbemScripting.SWbemLocator") + if err != nil { + return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + } + defer unknown.Release() + + wmi, err := unknown.QueryInterface(ole.IID_IDispatch) + if err != nil { + return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + } + defer wmi.Release() + + serviceRaw, err := wmi.CallMethod("ConnectServer") + if err != nil { + return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + } + service := serviceRaw.ToIDispatch() + defer service.Release() + + query := "SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0 and Name like '%(COM%'" + queryResult, err := oleutil.CallMethod(service, "ExecQuery", query) + if err != nil { + return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + } + result := queryResult.ToIDispatch() + defer result.Release() + + countVar, err := result.GetProperty("Count") + if err != nil { + return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + } + count := int(countVar.Val) + + res := []*PortDetails{} + + // Retrieve all items + for i := 0; i < count; i++ { + itemRaw, err := result.CallMethod("ItemIndex", i) + if err != nil { + return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + } + item := itemRaw.ToIDispatch() + defer item.Release() + + detail := &PortDetails{} + if err := getPortDetails(item, detail); err != nil { + return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + } + // SerialPort{Path: path, VendorId: VID, ProductId: PID, DisplayName: displayName.ToString()} + res = append(res, detail) + } + + return res, nil +} + +func getPortDetails(item *ole.IDispatch, res *PortDetails) error { + // Find port name + itemName, err := item.GetProperty("Name") + if err != nil { + return err + } + re := regexp.MustCompile("\\((COM[0-9]+)\\)").FindAllStringSubmatch(itemName.ToString(), 1) + if re == nil || len(re[0]) < 2 { + // Discard items that are not serial ports + return nil + } + res.Name = re[0][1] + + //itemPnPDeviceID, err := item.GetProperty("PnPDeviceID") + //if err != nil { + // return err + //} + //PnPDeviceID := itemPnPDeviceID.ToString() + + itemDeviceID, err := item.GetProperty("DeviceID") + if err != nil { + return err + } + parseDeviceID(itemDeviceID.ToString(), res) + + itemManufacturer, err := item.GetProperty("Product") + if err != nil { + return err + } + res.Manufacturer = itemManufacturer.ToString() + return nil +} diff --git a/usb_windows.go b/usb_windows.go index 32d78e2..c053297 100644 --- a/usb_windows.go +++ b/usb_windows.go @@ -51,6 +51,9 @@ func parseDeviceID(deviceID string, details *PortDetails) { // Other unidentified device type } +// setupapi based +// -------------- + //sys setupDiClassGuidsFromNameInternal(class string, guid *guid, guidSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiClassGuidsFromNameW //sys setupDiGetClassDevs(guid *guid, enumerator *string, hwndParent uintptr, flags uint32) (set devicesSet, err error) = setupapi.SetupDiGetClassDevsW //sys setupDiDestroyDeviceInfoList(set devicesSet) (err error) = setupapi.SetupDiDestroyDeviceInfoList From 5769f9680411996fc4e01d881f5db27d0c365bab Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 11 Dec 2016 23:46:15 +0100 Subject: [PATCH 035/181] Temporarily remove USB manufacturer/product info I need to find a better way to obtain those infos. --- serial.go | 5 +++-- usb_linux.go | 20 ++++++++++---------- usb_windows.go | 2 +- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/serial.go b/serial.go index 91b3332..54a1703 100644 --- a/serial.go +++ b/serial.go @@ -63,8 +63,9 @@ type PortDetails struct { VID string PID string SerialNumber string - Manufacturer string - Product string + + // Manufacturer string + // Product string } // GetDetailedPortsList retrieve ports details like USB VID/PID. diff --git a/usb_linux.go b/usb_linux.go index 35355bb..c671140 100644 --- a/usb_linux.go +++ b/usb_linux.go @@ -74,21 +74,21 @@ func parseUSBSysFS(usbDevicePath string, details *PortDetails) error { if err != nil { return err } - manufacturer, err := readLine(filepath.Join(usbDevicePath, "manufacturer")) - if err != nil { - return err - } - product, err := readLine(filepath.Join(usbDevicePath, "product")) - if err != nil { - return err - } + //manufacturer, err := readLine(filepath.Join(usbDevicePath, "manufacturer")) + //if err != nil { + // return err + //} + //product, err := readLine(filepath.Join(usbDevicePath, "product")) + //if err != nil { + // return err + //} details.IsUSB = true details.VID = vid details.PID = pid details.SerialNumber = serial - details.Manufacturer = manufacturer - details.Product = product + //details.Manufacturer = manufacturer + //details.Product = product return nil } diff --git a/usb_windows.go b/usb_windows.go index c053297..1e3a6c4 100644 --- a/usb_windows.go +++ b/usb_windows.go @@ -295,7 +295,7 @@ func retrievePortDetailsFromDevInfo(device *deviceInfo, details *PortDetails) er friendlyNameP := (*byte)(unsafe.Pointer(&friendlyName[0])) friendlyNameSize := uint32(len(friendlyName) * 2) if setupDiGetDeviceRegistryProperty(device.set, &device.data, spdrpDeviceDesc /* spdrpFriendlyName */, nil, friendlyNameP, &friendlyNameSize, nil) { - details.Product = syscall.UTF16ToString(friendlyName[:]) + //details.Product = syscall.UTF16ToString(friendlyName[:]) } return nil From ecd31dc766f224bbc809ebc859fe0a4dd3ae47ea Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 13 Dec 2016 23:15:12 +0100 Subject: [PATCH 036/181] Added USB VID/PID detection for osx --- usb_darwin.go | 196 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 194 insertions(+), 2 deletions(-) diff --git a/usb_darwin.go b/usb_darwin.go index 8a60a19..9b5c16c 100644 --- a/usb_darwin.go +++ b/usb_darwin.go @@ -6,7 +6,199 @@ package serial // import "go.bug.st/serial.v1" +// #cgo LDFLAGS: -framework CoreFoundation -framework IOKit -fconstant-cfstrings +// #include +// #include +// #include +import "C" +import ( + "errors" + "fmt" + "unsafe" +) + func nativeGetDetailedPortsList() ([]*PortDetails, error) { - // TODO - return nil, &PortError{code: FunctionNotImplemented} + var ports []*PortDetails + + services, err := getAllServices("IOSerialBSDClient") + if err != nil { + return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + } + for _, service := range services { + defer service.Release() + + port, err := extractPortInfo(C.io_registry_entry_t(service)) + if err != nil { + return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + } + ports = append(ports, port) + } + return ports, nil +} + +func extractPortInfo(service C.io_registry_entry_t) (*PortDetails, error) { + name, err := service.GetStringProperty("IOCalloutDevice") + if err != nil { + return nil, fmt.Errorf("Error extracting port info from device: %s", err.Error()) + } + port := &PortDetails{} + port.Name = name + port.IsUSB = false + + usbDevice := service + for usbDevice.GetClass() != "IOUSBDevice" { + if usbDevice, err = usbDevice.GetParent("IOService"); err != nil { + break + } + } + if err == nil { + // It's an IOUSBDevice + vid, _ := usbDevice.GetIntProperty("idVendor", C.kCFNumberSInt16Type) + pid, _ := usbDevice.GetIntProperty("idProduct", C.kCFNumberSInt16Type) + serialNumber, _ := usbDevice.GetStringProperty("USB Serial Number") + //product, _ := usbDevice.GetStringProperty("USB Product Name") + //manufacturer, _ := usbDevice.GetStringProperty("USB Vendor Name") + //fmt.Println(product + " - " + manufacturer) + + port.IsUSB = true + port.VID = fmt.Sprintf("%04X", vid) + port.PID = fmt.Sprintf("%04X", pid) + port.SerialNumber = serialNumber + } + return port, nil +} + +func getAllServices(serviceType string) ([]C.io_object_t, error) { + i, err := getMatchingServices(serviceMatching(serviceType)) + if err != nil { + return nil, err + } + defer i.Release() + + var services []C.io_object_t + tries := 0 + for tries < 5 { + // Extract all elements from iterator + if service, ok := i.Next(); ok { + services = append(services, service) + continue + } + // If iterator is still valid return the result + if i.IsValid() { + return services, nil + } + // Otherwise empty the result and retry + for _, s := range services { + s.Release() + } + services = []C.io_object_t{} + i.Reset() + tries++ + } + // Give up if the iteration continues to fail... + return nil, fmt.Errorf("IOServiceGetMatchingServices failed, data changed while iterating") +} + +// serviceMatching create a matching dictionary that specifies an IOService class match. +func serviceMatching(serviceType string) C.CFMutableDictionaryRef { + t := C.CString(serviceType) + defer C.free(unsafe.Pointer(t)) + return C.IOServiceMatching(t) +} + +// getMatchingServices look up registered IOService objects that match a matching dictionary. +func getMatchingServices(matcher C.CFMutableDictionaryRef) (C.io_iterator_t, error) { + var i C.io_iterator_t + err := C.IOServiceGetMatchingServices(C.kIOMasterPortDefault, matcher, &i) + if err != C.KERN_SUCCESS { + return 0, fmt.Errorf("IOServiceGetMatchingServices failed (code %d)", err) + } + return i, nil +} + +// CFStringRef + +func cfStringCreateWithString(s string) C.CFStringRef { + c := C.CString(s) + defer C.free(unsafe.Pointer(c)) + return C.CFStringCreateWithCString( + C.kCFAllocatorDefault, c, C.kCFStringEncodingMacRoman) +} + +// io_registry_entry_t + +func (me *C.io_registry_entry_t) GetParent(plane string) (C.io_registry_entry_t, error) { + cPlane := C.CString(plane) + defer C.free(unsafe.Pointer(cPlane)) + var parent C.io_registry_entry_t + err := C.IORegistryEntryGetParentEntry(*me, cPlane, &parent) + if err != 0 { + return 0, errors.New("No parent device available") + } + return parent, nil +} + +func (me *C.io_registry_entry_t) GetClass() string { + obj := (*C.io_object_t)(me) + return obj.GetClass() +} + +func (me *C.io_registry_entry_t) GetStringProperty(key string) (string, error) { + k := cfStringCreateWithString(key) + defer C.CFRelease(C.CFTypeRef(k)) + property := C.IORegistryEntryCreateCFProperty(*me, k, C.kCFAllocatorDefault, 0) + if property == nil { + return "", errors.New("Property not found: " + key) + } + defer C.CFRelease(property) + return C.GoString(C.CFStringGetCStringPtr(property, 0)), nil +} + +func (me *C.io_registry_entry_t) GetIntProperty(key string, intType C.CFNumberType) (int, error) { + k := cfStringCreateWithString(key) + defer C.CFRelease(C.CFTypeRef(k)) + property := C.IORegistryEntryCreateCFProperty(*me, k, C.kCFAllocatorDefault, 0) + if property == nil { + return 0, errors.New("Property not found: " + key) + } + defer C.CFRelease(property) + var res int + C.CFNumberGetValue(property, intType, unsafe.Pointer(&res)) + return res, nil +} + +// io_iterator_t + +// IsValid checks if an iterator is still valid. +// Some iterators will be made invalid if changes are made to the +// structure they are iterating over. This function checks the iterator +// is still valid and should be called when Next returns zero. +// An invalid iterator can be Reset and the iteration restarted. +func (me *C.io_iterator_t) IsValid() bool { + return C.IOIteratorIsValid(*me) == C.true +} + +func (me *C.io_iterator_t) Reset() { + C.IOIteratorReset(*me) +} + +func (me *C.io_iterator_t) Next() (C.io_object_t, bool) { + res := C.IOIteratorNext(*me) + return res, res != 0 +} + +func (me *C.io_iterator_t) Release() { + C.IOObjectRelease(C.io_object_t(*me)) +} + +// io_object_t + +func (me *C.io_object_t) Release() { + C.IOObjectRelease(*me) +} + +func (me *C.io_object_t) GetClass() string { + class := make([]C.char, 1024) + C.IOObjectGetClass(*me, &class[0]) + return C.GoString(&class[0]) } From b1f15cfb64808127de9bc257f0dfac01cc82b22f Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 25 Dec 2016 18:26:18 +0100 Subject: [PATCH 037/181] Improved error checking on osx USB detection --- usb_darwin.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/usb_darwin.go b/usb_darwin.go index 9b5c16c..fa63667 100644 --- a/usb_darwin.go +++ b/usb_darwin.go @@ -151,7 +151,17 @@ func (me *C.io_registry_entry_t) GetStringProperty(key string) (string, error) { return "", errors.New("Property not found: " + key) } defer C.CFRelease(property) - return C.GoString(C.CFStringGetCStringPtr(property, 0)), nil + + if ptr := C.CFStringGetCStringPtr(property, 0); ptr != nil { + return C.GoString(ptr), nil + } + // in certain circumstances CFStringGetCStringPtr may return NULL + // and we must retrieve the string by copy + buff := make([]C.char, 1024) + if C.CFStringGetCString(property, &buff[0], 1024, 0) != C.true { + return "", fmt.Errorf("Property '%s' can't be converted", key) + } + return C.GoString(&buff[0]), nil } func (me *C.io_registry_entry_t) GetIntProperty(key string, intType C.CFNumberType) (int, error) { @@ -163,7 +173,9 @@ func (me *C.io_registry_entry_t) GetIntProperty(key string, intType C.CFNumberTy } defer C.CFRelease(property) var res int - C.CFNumberGetValue(property, intType, unsafe.Pointer(&res)) + if C.CFNumberGetValue(property, intType, unsafe.Pointer(&res)) != C.true { + return res, fmt.Errorf("Property '%s' can't be converted or has been truncated", key) + } return res, nil } From 9d2bfefb78ee4f80c1a86676c546f6af416a045d Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 25 Dec 2016 18:46:04 +0100 Subject: [PATCH 038/181] Updated documentation (USB detection) Added a section for USB detection. cgo and "C" package are now used (to access osx frameworks). --- doc.go | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/doc.go b/doc.go index 647e88f..5d9bc6f 100644 --- a/doc.go +++ b/doc.go @@ -79,7 +79,26 @@ serial port: fmt.Printf("%v", string(buff[:n])) } -This library doesn't make use of cgo and "C" package, so it's a pure go library -that can be easily cross compiled. +If a port is a virutal USB-CDC serial port (an USB-to-RS232 cable or a +microcontroller development board are typical examples) is possible to +retrieve the USB metadata, like VID/PID or USB Serial Number, with the +GetDetailedPortsList function: + + ports, err := serial.GetDetailedPortsList() + if err != nil { + log.Fatal(err) + } + if len(ports) == 0 { + fmt.Println("No serial ports found!") + return + } + for _, port := range ports { + fmt.Printf("Found port: %s\n", port.Name) + if port.IsUSB { + fmt.Printf(" USB ID %s:%s\n", port.VID, port.PID) + fmt.Printf(" USB serial %s\n", port.SerialNumber) + } + } + */ package serial // import "go.bug.st/serial.v1" From 69c094e3c77377d3829f35f4cc9bba60fffa736d Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 26 Dec 2016 13:01:56 +0100 Subject: [PATCH 039/181] Fix typo in doc.go --- doc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc.go b/doc.go index 647e88f..75d8615 100644 --- a/doc.go +++ b/doc.go @@ -12,7 +12,7 @@ is the following: import "go.bug.st/serial.v1" -It is possibile to get the list of available serial ports with the +It is possible to get the list of available serial ports with the GetPortsList function: ports, err := serial.GetPortsList() From ff9cf2b84bddd6f2c55c1940683d17b6b3666716 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 20 Jan 2017 02:37:06 +0200 Subject: [PATCH 040/181] Windows: use OVERLAPPED operations --- serial_windows.go | 43 ++++++++++++++++++++++++++-- syscall_windows.go | 70 ++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 104 insertions(+), 9 deletions(-) diff --git a/serial_windows.go b/serial_windows.go index ed04e6b..68f7f76 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -65,13 +65,32 @@ func (port *windowsPort) Close() error { func (port *windowsPort) Read(p []byte) (int, error) { var readed uint32 params := &dcb{} + ev, err := createOverlappedEvent() + if err != nil { + return 0, err + } + defer syscall.CloseHandle(ev.HEvent) for { - if err := syscall.ReadFile(port.handle, p, &readed, nil); err != nil { + err := syscall.ReadFile(port.handle, p, &readed, ev) + switch err { + case nil: + // operation completed successfully + case syscall.ERROR_IO_PENDING: + // wait for overlapped I/O to complete + if err := getOverlappedResult(port.handle, ev, &readed, true); err != nil { + return int(readed), err + } + default: + // error happened return int(readed), err } + if readed > 0 { return int(readed), nil } + if err := resetEvent(ev.HEvent); err != nil { + return 0, err + } // At the moment it seems that the only reliable way to check if // a serial port is alive in Windows is to check if the SetCommState @@ -87,7 +106,16 @@ func (port *windowsPort) Read(p []byte) (int, error) { func (port *windowsPort) Write(p []byte) (int, error) { var writed uint32 - err := syscall.WriteFile(port.handle, p, &writed, nil) + ev, err := createOverlappedEvent() + if err != nil { + return 0, err + } + defer syscall.CloseHandle(ev.HEvent) + err = syscall.WriteFile(port.handle, p, &writed, ev) + if err == syscall.ERROR_IO_PENDING { + // wait for write to complete + err = getOverlappedResult(port.handle, ev, &writed, true) + } return int(writed), err } @@ -295,6 +323,15 @@ func (port *windowsPort) GetModemStatusBits() (*ModemStatusBits, error) { }, nil } +//sys createEvent(eventAttributes *uint32, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) = CreateEventW +//sys resetEvent(handle syscall.Handle) (err error) = ResetEvent +//sys getOverlappedResult(handle syscall.Handle, overlapEvent *syscall.Overlapped, n *uint32, wait bool) (err error) = GetOverlappedResult + +func createOverlappedEvent() (*syscall.Overlapped, error) { + h, err := createEvent(nil, true, false, nil) + return &syscall.Overlapped{HEvent: h}, err +} + func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { portName = "\\\\.\\" + portName path, err := syscall.UTF16PtrFromString(portName) @@ -306,7 +343,7 @@ func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, - 0, //syscall.FILE_FLAG_OVERLAPPED, + syscall.FILE_FLAG_OVERLAPPED, 0) if err != nil { switch err { diff --git a/syscall_windows.go b/syscall_windows.go index dfa3f84..a72df76 100644 --- a/syscall_windows.go +++ b/syscall_windows.go @@ -15,12 +15,15 @@ var ( modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") modkernel32 = windows.NewLazySystemDLL("kernel32.dll") - procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") - procGetCommState = modkernel32.NewProc("GetCommState") - procSetCommState = modkernel32.NewProc("SetCommState") - procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") - procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction") - procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus") + procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") + procGetCommState = modkernel32.NewProc("GetCommState") + procSetCommState = modkernel32.NewProc("SetCommState") + procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") + procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction") + procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus") + procCreateEventW = modkernel32.NewProc("CreateEventW") + procResetEvent = modkernel32.NewProc("ResetEvent") + procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") ) func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) { @@ -78,3 +81,58 @@ func getCommModemStatus(handle syscall.Handle, bits *uint32) (res bool) { res = r0 != 0 return } + +func createEvent(eventAttributes *uint32, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { + var _p0 uint32 + if manualReset { + _p0 = 1 + } else { + _p0 = 0 + } + var _p1 uint32 + if initialState { + _p1 = 1 + } else { + _p1 = 0 + } + r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(eventAttributes)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) + handle = syscall.Handle(r0) + if handle == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func resetEvent(handle syscall.Handle) (err error) { + r1, _, e1 := syscall.Syscall(procResetEvent.Addr(), 1, uintptr(handle), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getOverlappedResult(handle syscall.Handle, overlapEvent *syscall.Overlapped, n *uint32, wait bool) (err error) { + var _p0 uint32 + if wait { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapEvent)), uintptr(unsafe.Pointer(n)), uintptr(_p0), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} From 378aafb2f73b9e421e6bf8b4d7581861d01dd87c Mon Sep 17 00:00:00 2001 From: Veniamin Albaev Date: Tue, 24 Jan 2017 11:29:30 +0300 Subject: [PATCH 041/181] //go:generate moved out of OS specific go file To avoid cross platform go generate issue Look at https://github.com/golang/go/issues/16368 --- serial.go | 2 ++ serial_windows.go | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/serial.go b/serial.go index 331db34..c4a06d2 100644 --- a/serial.go +++ b/serial.go @@ -6,6 +6,8 @@ package serial // import "go.bug.st/serial.v1" +//go:generate go run extras/mksyscall_windows.go -output syscall_windows.go serial_windows.go + // Port is the interface for a serial Port type Port interface { // SetMode sets all parameters of the serial port diff --git a/serial_windows.go b/serial_windows.go index 68f7f76..b66842c 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -23,8 +23,6 @@ type windowsPort struct { handle syscall.Handle } -//go:generate go run extras/mksyscall_windows.go -output syscall_windows.go serial_windows.go - //sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) = advapi32.RegEnumValueW func nativeGetPortsList() ([]string, error) { From c8b1c23a56e6a7683f2544848c03ef328007a80b Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 7 Jan 2017 23:38:51 +0100 Subject: [PATCH 042/181] Moved USB enumerations subroutines in their own package --- doc.go | 19 ++- enumerator/enumerator.go | 41 +++++++ .../example_getdetailedportlist_test.go | 8 +- enumerator/syscall_windows.go | 113 ++++++++++++++++++ usb_darwin.go => enumerator/usb_darwin.go | 4 +- usb_freebsd.go => enumerator/usb_freebsd.go | 6 +- usb_linux.go => enumerator/usb_linux.go | 12 +- .../usb_ole_windows.go | 4 +- usb_windows.go => enumerator/usb_windows.go | 12 +- .../usb_windows_test.go | 4 +- serial.go | 20 ---- serial_windows.go | 2 +- syscall_windows.go | 109 +---------------- 13 files changed, 202 insertions(+), 152 deletions(-) create mode 100644 enumerator/enumerator.go rename example_getdetailedportlist_test.go => enumerator/example_getdetailedportlist_test.go (74%) create mode 100644 enumerator/syscall_windows.go rename usb_darwin.go => enumerator/usb_darwin.go (98%) rename usb_freebsd.go => enumerator/usb_freebsd.go (53%) rename usb_linux.go => enumerator/usb_linux.go (89%) rename usb_ole_windows.go => enumerator/usb_ole_windows.go (97%) rename usb_windows.go => enumerator/usb_windows.go (96%) rename usb_windows_test.go => enumerator/usb_windows_test.go (92%) diff --git a/doc.go b/doc.go index 5d9bc6f..c9b9901 100644 --- a/doc.go +++ b/doc.go @@ -79,12 +79,14 @@ serial port: fmt.Printf("%v", string(buff[:n])) } -If a port is a virutal USB-CDC serial port (an USB-to-RS232 cable or a -microcontroller development board are typical examples) is possible to -retrieve the USB metadata, like VID/PID or USB Serial Number, with the -GetDetailedPortsList function: +If a port is a virtual USB-CDC serial port (for example an USB-to-RS232 +cable or a microcontroller development board) is possible to retrieve +the USB metadata, like VID/PID or USB Serial Number, with the +GetDetailedPortsList function in the enumerator package: - ports, err := serial.GetDetailedPortsList() + import "go.bug.st/serial.v1/enumerator" + + ports, err := enumerator.GetDetailedPortsList() if err != nil { log.Fatal(err) } @@ -100,5 +102,12 @@ GetDetailedPortsList function: } } +for details on USB port enumeration see the documentation of the specific package. + +This library tries to avoid the use of the "C" package (and consequently the need +of cgo) to simplify cross compiling. +Unfortunately the USB enumeration package for darwin (MacOSX) requires cgo +to access the IOKit framework. This means that if you need USB enumeration +on darwin you're forced to use cgo. */ package serial // import "go.bug.st/serial.v1" diff --git a/enumerator/enumerator.go b/enumerator/enumerator.go new file mode 100644 index 0000000..b72a72f --- /dev/null +++ b/enumerator/enumerator.go @@ -0,0 +1,41 @@ +// +// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +package enumerator // import "go.bug.st/serial.v1/enumerator" + +// PortDetails contains detailed information about USB serial port. +// Use GetDetailedPortsList function to retrieve it. +type PortDetails struct { + Name string + IsUSB bool + VID string + PID string + SerialNumber string + + // Manufacturer string + // Product string +} + +// GetDetailedPortsList retrieve ports details like USB VID/PID. +// Please note that this function may not be available on all OS: +// in that case a FunctionNotImplemented error is returned. +func GetDetailedPortsList() ([]*PortDetails, error) { + return nativeGetDetailedPortsList() +} + +// PortEnumerationError is the error type for serial ports enumeration +type PortEnumerationError struct { + causedBy error +} + +// Error returns the complete error code with details on the cause of the error +func (e PortEnumerationError) Error() string { + reason := "Error while enumerating serial ports" + if e.causedBy != nil { + reason += ": " + e.causedBy.Error() + } + return reason +} diff --git a/example_getdetailedportlist_test.go b/enumerator/example_getdetailedportlist_test.go similarity index 74% rename from example_getdetailedportlist_test.go rename to enumerator/example_getdetailedportlist_test.go index 1c7a491..18c7738 100644 --- a/example_getdetailedportlist_test.go +++ b/enumerator/example_getdetailedportlist_test.go @@ -1,17 +1,17 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // -package serial_test +package enumerator_test import "fmt" import "log" -import "go.bug.st/serial.v1" +import "go.bug.st/serial.v1/enumerator" func ExampleGetDetailedPortsList() { - ports, err := serial.GetDetailedPortsList() + ports, err := enumerator.GetDetailedPortsList() if err != nil { log.Fatal(err) } diff --git a/enumerator/syscall_windows.go b/enumerator/syscall_windows.go new file mode 100644 index 0000000..cd598c7 --- /dev/null +++ b/enumerator/syscall_windows.go @@ -0,0 +1,113 @@ +// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT + +package enumerator + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +var ( + modsetupapi = windows.NewLazySystemDLL("setupapi.dll") + + procSetupDiClassGuidsFromNameW = modsetupapi.NewProc("SetupDiClassGuidsFromNameW") + procSetupDiGetClassDevsW = modsetupapi.NewProc("SetupDiGetClassDevsW") + procSetupDiDestroyDeviceInfoList = modsetupapi.NewProc("SetupDiDestroyDeviceInfoList") + procSetupDiEnumDeviceInfo = modsetupapi.NewProc("SetupDiEnumDeviceInfo") + procSetupDiGetDeviceInstanceIdW = modsetupapi.NewProc("SetupDiGetDeviceInstanceIdW") + procSetupDiOpenDevRegKey = modsetupapi.NewProc("SetupDiOpenDevRegKey") + procSetupDiGetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiGetDeviceRegistryPropertyW") +) + +func setupDiClassGuidsFromNameInternal(class string, guid *guid, guidSize uint32, requiredSize *uint32) (err error) { + var _p0 *uint16 + _p0, err = syscall.UTF16PtrFromString(class) + if err != nil { + return + } + return _setupDiClassGuidsFromNameInternal(_p0, guid, guidSize, requiredSize) +} + +func _setupDiClassGuidsFromNameInternal(class *uint16, guid *guid, guidSize uint32, requiredSize *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procSetupDiClassGuidsFromNameW.Addr(), 4, uintptr(unsafe.Pointer(class)), uintptr(unsafe.Pointer(guid)), uintptr(guidSize), uintptr(unsafe.Pointer(requiredSize)), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setupDiGetClassDevs(guid *guid, enumerator *string, hwndParent uintptr, flags uint32) (set devicesSet, err error) { + r0, _, e1 := syscall.Syscall6(procSetupDiGetClassDevsW.Addr(), 4, uintptr(unsafe.Pointer(guid)), uintptr(unsafe.Pointer(enumerator)), uintptr(hwndParent), uintptr(flags), 0, 0) + set = devicesSet(r0) + if set == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setupDiDestroyDeviceInfoList(set devicesSet) (err error) { + r1, _, e1 := syscall.Syscall(procSetupDiDestroyDeviceInfoList.Addr(), 1, uintptr(set), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setupDiEnumDeviceInfo(set devicesSet, index uint32, info *devInfoData) (err error) { + r1, _, e1 := syscall.Syscall(procSetupDiEnumDeviceInfo.Addr(), 3, uintptr(set), uintptr(index), uintptr(unsafe.Pointer(info))) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setupDiGetDeviceInstanceId(set devicesSet, devInfo *devInfoData, devInstanceId unsafe.Pointer, devInstanceIdSize uint32, requiredSize *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procSetupDiGetDeviceInstanceIdW.Addr(), 5, uintptr(set), uintptr(unsafe.Pointer(devInfo)), uintptr(devInstanceId), uintptr(devInstanceIdSize), uintptr(unsafe.Pointer(requiredSize)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setupDiOpenDevRegKey(set devicesSet, devInfo *devInfoData, scope dicsScope, hwProfile uint32, keyType uint32, samDesired regsam) (hkey syscall.Handle, err error) { + r0, _, e1 := syscall.Syscall6(procSetupDiOpenDevRegKey.Addr(), 6, uintptr(set), uintptr(unsafe.Pointer(devInfo)), uintptr(scope), uintptr(hwProfile), uintptr(keyType), uintptr(samDesired)) + hkey = syscall.Handle(r0) + if hkey == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setupDiGetDeviceRegistryProperty(set devicesSet, devInfo *devInfoData, property deviceProperty, propertyType *uint32, outValue *byte, outSize *uint32, reqSize *uint32) (res bool) { + r0, _, _ := syscall.Syscall9(procSetupDiGetDeviceRegistryPropertyW.Addr(), 7, uintptr(set), uintptr(unsafe.Pointer(devInfo)), uintptr(property), uintptr(unsafe.Pointer(propertyType)), uintptr(unsafe.Pointer(outValue)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(reqSize)), 0, 0) + res = r0 != 0 + return +} diff --git a/usb_darwin.go b/enumerator/usb_darwin.go similarity index 98% rename from usb_darwin.go rename to enumerator/usb_darwin.go index fa63667..a4b632a 100644 --- a/usb_darwin.go +++ b/enumerator/usb_darwin.go @@ -1,10 +1,10 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial.v1" +package enumerator // import "go.bug.st/serial.v1/enumerator" // #cgo LDFLAGS: -framework CoreFoundation -framework IOKit -fconstant-cfstrings // #include diff --git a/usb_freebsd.go b/enumerator/usb_freebsd.go similarity index 53% rename from usb_freebsd.go rename to enumerator/usb_freebsd.go index 8a60a19..d60d13f 100644 --- a/usb_freebsd.go +++ b/enumerator/usb_freebsd.go @@ -1,12 +1,12 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial.v1" +package enumerator // import "go.bug.st/serial.v1/enumerator" func nativeGetDetailedPortsList() ([]*PortDetails, error) { // TODO - return nil, &PortError{code: FunctionNotImplemented} + return nil, &PortEnumerationError{} } diff --git a/usb_linux.go b/enumerator/usb_linux.go similarity index 89% rename from usb_linux.go rename to enumerator/usb_linux.go index c671140..d2aca90 100644 --- a/usb_linux.go +++ b/enumerator/usb_linux.go @@ -1,30 +1,32 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial.v1" +package enumerator // import "go.bug.st/serial.v1/enumerator" import ( "bufio" "fmt" "os" "path/filepath" + + "go.bug.st/serial.v1" ) func nativeGetDetailedPortsList() ([]*PortDetails, error) { // Retrieve the port list - ports, err := nativeGetPortsList() + ports, err := serial.GetPortsList() if err != nil { - return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + return nil, &PortEnumerationError{causedBy: err} } var res []*PortDetails for _, port := range ports { details, err := nativeGetPortDetails(port) if err != nil { - return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + return nil, &PortEnumerationError{causedBy: err} } res = append(res, details) } diff --git a/usb_ole_windows.go b/enumerator/usb_ole_windows.go similarity index 97% rename from usb_ole_windows.go rename to enumerator/usb_ole_windows.go index 128aa86..bbc0435 100644 --- a/usb_ole_windows.go +++ b/enumerator/usb_ole_windows.go @@ -1,12 +1,12 @@ // -// Copyright 2014-2016 Lars Knudsen, Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Lars Knudsen, Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // // +build ignore -package serial +package enumerator import ( "log" diff --git a/usb_windows.go b/enumerator/usb_windows.go similarity index 96% rename from usb_windows.go rename to enumerator/usb_windows.go index 1e3a6c4..1431deb 100644 --- a/usb_windows.go +++ b/enumerator/usb_windows.go @@ -1,10 +1,10 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial.v1" +package enumerator // import "go.bug.st/serial.v1/enumerator" import ( "fmt" @@ -54,6 +54,8 @@ func parseDeviceID(deviceID string, details *PortDetails) { // setupapi based // -------------- +//go:generate go run ../extras/mksyscall_windows.go -output syscall_windows.go usb_windows.go + //sys setupDiClassGuidsFromNameInternal(class string, guid *guid, guidSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiClassGuidsFromNameW //sys setupDiGetClassDevs(guid *guid, enumerator *string, hwndParent uintptr, flags uint32) (set devicesSet, err error) = setupapi.SetupDiGetClassDevsW //sys setupDiDestroyDeviceInfoList(set devicesSet) (err error) = setupapi.SetupDiDestroyDeviceInfoList @@ -232,14 +234,14 @@ func (dev *deviceInfo) openDevRegKey(scope dicsScope, hwProfile uint32, keyType func nativeGetDetailedPortsList() ([]*PortDetails, error) { guids, err := classGuidsFromName("Ports") if err != nil { - return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + return nil, &PortEnumerationError{causedBy: err} } var res []*PortDetails for _, g := range guids { devsSet, err := g.getDevicesSet() if err != nil { - return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + return nil, &PortEnumerationError{causedBy: err} } defer devsSet.destroy() @@ -260,7 +262,7 @@ func nativeGetDetailedPortsList() ([]*PortDetails, error) { details.Name = portName if err := retrievePortDetailsFromDevInfo(device, details); err != nil { - return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + return nil, &PortEnumerationError{causedBy: err} } res = append(res, details) } diff --git a/usb_windows_test.go b/enumerator/usb_windows_test.go similarity index 92% rename from usb_windows_test.go rename to enumerator/usb_windows_test.go index ee58d47..853c1a2 100644 --- a/usb_windows_test.go +++ b/enumerator/usb_windows_test.go @@ -1,10 +1,10 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial.v1" +package enumerator // import "go.bug.st/serial.v1/enumerator" import ( "testing" diff --git a/serial.go b/serial.go index 54a1703..391911c 100644 --- a/serial.go +++ b/serial.go @@ -55,26 +55,6 @@ func GetPortsList() ([]string, error) { return nativeGetPortsList() } -// PortDetails contains detailed information about USB serial port. -// Use GetDetailedPortsList function to retrieve it. -type PortDetails struct { - Name string - IsUSB bool - VID string - PID string - SerialNumber string - - // Manufacturer string - // Product string -} - -// GetDetailedPortsList retrieve ports details like USB VID/PID. -// Please note that this function may not be available on all OS: -// in that case a FunctionNotImplemented error is returned. -func GetDetailedPortsList() ([]*PortDetails, error) { - return nativeGetDetailedPortsList() -} - // Mode describes a serial port configuration. type Mode struct { BaudRate int // The serial port bitrate (aka Baudrate) diff --git a/serial_windows.go b/serial_windows.go index 500e85a..ed04e6b 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -23,7 +23,7 @@ type windowsPort struct { handle syscall.Handle } -//go:generate go run extras/mksyscall_windows.go -output syscall_windows.go serial_windows.go usb_windows.go +//go:generate go run extras/mksyscall_windows.go -output syscall_windows.go serial_windows.go //sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) = advapi32.RegEnumValueW diff --git a/syscall_windows.go b/syscall_windows.go index add2cce..dfa3f84 100644 --- a/syscall_windows.go +++ b/syscall_windows.go @@ -14,21 +14,13 @@ var _ unsafe.Pointer var ( modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") modkernel32 = windows.NewLazySystemDLL("kernel32.dll") - modsetupapi = windows.NewLazySystemDLL("setupapi.dll") - procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") - procGetCommState = modkernel32.NewProc("GetCommState") - procSetCommState = modkernel32.NewProc("SetCommState") - procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") - procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction") - procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus") - procSetupDiClassGuidsFromNameW = modsetupapi.NewProc("SetupDiClassGuidsFromNameW") - procSetupDiGetClassDevsW = modsetupapi.NewProc("SetupDiGetClassDevsW") - procSetupDiDestroyDeviceInfoList = modsetupapi.NewProc("SetupDiDestroyDeviceInfoList") - procSetupDiEnumDeviceInfo = modsetupapi.NewProc("SetupDiEnumDeviceInfo") - procSetupDiGetDeviceInstanceIdW = modsetupapi.NewProc("SetupDiGetDeviceInstanceIdW") - procSetupDiOpenDevRegKey = modsetupapi.NewProc("SetupDiOpenDevRegKey") - procSetupDiGetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiGetDeviceRegistryPropertyW") + procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") + procGetCommState = modkernel32.NewProc("GetCommState") + procSetCommState = modkernel32.NewProc("SetCommState") + procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") + procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction") + procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus") ) func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) { @@ -86,92 +78,3 @@ func getCommModemStatus(handle syscall.Handle, bits *uint32) (res bool) { res = r0 != 0 return } - -func setupDiClassGuidsFromNameInternal(class string, guid *guid, guidSize uint32, requiredSize *uint32) (err error) { - var _p0 *uint16 - _p0, err = syscall.UTF16PtrFromString(class) - if err != nil { - return - } - return _setupDiClassGuidsFromNameInternal(_p0, guid, guidSize, requiredSize) -} - -func _setupDiClassGuidsFromNameInternal(class *uint16, guid *guid, guidSize uint32, requiredSize *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procSetupDiClassGuidsFromNameW.Addr(), 4, uintptr(unsafe.Pointer(class)), uintptr(unsafe.Pointer(guid)), uintptr(guidSize), uintptr(unsafe.Pointer(requiredSize)), 0, 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func setupDiGetClassDevs(guid *guid, enumerator *string, hwndParent uintptr, flags uint32) (set devicesSet, err error) { - r0, _, e1 := syscall.Syscall6(procSetupDiGetClassDevsW.Addr(), 4, uintptr(unsafe.Pointer(guid)), uintptr(unsafe.Pointer(enumerator)), uintptr(hwndParent), uintptr(flags), 0, 0) - set = devicesSet(r0) - if set == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func setupDiDestroyDeviceInfoList(set devicesSet) (err error) { - r1, _, e1 := syscall.Syscall(procSetupDiDestroyDeviceInfoList.Addr(), 1, uintptr(set), 0, 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func setupDiEnumDeviceInfo(set devicesSet, index uint32, info *devInfoData) (err error) { - r1, _, e1 := syscall.Syscall(procSetupDiEnumDeviceInfo.Addr(), 3, uintptr(set), uintptr(index), uintptr(unsafe.Pointer(info))) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func setupDiGetDeviceInstanceId(set devicesSet, devInfo *devInfoData, devInstanceId unsafe.Pointer, devInstanceIdSize uint32, requiredSize *uint32) (err error) { - r1, _, e1 := syscall.Syscall6(procSetupDiGetDeviceInstanceIdW.Addr(), 5, uintptr(set), uintptr(unsafe.Pointer(devInfo)), uintptr(devInstanceId), uintptr(devInstanceIdSize), uintptr(unsafe.Pointer(requiredSize)), 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func setupDiOpenDevRegKey(set devicesSet, devInfo *devInfoData, scope dicsScope, hwProfile uint32, keyType uint32, samDesired regsam) (hkey syscall.Handle, err error) { - r0, _, e1 := syscall.Syscall6(procSetupDiOpenDevRegKey.Addr(), 6, uintptr(set), uintptr(unsafe.Pointer(devInfo)), uintptr(scope), uintptr(hwProfile), uintptr(keyType), uintptr(samDesired)) - hkey = syscall.Handle(r0) - if hkey == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func setupDiGetDeviceRegistryProperty(set devicesSet, devInfo *devInfoData, property deviceProperty, propertyType *uint32, outValue *byte, outSize *uint32, reqSize *uint32) (res bool) { - r0, _, _ := syscall.Syscall9(procSetupDiGetDeviceRegistryPropertyW.Addr(), 7, uintptr(set), uintptr(unsafe.Pointer(devInfo)), uintptr(property), uintptr(unsafe.Pointer(propertyType)), uintptr(unsafe.Pointer(outValue)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(reqSize)), 0, 0) - res = r0 != 0 - return -} From 0eb640a768cf08178c3b627c08035380a0e45a61 Mon Sep 17 00:00:00 2001 From: Veniamin Albaev Date: Sun, 29 Jan 2017 23:00:19 +0300 Subject: [PATCH 043/181] TravisCI usign go 1.7.x instead of 1.7.2 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2783c85..0a758a0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: go go: - - 1.7.2 + - 1.7.x go_import_path: go.bug.st/serial.v1 From 3ad7c6b7d20f166d15a5ba4b597750d6a0fd5ccc Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 4 Feb 2017 00:24:21 +0100 Subject: [PATCH 044/181] Moved go:generate into non-GOOS specific file See #19 --- enumerator/enumerator.go | 2 ++ enumerator/usb_windows.go | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/enumerator/enumerator.go b/enumerator/enumerator.go index b72a72f..918db57 100644 --- a/enumerator/enumerator.go +++ b/enumerator/enumerator.go @@ -6,6 +6,8 @@ package enumerator // import "go.bug.st/serial.v1/enumerator" +//go:generate go run ../extras/mksyscall_windows.go -output syscall_windows.go usb_windows.go + // PortDetails contains detailed information about USB serial port. // Use GetDetailedPortsList function to retrieve it. type PortDetails struct { diff --git a/enumerator/usb_windows.go b/enumerator/usb_windows.go index 1431deb..12ded68 100644 --- a/enumerator/usb_windows.go +++ b/enumerator/usb_windows.go @@ -54,8 +54,6 @@ func parseDeviceID(deviceID string, details *PortDetails) { // setupapi based // -------------- -//go:generate go run ../extras/mksyscall_windows.go -output syscall_windows.go usb_windows.go - //sys setupDiClassGuidsFromNameInternal(class string, guid *guid, guidSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiClassGuidsFromNameW //sys setupDiGetClassDevs(guid *guid, enumerator *string, hwndParent uintptr, flags uint32) (set devicesSet, err error) = setupapi.SetupDiGetClassDevsW //sys setupDiDestroyDeviceInfoList(set devicesSet) (err error) = setupapi.SetupDiDestroyDeviceInfoList From fbd0f9fb33557538e5d450dd86eeb4e4bea10044 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 4 Feb 2017 00:31:57 +0100 Subject: [PATCH 045/181] Exclude 'enumerator' module from Travis-CI build --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3ad5c64..40d48ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,7 @@ before_install: script: - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go get github.com/stretchr/testify/require - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go get golang.org/x/sys/windows - - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go build -v ./... + - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go build -v go.bug.st/serial.v1 - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go test -c -v go.bug.st/serial.v1 notifications: From 11d1e54246caacb208aa8fe57e609713d58cf513 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 4 Feb 2017 00:53:51 +0100 Subject: [PATCH 046/181] Updated docs for USB --- doc.go | 2 +- enumerator/doc.go | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 enumerator/doc.go diff --git a/doc.go b/doc.go index c9b9901..b83f924 100644 --- a/doc.go +++ b/doc.go @@ -84,7 +84,7 @@ cable or a microcontroller development board) is possible to retrieve the USB metadata, like VID/PID or USB Serial Number, with the GetDetailedPortsList function in the enumerator package: - import "go.bug.st/serial.v1/enumerator" + import "go.bug.st/serial.v1/enumerator" ports, err := enumerator.GetDetailedPortsList() if err != nil { diff --git a/enumerator/doc.go b/enumerator/doc.go new file mode 100644 index 0000000..d214715 --- /dev/null +++ b/enumerator/doc.go @@ -0,0 +1,21 @@ +// +// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +/* +Package enumerator is a golang cross-platform library for USB serial port discovery. + +WARNING: this library is still beta-testing code! please consider the library +and the API as *unstable*. Beware that, even if at this point it's unlike to +happen, the API may be subject to change until this notice is removed from +the documentation. + +This library has been tested on Linux, Windows and Mac and uses specific OS +services to enumerate USB PID/VID, in particular on MacOSX the use of cgo is +required in order to access the IOKit Framework. This means that the library +cannot be easily cross compiled for GOOS=darwing targets. + +*/ +package enumerator // import "go.bug.st/serial.v1/enumerator" From 51237629e65e07ff4849549128fc96417cfa9eca Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Mon, 13 Feb 2017 16:02:49 +0100 Subject: [PATCH 047/181] Fix build using Darwin and osxcross --- enumerator/usb_darwin.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/enumerator/usb_darwin.go b/enumerator/usb_darwin.go index a4b632a..b02de12 100644 --- a/enumerator/usb_darwin.go +++ b/enumerator/usb_darwin.go @@ -22,14 +22,14 @@ func nativeGetDetailedPortsList() ([]*PortDetails, error) { services, err := getAllServices("IOSerialBSDClient") if err != nil { - return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + return nil, &PortEnumerationError{causedBy: err} } for _, service := range services { defer service.Release() port, err := extractPortInfo(C.io_registry_entry_t(service)) if err != nil { - return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} + return nil, &PortEnumerationError{causedBy: err} } ports = append(ports, port) } From b55fdf163bd35c9b49b51104cb43ef05a1244217 Mon Sep 17 00:00:00 2001 From: Veniamin Albaev Date: Wed, 1 Feb 2017 09:23:20 +0300 Subject: [PATCH 048/181] All `//sys` instructions moved to separate `syscall_*` files Generated `syscall_*` files ranamed to `zsyscall_*` ones like it done in `golang.org/x/sys` package. --- serial.go | 2 +- serial_darwin.go | 2 - serial_freebsd.go | 2 - serial_linux.go | 2 - serial_windows.go | 14 ----- syscall_darwin.go | 14 +---- syscall_freebsd.go | 14 +---- syscall_linux.go | 14 +---- syscall_windows.go | 143 +++++--------------------------------------- zsyscall_darwin.go | 21 +++++++ zsyscall_freebsd.go | 21 +++++++ zsyscall_linux.go | 21 +++++++ zsyscall_windows.go | 138 ++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 220 insertions(+), 188 deletions(-) create mode 100644 zsyscall_darwin.go create mode 100644 zsyscall_freebsd.go create mode 100644 zsyscall_linux.go create mode 100644 zsyscall_windows.go diff --git a/serial.go b/serial.go index 24be176..625a249 100644 --- a/serial.go +++ b/serial.go @@ -6,7 +6,7 @@ package serial // import "go.bug.st/serial.v1" -//go:generate go run extras/mksyscall_windows.go -output syscall_windows.go serial_windows.go +//go:generate go run extras/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go // Port is the interface for a serial Port type Port interface { diff --git a/serial_darwin.go b/serial_darwin.go index 393236d..eb9a7ae 100644 --- a/serial_darwin.go +++ b/serial_darwin.go @@ -11,7 +11,5 @@ import "syscall" const devFolder = "/dev" const regexFilter = "^(cu|tty)\\..*" -//sys ioctl(fd int, req uint64, data uintptr) (err error) - const ioctlTcgetattr = syscall.TIOCGETA const ioctlTcsetattr = syscall.TIOCSETA diff --git a/serial_freebsd.go b/serial_freebsd.go index 1e09349..c237cfc 100644 --- a/serial_freebsd.go +++ b/serial_freebsd.go @@ -53,7 +53,5 @@ const tcCRTS_IFLOW uint32 = 0x00020000 const tcCRTSCTS uint32 = tcCCTS_OFLOW -//sys ioctl(fd int, req uint64, data uintptr) (err error) - const ioctlTcgetattr = syscall.TIOCGETA const ioctlTcsetattr = syscall.TIOCSETA diff --git a/serial_linux.go b/serial_linux.go index 89f3aed..281820f 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -60,7 +60,5 @@ const tcIUCLC = syscall.IUCLC const tcCRTSCTS uint32 = 0x80000000 -//sys ioctl(fd int, req uint64, data uintptr) (err error) - const ioctlTcgetattr = syscall.TCGETS const ioctlTcsetattr = syscall.TCSETS diff --git a/serial_windows.go b/serial_windows.go index b66842c..e261cbe 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -23,8 +23,6 @@ type windowsPort struct { handle syscall.Handle } -//sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) = advapi32.RegEnumValueW - func nativeGetPortsList() ([]string, error) { subKey, err := syscall.UTF16PtrFromString("HARDWARE\\DEVICEMAP\\SERIALCOMM\\") if err != nil { @@ -181,10 +179,6 @@ type commTimeouts struct { WriteTotalTimeoutConstant uint32 } -//sys getCommState(handle syscall.Handle, dcb *dcb) (err error) = GetCommState -//sys setCommState(handle syscall.Handle, dcb *dcb) (err error) = SetCommState -//sys setCommTimeouts(handle syscall.Handle, timeouts *commTimeouts) (err error) = SetCommTimeouts - const ( noParity = 0 oddParity = 1 @@ -213,8 +207,6 @@ var stopBitsMap = map[StopBits]byte{ TwoStopBits: twoStopBits, } -//sys escapeCommFunction(handle syscall.Handle, function uint32) (res bool) = EscapeCommFunction - const ( commFunctionSetXOFF = 1 commFunctionSetXON = 2 @@ -226,8 +218,6 @@ const ( commFunctionClrBreak = 9 ) -//sys getCommModemStatus(handle syscall.Handle, bits *uint32) (res bool) = GetCommModemStatus - const ( msCTSOn = 0x0010 msDSROn = 0x0020 @@ -321,10 +311,6 @@ func (port *windowsPort) GetModemStatusBits() (*ModemStatusBits, error) { }, nil } -//sys createEvent(eventAttributes *uint32, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) = CreateEventW -//sys resetEvent(handle syscall.Handle) (err error) = ResetEvent -//sys getOverlappedResult(handle syscall.Handle, overlapEvent *syscall.Overlapped, n *uint32, wait bool) (err error) = GetOverlappedResult - func createOverlappedEvent() (*syscall.Overlapped, error) { h, err := createEvent(nil, true, false, nil) return &syscall.Overlapped{HEvent: h}, err diff --git a/syscall_darwin.go b/syscall_darwin.go index c8cd031..0d33f37 100644 --- a/syscall_darwin.go +++ b/syscall_darwin.go @@ -4,18 +4,6 @@ // license that can be found in the LICENSE file. // -// This file is machine generated by the command: -// mksyscall.pl serial_darwin.go -// The generated stub is modified to make it compile under the "serial" package - package serial // import "go.bug.st/serial.v1" -import "syscall" - -func ioctl(fd int, req uint64, data uintptr) (err error) { - _, _, e1 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data)) - if e1 != 0 { - err = e1 - } - return -} +//sys ioctl(fd int, req uint64, data uintptr) (err error) diff --git a/syscall_freebsd.go b/syscall_freebsd.go index c8cd031..0d33f37 100644 --- a/syscall_freebsd.go +++ b/syscall_freebsd.go @@ -4,18 +4,6 @@ // license that can be found in the LICENSE file. // -// This file is machine generated by the command: -// mksyscall.pl serial_darwin.go -// The generated stub is modified to make it compile under the "serial" package - package serial // import "go.bug.st/serial.v1" -import "syscall" - -func ioctl(fd int, req uint64, data uintptr) (err error) { - _, _, e1 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data)) - if e1 != 0 { - err = e1 - } - return -} +//sys ioctl(fd int, req uint64, data uintptr) (err error) diff --git a/syscall_linux.go b/syscall_linux.go index cb216b1..0d33f37 100644 --- a/syscall_linux.go +++ b/syscall_linux.go @@ -4,18 +4,6 @@ // license that can be found in the LICENSE file. // -// This file is machine generated by the command: -// mksyscall.pl serial_linux.go -// The generated stub is modified to make it compile under the "serial" package - package serial // import "go.bug.st/serial.v1" -import "syscall" - -func ioctl(fd int, req uint64, data uintptr) (err error) { - _, _, e1 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data)) - if e1 != 0 { - err = e1 - } - return -} +//sys ioctl(fd int, req uint64, data uintptr) (err error) diff --git a/syscall_windows.go b/syscall_windows.go index a72df76..69c4550 100644 --- a/syscall_windows.go +++ b/syscall_windows.go @@ -1,138 +1,25 @@ -// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT +// +// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// -package serial +package serial // import "go.bug.st/serial.v1" -import ( - "syscall" - "unsafe" +//sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) = advapi32.RegEnumValueW - "golang.org/x/sys/windows" -) +//sys getCommState(handle syscall.Handle, dcb *dcb) (err error) = GetCommState -var _ unsafe.Pointer +//sys setCommState(handle syscall.Handle, dcb *dcb) (err error) = SetCommState -var ( - modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") - modkernel32 = windows.NewLazySystemDLL("kernel32.dll") +//sys setCommTimeouts(handle syscall.Handle, timeouts *commTimeouts) (err error) = SetCommTimeouts - procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") - procGetCommState = modkernel32.NewProc("GetCommState") - procSetCommState = modkernel32.NewProc("SetCommState") - procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") - procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction") - procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procResetEvent = modkernel32.NewProc("ResetEvent") - procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") -) +//sys escapeCommFunction(handle syscall.Handle, function uint32) (res bool) = EscapeCommFunction -func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) { - r0, _, _ := syscall.Syscall9(procRegEnumValueW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(class)), uintptr(unsafe.Pointer(value)), uintptr(unsafe.Pointer(valueLen)), 0) - if r0 != 0 { - regerrno = syscall.Errno(r0) - } - return -} +//sys getCommModemStatus(handle syscall.Handle, bits *uint32) (res bool) = GetCommModemStatus -func getCommState(handle syscall.Handle, dcb *dcb) (err error) { - r1, _, e1 := syscall.Syscall(procGetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} +//sys createEvent(eventAttributes *uint32, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) = CreateEventW -func setCommState(handle syscall.Handle, dcb *dcb) (err error) { - r1, _, e1 := syscall.Syscall(procSetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} +//sys resetEvent(handle syscall.Handle) (err error) = ResetEvent -func setCommTimeouts(handle syscall.Handle, timeouts *commTimeouts) (err error) { - r1, _, e1 := syscall.Syscall(procSetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func escapeCommFunction(handle syscall.Handle, function uint32) (res bool) { - r0, _, _ := syscall.Syscall(procEscapeCommFunction.Addr(), 2, uintptr(handle), uintptr(function), 0) - res = r0 != 0 - return -} - -func getCommModemStatus(handle syscall.Handle, bits *uint32) (res bool) { - r0, _, _ := syscall.Syscall(procGetCommModemStatus.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(bits)), 0) - res = r0 != 0 - return -} - -func createEvent(eventAttributes *uint32, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { - var _p0 uint32 - if manualReset { - _p0 = 1 - } else { - _p0 = 0 - } - var _p1 uint32 - if initialState { - _p1 = 1 - } else { - _p1 = 0 - } - r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(eventAttributes)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) - handle = syscall.Handle(r0) - if handle == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func resetEvent(handle syscall.Handle) (err error) { - r1, _, e1 := syscall.Syscall(procResetEvent.Addr(), 1, uintptr(handle), 0, 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} - -func getOverlappedResult(handle syscall.Handle, overlapEvent *syscall.Overlapped, n *uint32, wait bool) (err error) { - var _p0 uint32 - if wait { - _p0 = 1 - } else { - _p0 = 0 - } - r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapEvent)), uintptr(unsafe.Pointer(n)), uintptr(_p0), 0, 0) - if r1 == 0 { - if e1 != 0 { - err = error(e1) - } else { - err = syscall.EINVAL - } - } - return -} +//sys getOverlappedResult(handle syscall.Handle, overlapEvent *syscall.Overlapped, n *uint32, wait bool) (err error) = GetOverlappedResult diff --git a/zsyscall_darwin.go b/zsyscall_darwin.go new file mode 100644 index 0000000..c8cd031 --- /dev/null +++ b/zsyscall_darwin.go @@ -0,0 +1,21 @@ +// +// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +// This file is machine generated by the command: +// mksyscall.pl serial_darwin.go +// The generated stub is modified to make it compile under the "serial" package + +package serial // import "go.bug.st/serial.v1" + +import "syscall" + +func ioctl(fd int, req uint64, data uintptr) (err error) { + _, _, e1 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data)) + if e1 != 0 { + err = e1 + } + return +} diff --git a/zsyscall_freebsd.go b/zsyscall_freebsd.go new file mode 100644 index 0000000..c8cd031 --- /dev/null +++ b/zsyscall_freebsd.go @@ -0,0 +1,21 @@ +// +// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +// This file is machine generated by the command: +// mksyscall.pl serial_darwin.go +// The generated stub is modified to make it compile under the "serial" package + +package serial // import "go.bug.st/serial.v1" + +import "syscall" + +func ioctl(fd int, req uint64, data uintptr) (err error) { + _, _, e1 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data)) + if e1 != 0 { + err = e1 + } + return +} diff --git a/zsyscall_linux.go b/zsyscall_linux.go new file mode 100644 index 0000000..cb216b1 --- /dev/null +++ b/zsyscall_linux.go @@ -0,0 +1,21 @@ +// +// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +// This file is machine generated by the command: +// mksyscall.pl serial_linux.go +// The generated stub is modified to make it compile under the "serial" package + +package serial // import "go.bug.st/serial.v1" + +import "syscall" + +func ioctl(fd int, req uint64, data uintptr) (err error) { + _, _, e1 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data)) + if e1 != 0 { + err = e1 + } + return +} diff --git a/zsyscall_windows.go b/zsyscall_windows.go new file mode 100644 index 0000000..a72df76 --- /dev/null +++ b/zsyscall_windows.go @@ -0,0 +1,138 @@ +// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT + +package serial + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +var ( + modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + + procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") + procGetCommState = modkernel32.NewProc("GetCommState") + procSetCommState = modkernel32.NewProc("SetCommState") + procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") + procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction") + procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus") + procCreateEventW = modkernel32.NewProc("CreateEventW") + procResetEvent = modkernel32.NewProc("ResetEvent") + procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") +) + +func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) { + r0, _, _ := syscall.Syscall9(procRegEnumValueW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(class)), uintptr(unsafe.Pointer(value)), uintptr(unsafe.Pointer(valueLen)), 0) + if r0 != 0 { + regerrno = syscall.Errno(r0) + } + return +} + +func getCommState(handle syscall.Handle, dcb *dcb) (err error) { + r1, _, e1 := syscall.Syscall(procGetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setCommState(handle syscall.Handle, dcb *dcb) (err error) { + r1, _, e1 := syscall.Syscall(procSetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setCommTimeouts(handle syscall.Handle, timeouts *commTimeouts) (err error) { + r1, _, e1 := syscall.Syscall(procSetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func escapeCommFunction(handle syscall.Handle, function uint32) (res bool) { + r0, _, _ := syscall.Syscall(procEscapeCommFunction.Addr(), 2, uintptr(handle), uintptr(function), 0) + res = r0 != 0 + return +} + +func getCommModemStatus(handle syscall.Handle, bits *uint32) (res bool) { + r0, _, _ := syscall.Syscall(procGetCommModemStatus.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(bits)), 0) + res = r0 != 0 + return +} + +func createEvent(eventAttributes *uint32, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { + var _p0 uint32 + if manualReset { + _p0 = 1 + } else { + _p0 = 0 + } + var _p1 uint32 + if initialState { + _p1 = 1 + } else { + _p1 = 0 + } + r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(eventAttributes)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) + handle = syscall.Handle(r0) + if handle == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func resetEvent(handle syscall.Handle) (err error) { + r1, _, e1 := syscall.Syscall(procResetEvent.Addr(), 1, uintptr(handle), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getOverlappedResult(handle syscall.Handle, overlapEvent *syscall.Overlapped, n *uint32, wait bool) (err error) { + var _p0 uint32 + if wait { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapEvent)), uintptr(unsafe.Pointer(n)), uintptr(_p0), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} From 970475ed0689b2e59015b255ce6430e6fde8d3a9 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 4 Mar 2017 16:29:55 +0100 Subject: [PATCH 049/181] Updated licenses --- doc.go | 2 +- example_getportlist_test.go | 2 +- example_modem_bits_test.go | 2 +- example_serialport_test.go | 2 +- example_test.go | 2 +- serial.go | 2 +- serial_darwin.go | 2 +- serial_darwin_386.go | 2 +- serial_darwin_amd64.go | 2 +- serial_freebsd.go | 2 +- serial_linux.go | 2 +- serial_unix.go | 2 +- serial_windows.go | 2 +- syscall_darwin.go | 2 +- syscall_freebsd.go | 2 +- syscall_linux.go | 2 +- syscall_windows.go | 2 +- unixutils/pipe.go | 2 +- unixutils/select.go | 2 +- zsyscall_darwin.go | 2 +- zsyscall_freebsd.go | 2 +- zsyscall_linux.go | 2 +- 22 files changed, 22 insertions(+), 22 deletions(-) diff --git a/doc.go b/doc.go index 843eb7f..b6a70e9 100644 --- a/doc.go +++ b/doc.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/example_getportlist_test.go b/example_getportlist_test.go index 5ac8244..c8cb35b 100644 --- a/example_getportlist_test.go +++ b/example_getportlist_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/example_modem_bits_test.go b/example_modem_bits_test.go index 1532616..9ae3389 100644 --- a/example_modem_bits_test.go +++ b/example_modem_bits_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/example_serialport_test.go b/example_serialport_test.go index 4e6de61..6f09659 100644 --- a/example_serialport_test.go +++ b/example_serialport_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/example_test.go b/example_test.go index 0f39d26..4f56978 100644 --- a/example_test.go +++ b/example_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial.go b/serial.go index 625a249..48392c5 100644 --- a/serial.go +++ b/serial.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_darwin.go b/serial_darwin.go index eb9a7ae..13cf862 100644 --- a/serial_darwin.go +++ b/serial_darwin.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_darwin_386.go b/serial_darwin_386.go index a1a6771..4f96df7 100644 --- a/serial_darwin_386.go +++ b/serial_darwin_386.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_darwin_amd64.go b/serial_darwin_amd64.go index b721d78..6ea59d5 100644 --- a/serial_darwin_amd64.go +++ b/serial_darwin_amd64.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_freebsd.go b/serial_freebsd.go index c237cfc..ae5ff09 100644 --- a/serial_freebsd.go +++ b/serial_freebsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_linux.go b/serial_linux.go index 281820f..0ef03b8 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_unix.go b/serial_unix.go index 3580104..8dc2c74 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_windows.go b/serial_windows.go index e261cbe..aabb7be 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/syscall_darwin.go b/syscall_darwin.go index 0d33f37..670a257 100644 --- a/syscall_darwin.go +++ b/syscall_darwin.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/syscall_freebsd.go b/syscall_freebsd.go index 0d33f37..670a257 100644 --- a/syscall_freebsd.go +++ b/syscall_freebsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/syscall_linux.go b/syscall_linux.go index 0d33f37..670a257 100644 --- a/syscall_linux.go +++ b/syscall_linux.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/syscall_windows.go b/syscall_windows.go index 69c4550..529b4df 100644 --- a/syscall_windows.go +++ b/syscall_windows.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/unixutils/pipe.go b/unixutils/pipe.go index 567c739..941b6eb 100644 --- a/unixutils/pipe.go +++ b/unixutils/pipe.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/unixutils/select.go b/unixutils/select.go index bb73b1b..7ad60a8 100644 --- a/unixutils/select.go +++ b/unixutils/select.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/zsyscall_darwin.go b/zsyscall_darwin.go index c8cd031..49cfac4 100644 --- a/zsyscall_darwin.go +++ b/zsyscall_darwin.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/zsyscall_freebsd.go b/zsyscall_freebsd.go index c8cd031..49cfac4 100644 --- a/zsyscall_freebsd.go +++ b/zsyscall_freebsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/zsyscall_linux.go b/zsyscall_linux.go index cb216b1..60c2d75 100644 --- a/zsyscall_linux.go +++ b/zsyscall_linux.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2016 Cristian Maglie. All rights reserved. +// Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // From 48d7240ab82d1a0772a25913505ab923c4f22e6d Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 4 Mar 2017 22:54:59 +0100 Subject: [PATCH 050/181] Fixed typo --- zsyscall_freebsd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zsyscall_freebsd.go b/zsyscall_freebsd.go index 49cfac4..702fd99 100644 --- a/zsyscall_freebsd.go +++ b/zsyscall_freebsd.go @@ -5,7 +5,7 @@ // // This file is machine generated by the command: -// mksyscall.pl serial_darwin.go +// mksyscall.pl serial_freebsd.go // The generated stub is modified to make it compile under the "serial" package package serial // import "go.bug.st/serial.v1" From 4da3278c95f0bfd86ff953446fd8972fdfaff5f7 Mon Sep 17 00:00:00 2001 From: Veniamin Albaev Date: Thu, 9 Mar 2017 10:46:44 +0300 Subject: [PATCH 051/181] Use mksyscall_windows.go from $GOROOT --- extras/mksyscall_windows.go | 871 ------------------------------------ serial.go | 2 +- zsyscall_windows.go | 37 +- 3 files changed, 32 insertions(+), 878 deletions(-) delete mode 100755 extras/mksyscall_windows.go diff --git a/extras/mksyscall_windows.go b/extras/mksyscall_windows.go deleted file mode 100755 index 1e0d940..0000000 --- a/extras/mksyscall_windows.go +++ /dev/null @@ -1,871 +0,0 @@ -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build ignore - -/* -mksyscall_windows generates windows system call bodies - -It parses all files specified on command line containing function -prototypes (like syscall_windows.go) and prints system call bodies -to standard output. - -The prototypes are marked by lines beginning with "//sys" and read -like func declarations if //sys is replaced by func, but: - -* The parameter lists must give a name for each argument. This - includes return parameters. - -* The parameter lists must give a type for each argument: - the (x, y, z int) shorthand is not allowed. - -* If the return parameter is an error number, it must be named err. - -* If go func name needs to be different from it's winapi dll name, - the winapi name could be specified at the end, after "=" sign, like - //sys LoadLibrary(libname string) (handle uint32, err error) = LoadLibraryA - -* Each function that returns err needs to supply a condition, that - return value of winapi will be tested against to detect failure. - This would set err to windows "last-error", otherwise it will be nil. - The value can be provided at end of //sys declaration, like - //sys LoadLibrary(libname string) (handle uint32, err error) [failretval==-1] = LoadLibraryA - and is [failretval==0] by default. - -Usage: - mksyscall_windows [flags] [path ...] - -The flags are: - -output - Specify output file name (outputs to console if blank). - -trace - Generate print statement after every syscall. -*/ -package main - -import ( - "bufio" - "bytes" - "errors" - "flag" - "fmt" - "go/format" - "go/parser" - "go/token" - "io" - "io/ioutil" - "log" - "os" - "path/filepath" - "runtime" - "sort" - "strconv" - "strings" - "text/template" -) - -var ( - filename = flag.String("output", "", "output file name (standard output if omitted)") - printTraceFlag = flag.Bool("trace", false, "generate print statement after every syscall") - systemDLL = flag.Bool("systemdll", true, "whether all DLLs should be loaded from the Windows system directory") -) - -func trim(s string) string { - return strings.Trim(s, " \t") -} - -var packageName string - -func packagename() string { - return packageName -} - -func syscalldot() string { - if packageName == "syscall" { - return "" - } - return "syscall." -} - -// Param is function parameter -type Param struct { - Name string - Type string - fn *Fn - tmpVarIdx int -} - -// tmpVar returns temp variable name that will be used to represent p during syscall. -func (p *Param) tmpVar() string { - if p.tmpVarIdx < 0 { - p.tmpVarIdx = p.fn.curTmpVarIdx - p.fn.curTmpVarIdx++ - } - return fmt.Sprintf("_p%d", p.tmpVarIdx) -} - -// BoolTmpVarCode returns source code for bool temp variable. -func (p *Param) BoolTmpVarCode() string { - const code = `var %s uint32 - if %s { - %s = 1 - } else { - %s = 0 - }` - tmp := p.tmpVar() - return fmt.Sprintf(code, tmp, p.Name, tmp, tmp) -} - -// SliceTmpVarCode returns source code for slice temp variable. -func (p *Param) SliceTmpVarCode() string { - const code = `var %s *%s - if len(%s) > 0 { - %s = &%s[0] - }` - tmp := p.tmpVar() - return fmt.Sprintf(code, tmp, p.Type[2:], p.Name, tmp, p.Name) -} - -// StringTmpVarCode returns source code for string temp variable. -func (p *Param) StringTmpVarCode() string { - errvar := p.fn.Rets.ErrorVarName() - if errvar == "" { - errvar = "_" - } - tmp := p.tmpVar() - const code = `var %s %s - %s, %s = %s(%s)` - s := fmt.Sprintf(code, tmp, p.fn.StrconvType(), tmp, errvar, p.fn.StrconvFunc(), p.Name) - if errvar == "-" { - return s - } - const morecode = ` - if %s != nil { - return - }` - return s + fmt.Sprintf(morecode, errvar) -} - -// TmpVarCode returns source code for temp variable. -func (p *Param) TmpVarCode() string { - switch { - case p.Type == "bool": - return p.BoolTmpVarCode() - case strings.HasPrefix(p.Type, "[]"): - return p.SliceTmpVarCode() - default: - return "" - } -} - -// TmpVarHelperCode returns source code for helper's temp variable. -func (p *Param) TmpVarHelperCode() string { - if p.Type != "string" { - return "" - } - return p.StringTmpVarCode() -} - -// SyscallArgList returns source code fragments representing p parameter -// in syscall. Slices are translated into 2 syscall parameters: pointer to -// the first element and length. -func (p *Param) SyscallArgList() []string { - t := p.HelperType() - var s string - switch { - case t[0] == '*': - s = fmt.Sprintf("unsafe.Pointer(%s)", p.Name) - case t == "bool": - s = p.tmpVar() - case strings.HasPrefix(t, "[]"): - return []string{ - fmt.Sprintf("uintptr(unsafe.Pointer(%s))", p.tmpVar()), - fmt.Sprintf("uintptr(len(%s))", p.Name), - } - default: - s = p.Name - } - return []string{fmt.Sprintf("uintptr(%s)", s)} -} - -// IsError determines if p parameter is used to return error. -func (p *Param) IsError() bool { - return p.Name == "err" && p.Type == "error" -} - -// HelperType returns type of parameter p used in helper function. -func (p *Param) HelperType() string { - if p.Type == "string" { - return p.fn.StrconvType() - } - return p.Type -} - -// join concatenates parameters ps into a string with sep separator. -// Each parameter is converted into string by applying fn to it -// before conversion. -func join(ps []*Param, fn func(*Param) string, sep string) string { - if len(ps) == 0 { - return "" - } - a := make([]string, 0) - for _, p := range ps { - a = append(a, fn(p)) - } - return strings.Join(a, sep) -} - -// Rets describes function return parameters. -type Rets struct { - Name string - Type string - ReturnsError bool - FailCond string -} - -// ErrorVarName returns error variable name for r. -func (r *Rets) ErrorVarName() string { - if r.ReturnsError { - return "err" - } - if r.Type == "error" { - return r.Name - } - return "" -} - -// ToParams converts r into slice of *Param. -func (r *Rets) ToParams() []*Param { - ps := make([]*Param, 0) - if len(r.Name) > 0 { - ps = append(ps, &Param{Name: r.Name, Type: r.Type}) - } - if r.ReturnsError { - ps = append(ps, &Param{Name: "err", Type: "error"}) - } - return ps -} - -// List returns source code of syscall return parameters. -func (r *Rets) List() string { - s := join(r.ToParams(), func(p *Param) string { return p.Name + " " + p.Type }, ", ") - if len(s) > 0 { - s = "(" + s + ")" - } - return s -} - -// PrintList returns source code of trace printing part correspondent -// to syscall return values. -func (r *Rets) PrintList() string { - return join(r.ToParams(), func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `) -} - -// SetReturnValuesCode returns source code that accepts syscall return values. -func (r *Rets) SetReturnValuesCode() string { - if r.Name == "" && !r.ReturnsError { - return "" - } - retvar := "r0" - if r.Name == "" { - retvar = "r1" - } - errvar := "_" - if r.ReturnsError { - errvar = "e1" - } - return fmt.Sprintf("%s, _, %s := ", retvar, errvar) -} - -func (r *Rets) useLongHandleErrorCode(retvar string) string { - const code = `if %s { - if e1 != 0 { - err = error(e1) - } else { - err = %sEINVAL - } - }` - cond := retvar + " == 0" - if r.FailCond != "" { - cond = strings.Replace(r.FailCond, "failretval", retvar, 1) - } - return fmt.Sprintf(code, cond, syscalldot()) -} - -// SetErrorCode returns source code that sets return parameters. -func (r *Rets) SetErrorCode() string { - const code = `if r0 != 0 { - %s = %sErrno(r0) - }` - if r.Name == "" && !r.ReturnsError { - return "" - } - if r.Name == "" { - return r.useLongHandleErrorCode("r1") - } - if r.Type == "error" { - return fmt.Sprintf(code, r.Name, syscalldot()) - } - s := "" - switch { - case r.Type[0] == '*': - s = fmt.Sprintf("%s = (%s)(unsafe.Pointer(r0))", r.Name, r.Type) - case r.Type == "bool": - s = fmt.Sprintf("%s = r0 != 0", r.Name) - default: - s = fmt.Sprintf("%s = %s(r0)", r.Name, r.Type) - } - if !r.ReturnsError { - return s - } - return s + "\n\t" + r.useLongHandleErrorCode(r.Name) -} - -// Fn describes syscall function. -type Fn struct { - Name string - Params []*Param - Rets *Rets - PrintTrace bool - dllname string - dllfuncname string - src string - // TODO: get rid of this field and just use parameter index instead - curTmpVarIdx int // insure tmp variables have uniq names -} - -// extractParams parses s to extract function parameters. -func extractParams(s string, f *Fn) ([]*Param, error) { - s = trim(s) - if s == "" { - return nil, nil - } - a := strings.Split(s, ",") - ps := make([]*Param, len(a)) - for i := range ps { - s2 := trim(a[i]) - b := strings.Split(s2, " ") - if len(b) != 2 { - b = strings.Split(s2, "\t") - if len(b) != 2 { - return nil, errors.New("Could not extract function parameter from \"" + s2 + "\"") - } - } - ps[i] = &Param{ - Name: trim(b[0]), - Type: trim(b[1]), - fn: f, - tmpVarIdx: -1, - } - } - return ps, nil -} - -// extractSection extracts text out of string s starting after start -// and ending just before end. found return value will indicate success, -// and prefix, body and suffix will contain correspondent parts of string s. -func extractSection(s string, start, end rune) (prefix, body, suffix string, found bool) { - s = trim(s) - if strings.HasPrefix(s, string(start)) { - // no prefix - body = s[1:] - } else { - a := strings.SplitN(s, string(start), 2) - if len(a) != 2 { - return "", "", s, false - } - prefix = a[0] - body = a[1] - } - a := strings.SplitN(body, string(end), 2) - if len(a) != 2 { - return "", "", "", false - } - return prefix, a[0], a[1], true -} - -// newFn parses string s and return created function Fn. -func newFn(s string) (*Fn, error) { - s = trim(s) - f := &Fn{ - Rets: &Rets{}, - src: s, - PrintTrace: *printTraceFlag, - } - // function name and args - prefix, body, s, found := extractSection(s, '(', ')') - if !found || prefix == "" { - return nil, errors.New("Could not extract function name and parameters from \"" + f.src + "\"") - } - f.Name = prefix - var err error - f.Params, err = extractParams(body, f) - if err != nil { - return nil, err - } - // return values - _, body, s, found = extractSection(s, '(', ')') - if found { - r, err := extractParams(body, f) - if err != nil { - return nil, err - } - switch len(r) { - case 0: - case 1: - if r[0].IsError() { - f.Rets.ReturnsError = true - } else { - f.Rets.Name = r[0].Name - f.Rets.Type = r[0].Type - } - case 2: - if !r[1].IsError() { - return nil, errors.New("Only last windows error is allowed as second return value in \"" + f.src + "\"") - } - f.Rets.ReturnsError = true - f.Rets.Name = r[0].Name - f.Rets.Type = r[0].Type - default: - return nil, errors.New("Too many return values in \"" + f.src + "\"") - } - } - // fail condition - _, body, s, found = extractSection(s, '[', ']') - if found { - f.Rets.FailCond = body - } - // dll and dll function names - s = trim(s) - if s == "" { - return f, nil - } - if !strings.HasPrefix(s, "=") { - return nil, errors.New("Could not extract dll name from \"" + f.src + "\"") - } - s = trim(s[1:]) - a := strings.Split(s, ".") - switch len(a) { - case 1: - f.dllfuncname = a[0] - case 2: - f.dllname = a[0] - f.dllfuncname = a[1] - default: - return nil, errors.New("Could not extract dll name from \"" + f.src + "\"") - } - return f, nil -} - -// DLLName returns DLL name for function f. -func (f *Fn) DLLName() string { - if f.dllname == "" { - return "kernel32" - } - return f.dllname -} - -// DLLName returns DLL function name for function f. -func (f *Fn) DLLFuncName() string { - if f.dllfuncname == "" { - return f.Name - } - return f.dllfuncname -} - -// ParamList returns source code for function f parameters. -func (f *Fn) ParamList() string { - return join(f.Params, func(p *Param) string { return p.Name + " " + p.Type }, ", ") -} - -// HelperParamList returns source code for helper function f parameters. -func (f *Fn) HelperParamList() string { - return join(f.Params, func(p *Param) string { return p.Name + " " + p.HelperType() }, ", ") -} - -// ParamPrintList returns source code of trace printing part correspondent -// to syscall input parameters. -func (f *Fn) ParamPrintList() string { - return join(f.Params, func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `) -} - -// ParamCount return number of syscall parameters for function f. -func (f *Fn) ParamCount() int { - n := 0 - for _, p := range f.Params { - n += len(p.SyscallArgList()) - } - return n -} - -// SyscallParamCount determines which version of Syscall/Syscall6/Syscall9/... -// to use. It returns parameter count for correspondent SyscallX function. -func (f *Fn) SyscallParamCount() int { - n := f.ParamCount() - switch { - case n <= 3: - return 3 - case n <= 6: - return 6 - case n <= 9: - return 9 - case n <= 12: - return 12 - case n <= 15: - return 15 - default: - panic("too many arguments to system call") - } -} - -// Syscall determines which SyscallX function to use for function f. -func (f *Fn) Syscall() string { - c := f.SyscallParamCount() - if c == 3 { - return syscalldot() + "Syscall" - } - return syscalldot() + "Syscall" + strconv.Itoa(c) -} - -// SyscallParamList returns source code for SyscallX parameters for function f. -func (f *Fn) SyscallParamList() string { - a := make([]string, 0) - for _, p := range f.Params { - a = append(a, p.SyscallArgList()...) - } - for len(a) < f.SyscallParamCount() { - a = append(a, "0") - } - return strings.Join(a, ", ") -} - -// HelperCallParamList returns source code of call into function f helper. -func (f *Fn) HelperCallParamList() string { - a := make([]string, 0, len(f.Params)) - for _, p := range f.Params { - s := p.Name - if p.Type == "string" { - s = p.tmpVar() - } - a = append(a, s) - } - return strings.Join(a, ", ") -} - -// IsUTF16 is true, if f is W (utf16) function. It is false -// for all A (ascii) functions. -func (f *Fn) IsUTF16() bool { - s := f.DLLFuncName() - return s[len(s)-1] == 'W' -} - -// StrconvFunc returns name of Go string to OS string function for f. -func (f *Fn) StrconvFunc() string { - if f.IsUTF16() { - return syscalldot() + "UTF16PtrFromString" - } - return syscalldot() + "BytePtrFromString" -} - -// StrconvType returns Go type name used for OS string for f. -func (f *Fn) StrconvType() string { - if f.IsUTF16() { - return "*uint16" - } - return "*byte" -} - -// HasStringParam is true, if f has at least one string parameter. -// Otherwise it is false. -func (f *Fn) HasStringParam() bool { - for _, p := range f.Params { - if p.Type == "string" { - return true - } - } - return false -} - -// HelperName returns name of function f helper. -func (f *Fn) HelperName() string { - if !f.HasStringParam() { - return f.Name - } - return "_" + f.Name -} - -// Source files and functions. -type Source struct { - Funcs []*Fn - Files []string - StdLibImports []string - ExternalImports []string -} - -func (src *Source) Import(pkg string) { - src.StdLibImports = append(src.StdLibImports, pkg) - sort.Strings(src.StdLibImports) -} - -func (src *Source) ExternalImport(pkg string) { - src.ExternalImports = append(src.ExternalImports, pkg) - sort.Strings(src.ExternalImports) -} - -// ParseFiles parses files listed in fs and extracts all syscall -// functions listed in sys comments. It returns source files -// and functions collection *Source if successful. -func ParseFiles(fs []string) (*Source, error) { - src := &Source{ - Funcs: make([]*Fn, 0), - Files: make([]string, 0), - StdLibImports: []string{ - "unsafe", - }, - ExternalImports: make([]string, 0), - } - for _, file := range fs { - if err := src.ParseFile(file); err != nil { - return nil, err - } - } - return src, nil -} - -// DLLs return dll names for a source set src. -func (src *Source) DLLs() []string { - uniq := make(map[string]bool) - r := make([]string, 0) - for _, f := range src.Funcs { - name := f.DLLName() - if _, found := uniq[name]; !found { - uniq[name] = true - r = append(r, name) - } - } - return r -} - -// ParseFile adds additional file path to a source set src. -func (src *Source) ParseFile(path string) error { - file, err := os.Open(path) - if err != nil { - return err - } - defer file.Close() - - s := bufio.NewScanner(file) - for s.Scan() { - t := trim(s.Text()) - if len(t) < 7 { - continue - } - if !strings.HasPrefix(t, "//sys") { - continue - } - t = t[5:] - if !(t[0] == ' ' || t[0] == '\t') { - continue - } - f, err := newFn(t[1:]) - if err != nil { - return err - } - src.Funcs = append(src.Funcs, f) - } - if err := s.Err(); err != nil { - return err - } - src.Files = append(src.Files, path) - - // get package name - fset := token.NewFileSet() - _, err = file.Seek(0, 0) - if err != nil { - return err - } - pkg, err := parser.ParseFile(fset, "", file, parser.PackageClauseOnly) - if err != nil { - return err - } - packageName = pkg.Name.Name - - return nil -} - -// IsStdRepo returns true if src is part of standard library. -func (src *Source) IsStdRepo() (bool, error) { - if len(src.Files) == 0 { - return false, errors.New("no input files provided") - } - abspath, err := filepath.Abs(src.Files[0]) - if err != nil { - return false, err - } - goroot := runtime.GOROOT() - if runtime.GOOS == "windows" { - abspath = strings.ToLower(abspath) - goroot = strings.ToLower(goroot) - } - return strings.HasPrefix(abspath, goroot), nil -} - -// Generate output source file from a source set src. -func (src *Source) Generate(w io.Writer) error { - const ( - pkgStd = iota // any package in std library - pkgXSysWindows // x/sys/windows package - pkgOther - ) - isStdRepo, err := src.IsStdRepo() - if err != nil { - return err - } - var pkgtype int - switch { - case isStdRepo: - pkgtype = pkgStd - case packageName == "windows": - // TODO: this needs better logic than just using package name - pkgtype = pkgXSysWindows - default: - pkgtype = pkgOther - } - if *systemDLL { - switch pkgtype { - case pkgStd: - src.Import("internal/syscall/windows/sysdll") - case pkgXSysWindows: - default: - src.ExternalImport("golang.org/x/sys/windows") - } - } - if packageName != "syscall" { - src.Import("syscall") - } - funcMap := template.FuncMap{ - "packagename": packagename, - "syscalldot": syscalldot, - "newlazydll": func(dll string) string { - arg := "\"" + dll + ".dll\"" - if !*systemDLL { - return syscalldot() + "NewLazyDLL(" + arg + ")" - } - switch pkgtype { - case pkgStd: - return syscalldot() + "NewLazyDLL(sysdll.Add(" + arg + "))" - case pkgXSysWindows: - return "NewLazySystemDLL(" + arg + ")" - default: - return "windows.NewLazySystemDLL(" + arg + ")" - } - }, - } - t := template.Must(template.New("main").Funcs(funcMap).Parse(srcTemplate)) - err = t.Execute(w, src) - if err != nil { - return errors.New("Failed to execute template: " + err.Error()) - } - return nil -} - -func usage() { - fmt.Fprintf(os.Stderr, "usage: mksyscall_windows [flags] [path ...]\n") - flag.PrintDefaults() - os.Exit(1) -} - -func main() { - flag.Usage = usage - flag.Parse() - if len(flag.Args()) <= 0 { - fmt.Fprintf(os.Stderr, "no files to parse provided\n") - usage() - } - - src, err := ParseFiles(flag.Args()) - if err != nil { - log.Fatal(err) - } - - var buf bytes.Buffer - if err := src.Generate(&buf); err != nil { - log.Fatal(err) - } - - data, err := format.Source(buf.Bytes()) - if err != nil { - log.Fatal(err) - } - if *filename == "" { - _, err = os.Stdout.Write(data) - } else { - err = ioutil.WriteFile(*filename, data, 0644) - } - if err != nil { - log.Fatal(err) - } -} - -// TODO: use println instead to print in the following template -const srcTemplate = ` - -{{define "main"}}// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT - -package {{packagename}} - -import ( -{{range .StdLibImports}}"{{.}}" -{{end}} - -{{range .ExternalImports}}"{{.}}" -{{end}} -) - -var _ unsafe.Pointer - -var ( -{{template "dlls" .}} -{{template "funcnames" .}}) -{{range .Funcs}}{{if .HasStringParam}}{{template "helperbody" .}}{{end}}{{template "funcbody" .}}{{end}} -{{end}} - -{{/* help functions */}} - -{{define "dlls"}}{{range .DLLs}} mod{{.}} = {{newlazydll .}} -{{end}}{{end}} - -{{define "funcnames"}}{{range .Funcs}} proc{{.DLLFuncName}} = mod{{.DLLName}}.NewProc("{{.DLLFuncName}}") -{{end}}{{end}} - -{{define "helperbody"}} -func {{.Name}}({{.ParamList}}) {{template "results" .}}{ -{{template "helpertmpvars" .}} return {{.HelperName}}({{.HelperCallParamList}}) -} -{{end}} - -{{define "funcbody"}} -func {{.HelperName}}({{.HelperParamList}}) {{template "results" .}}{ -{{template "tmpvars" .}} {{template "syscall" .}} -{{template "seterror" .}}{{template "printtrace" .}} return -} -{{end}} - -{{define "helpertmpvars"}}{{range .Params}}{{if .TmpVarHelperCode}} {{.TmpVarHelperCode}} -{{end}}{{end}}{{end}} - -{{define "tmpvars"}}{{range .Params}}{{if .TmpVarCode}} {{.TmpVarCode}} -{{end}}{{end}}{{end}} - -{{define "results"}}{{if .Rets.List}}{{.Rets.List}} {{end}}{{end}} - -{{define "syscall"}}{{.Rets.SetReturnValuesCode}}{{.Syscall}}(proc{{.DLLFuncName}}.Addr(), {{.ParamCount}}, {{.SyscallParamList}}){{end}} - -{{define "seterror"}}{{if .Rets.SetErrorCode}} {{.Rets.SetErrorCode}} -{{end}}{{end}} - -{{define "printtrace"}}{{if .PrintTrace}} print("SYSCALL: {{.Name}}(", {{.ParamPrintList}}") (", {{.Rets.PrintList}}")\n") -{{end}}{{end}} - -` diff --git a/serial.go b/serial.go index 48392c5..d61f658 100644 --- a/serial.go +++ b/serial.go @@ -6,7 +6,7 @@ package serial // import "go.bug.st/serial.v1" -//go:generate go run extras/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go +//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go // Port is the interface for a serial Port type Port interface { diff --git a/zsyscall_windows.go b/zsyscall_windows.go index a72df76..896fd40 100644 --- a/zsyscall_windows.go +++ b/zsyscall_windows.go @@ -11,6 +11,31 @@ import ( var _ unsafe.Pointer +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + var ( modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") modkernel32 = windows.NewLazySystemDLL("kernel32.dll") @@ -38,7 +63,7 @@ func getCommState(handle syscall.Handle, dcb *dcb) (err error) { r1, _, e1 := syscall.Syscall(procGetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0) if r1 == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -50,7 +75,7 @@ func setCommState(handle syscall.Handle, dcb *dcb) (err error) { r1, _, e1 := syscall.Syscall(procSetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0) if r1 == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -62,7 +87,7 @@ func setCommTimeouts(handle syscall.Handle, timeouts *commTimeouts) (err error) r1, _, e1 := syscall.Syscall(procSetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0) if r1 == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -99,7 +124,7 @@ func createEvent(eventAttributes *uint32, manualReset bool, initialState bool, n handle = syscall.Handle(r0) if handle == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -111,7 +136,7 @@ func resetEvent(handle syscall.Handle) (err error) { r1, _, e1 := syscall.Syscall(procResetEvent.Addr(), 1, uintptr(handle), 0, 0) if r1 == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -129,7 +154,7 @@ func getOverlappedResult(handle syscall.Handle, overlapEvent *syscall.Overlapped r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapEvent)), uintptr(unsafe.Pointer(n)), uintptr(_p0), 0, 0) if r1 == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } From ccc846995ce1f04fc987ff4d4ff82ac26775d87c Mon Sep 17 00:00:00 2001 From: Veniamin Albaev Date: Thu, 9 Mar 2017 10:53:16 +0300 Subject: [PATCH 052/181] TravisCI: add go 1.8.x --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 16a513d..270c7e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: go go: - 1.7.x + - 1.8.x go_import_path: go.bug.st/serial.v1 From 4d886c102874b3678df370d74d99d84b8ce9ba9b Mon Sep 17 00:00:00 2001 From: Veniamin Albaev Date: Thu, 9 Mar 2017 11:04:34 +0300 Subject: [PATCH 053/181] Unix: move from syscall to golang.org/x/sys/unix Prepare to using `unix.TCFLSH`/`unix.TIOCFLUSH` which is missing in `syscall` package --- serial_darwin.go | 6 +- serial_darwin_386.go | 50 +++++++------- serial_darwin_amd64.go | 50 +++++++------- serial_freebsd.go | 58 ++++++++-------- serial_linux.go | 80 +++++++++++----------- serial_unix.go | 151 +++++++++++++++++++++-------------------- zsyscall_darwin.go | 4 +- zsyscall_freebsd.go | 4 +- zsyscall_linux.go | 4 +- 9 files changed, 204 insertions(+), 203 deletions(-) diff --git a/serial_darwin.go b/serial_darwin.go index 13cf862..845b51f 100644 --- a/serial_darwin.go +++ b/serial_darwin.go @@ -6,10 +6,10 @@ package serial // import "go.bug.st/serial.v1" -import "syscall" +import "golang.org/x/sys/unix" const devFolder = "/dev" const regexFilter = "^(cu|tty)\\..*" -const ioctlTcgetattr = syscall.TIOCGETA -const ioctlTcsetattr = syscall.TIOCSETA +const ioctlTcgetattr = unix.TIOCGETA +const ioctlTcsetattr = unix.TIOCSETA diff --git a/serial_darwin_386.go b/serial_darwin_386.go index 4f96df7..4200f26 100644 --- a/serial_darwin_386.go +++ b/serial_darwin_386.go @@ -6,38 +6,38 @@ package serial // import "go.bug.st/serial.v1" -import "syscall" +import "golang.org/x/sys/unix" // termios manipulation functions var baudrateMap = map[int]uint32{ - 0: syscall.B9600, // Default to 9600 - 50: syscall.B50, - 75: syscall.B75, - 110: syscall.B110, - 134: syscall.B134, - 150: syscall.B150, - 200: syscall.B200, - 300: syscall.B300, - 600: syscall.B600, - 1200: syscall.B1200, - 1800: syscall.B1800, - 2400: syscall.B2400, - 4800: syscall.B4800, - 9600: syscall.B9600, - 19200: syscall.B19200, - 38400: syscall.B38400, - 57600: syscall.B57600, - 115200: syscall.B115200, - 230400: syscall.B230400, + 0: unix.B9600, // Default to 9600 + 50: unix.B50, + 75: unix.B75, + 110: unix.B110, + 134: unix.B134, + 150: unix.B150, + 200: unix.B200, + 300: unix.B300, + 600: unix.B600, + 1200: unix.B1200, + 1800: unix.B1800, + 2400: unix.B2400, + 4800: unix.B4800, + 9600: unix.B9600, + 19200: unix.B19200, + 38400: unix.B38400, + 57600: unix.B57600, + 115200: unix.B115200, + 230400: unix.B230400, } var databitsMap = map[int]uint32{ - 0: syscall.CS8, // Default to 8 bits - 5: syscall.CS5, - 6: syscall.CS6, - 7: syscall.CS7, - 8: syscall.CS8, + 0: unix.CS8, // Default to 8 bits + 5: unix.CS5, + 6: unix.CS6, + 7: unix.CS7, + 8: unix.CS8, } const tcCMSPAR uint32 = 0 // may be CMSPAR or PAREXT diff --git a/serial_darwin_amd64.go b/serial_darwin_amd64.go index 6ea59d5..d7109c0 100644 --- a/serial_darwin_amd64.go +++ b/serial_darwin_amd64.go @@ -6,38 +6,38 @@ package serial // import "go.bug.st/serial.v1" -import "syscall" +import "golang.org/x/sys/unix" // termios manipulation functions var baudrateMap = map[int]uint64{ - 0: syscall.B9600, // Default to 9600 - 50: syscall.B50, - 75: syscall.B75, - 110: syscall.B110, - 134: syscall.B134, - 150: syscall.B150, - 200: syscall.B200, - 300: syscall.B300, - 600: syscall.B600, - 1200: syscall.B1200, - 1800: syscall.B1800, - 2400: syscall.B2400, - 4800: syscall.B4800, - 9600: syscall.B9600, - 19200: syscall.B19200, - 38400: syscall.B38400, - 57600: syscall.B57600, - 115200: syscall.B115200, - 230400: syscall.B230400, + 0: unix.B9600, // Default to 9600 + 50: unix.B50, + 75: unix.B75, + 110: unix.B110, + 134: unix.B134, + 150: unix.B150, + 200: unix.B200, + 300: unix.B300, + 600: unix.B600, + 1200: unix.B1200, + 1800: unix.B1800, + 2400: unix.B2400, + 4800: unix.B4800, + 9600: unix.B9600, + 19200: unix.B19200, + 38400: unix.B38400, + 57600: unix.B57600, + 115200: unix.B115200, + 230400: unix.B230400, } var databitsMap = map[int]uint64{ - 0: syscall.CS8, // Default to 8 bits - 5: syscall.CS5, - 6: syscall.CS6, - 7: syscall.CS7, - 8: syscall.CS8, + 0: unix.CS8, // Default to 8 bits + 5: unix.CS5, + 6: unix.CS6, + 7: unix.CS7, + 8: unix.CS8, } const tcCMSPAR uint64 = 0 // may be CMSPAR or PAREXT diff --git a/serial_freebsd.go b/serial_freebsd.go index ae5ff09..0e54166 100644 --- a/serial_freebsd.go +++ b/serial_freebsd.go @@ -6,7 +6,7 @@ package serial // import "go.bug.st/serial.v1" -import "syscall" +import "golang.org/x/sys/unix" const devFolder = "/dev" const regexFilter = "^(cu|tty)\\..*" @@ -14,35 +14,35 @@ const regexFilter = "^(cu|tty)\\..*" // termios manipulation functions var baudrateMap = map[int]uint32{ - 0: syscall.B9600, // Default to 9600 - 50: syscall.B50, - 75: syscall.B75, - 110: syscall.B110, - 134: syscall.B134, - 150: syscall.B150, - 200: syscall.B200, - 300: syscall.B300, - 600: syscall.B600, - 1200: syscall.B1200, - 1800: syscall.B1800, - 2400: syscall.B2400, - 4800: syscall.B4800, - 9600: syscall.B9600, - 19200: syscall.B19200, - 38400: syscall.B38400, - 57600: syscall.B57600, - 115200: syscall.B115200, - 230400: syscall.B230400, - 460800: syscall.B460800, - 921600: syscall.B921600, + 0: unix.B9600, // Default to 9600 + 50: unix.B50, + 75: unix.B75, + 110: unix.B110, + 134: unix.B134, + 150: unix.B150, + 200: unix.B200, + 300: unix.B300, + 600: unix.B600, + 1200: unix.B1200, + 1800: unix.B1800, + 2400: unix.B2400, + 4800: unix.B4800, + 9600: unix.B9600, + 19200: unix.B19200, + 38400: unix.B38400, + 57600: unix.B57600, + 115200: unix.B115200, + 230400: unix.B230400, + 460800: unix.B460800, + 921600: unix.B921600, } var databitsMap = map[int]uint32{ - 0: syscall.CS8, // Default to 8 bits - 5: syscall.CS5, - 6: syscall.CS6, - 7: syscall.CS7, - 8: syscall.CS8, + 0: unix.CS8, // Default to 8 bits + 5: unix.CS5, + 6: unix.CS6, + 7: unix.CS7, + 8: unix.CS8, } const tcCMSPAR uint32 = 0 // may be CMSPAR or PAREXT @@ -53,5 +53,5 @@ const tcCRTS_IFLOW uint32 = 0x00020000 const tcCRTSCTS uint32 = tcCCTS_OFLOW -const ioctlTcgetattr = syscall.TIOCGETA -const ioctlTcsetattr = syscall.TIOCSETA +const ioctlTcgetattr = unix.TIOCGETA +const ioctlTcsetattr = unix.TIOCSETA diff --git a/serial_linux.go b/serial_linux.go index 0ef03b8..b41f25b 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -6,7 +6,7 @@ package serial // import "go.bug.st/serial.v1" -import "syscall" +import "golang.org/x/sys/unix" const devFolder = "/dev" const regexFilter = "(ttyS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO)[0-9]{1,3}" @@ -14,51 +14,51 @@ const regexFilter = "(ttyS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO)[0-9]{1,3}" // termios manipulation functions var baudrateMap = map[int]uint32{ - 0: syscall.B9600, // Default to 9600 - 50: syscall.B50, - 75: syscall.B75, - 110: syscall.B110, - 134: syscall.B134, - 150: syscall.B150, - 200: syscall.B200, - 300: syscall.B300, - 600: syscall.B600, - 1200: syscall.B1200, - 1800: syscall.B1800, - 2400: syscall.B2400, - 4800: syscall.B4800, - 9600: syscall.B9600, - 19200: syscall.B19200, - 38400: syscall.B38400, - 57600: syscall.B57600, - 115200: syscall.B115200, - 230400: syscall.B230400, - 460800: syscall.B460800, - 500000: syscall.B500000, - 576000: syscall.B576000, - 921600: syscall.B921600, - 1000000: syscall.B1000000, - 1152000: syscall.B1152000, - 1500000: syscall.B1500000, - 2000000: syscall.B2000000, - 2500000: syscall.B2500000, - 3000000: syscall.B3000000, - 3500000: syscall.B3500000, - 4000000: syscall.B4000000, + 0: unix.B9600, // Default to 9600 + 50: unix.B50, + 75: unix.B75, + 110: unix.B110, + 134: unix.B134, + 150: unix.B150, + 200: unix.B200, + 300: unix.B300, + 600: unix.B600, + 1200: unix.B1200, + 1800: unix.B1800, + 2400: unix.B2400, + 4800: unix.B4800, + 9600: unix.B9600, + 19200: unix.B19200, + 38400: unix.B38400, + 57600: unix.B57600, + 115200: unix.B115200, + 230400: unix.B230400, + 460800: unix.B460800, + 500000: unix.B500000, + 576000: unix.B576000, + 921600: unix.B921600, + 1000000: unix.B1000000, + 1152000: unix.B1152000, + 1500000: unix.B1500000, + 2000000: unix.B2000000, + 2500000: unix.B2500000, + 3000000: unix.B3000000, + 3500000: unix.B3500000, + 4000000: unix.B4000000, } var databitsMap = map[int]uint32{ - 0: syscall.CS8, // Default to 8 bits - 5: syscall.CS5, - 6: syscall.CS6, - 7: syscall.CS7, - 8: syscall.CS8, + 0: unix.CS8, // Default to 8 bits + 5: unix.CS5, + 6: unix.CS6, + 7: unix.CS7, + 8: unix.CS8, } const tcCMSPAR uint32 = 0 // may be CMSPAR or PAREXT -const tcIUCLC = syscall.IUCLC +const tcIUCLC = unix.IUCLC const tcCRTSCTS uint32 = 0x80000000 -const ioctlTcgetattr = syscall.TCGETS -const ioctlTcsetattr = syscall.TCSETS +const ioctlTcgetattr = unix.TCGETS +const ioctlTcsetattr = unix.TCSETS diff --git a/serial_unix.go b/serial_unix.go index 8dc2c74..58ee9c0 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -13,9 +13,10 @@ import ( "regexp" "strings" "sync" - "syscall" "unsafe" + "golang.org/x/sys/unix" + "go.bug.st/serial.v1/unixutils" ) @@ -30,7 +31,7 @@ type unixPort struct { func (port *unixPort) Close() error { // Close port port.releaseExclusiveAccess() - if err := syscall.Close(port.handle); err != nil { + if err := unix.Close(port.handle); err != nil { return err } port.opened = false @@ -66,11 +67,11 @@ func (port *unixPort) Read(p []byte) (n int, err error) { if res.IsReadable(port.closeSignal.ReadFD()) { return 0, &PortError{code: PortClosed} } - return syscall.Read(port.handle, p) + return unix.Read(port.handle, p) } func (port *unixPort) Write(p []byte) (n int, err error) { - return syscall.Write(port.handle, p) + return unix.Write(port.handle, p) } func (port *unixPort) SetMode(mode *Mode) error { @@ -99,9 +100,9 @@ func (port *unixPort) SetDTR(dtr bool) error { return err } if dtr { - status |= syscall.TIOCM_DTR + status |= unix.TIOCM_DTR } else { - status &^= syscall.TIOCM_DTR + status &^= unix.TIOCM_DTR } return port.setModemBitsStatus(status) } @@ -112,9 +113,9 @@ func (port *unixPort) SetRTS(rts bool) error { return err } if rts { - status |= syscall.TIOCM_RTS + status |= unix.TIOCM_RTS } else { - status &^= syscall.TIOCM_RTS + status &^= unix.TIOCM_RTS } return port.setModemBitsStatus(status) } @@ -125,20 +126,20 @@ func (port *unixPort) GetModemStatusBits() (*ModemStatusBits, error) { return nil, err } return &ModemStatusBits{ - CTS: (status & syscall.TIOCM_CTS) != 0, - DCD: (status & syscall.TIOCM_CD) != 0, - DSR: (status & syscall.TIOCM_DSR) != 0, - RI: (status & syscall.TIOCM_RI) != 0, + CTS: (status & unix.TIOCM_CTS) != 0, + DCD: (status & unix.TIOCM_CD) != 0, + DSR: (status & unix.TIOCM_DSR) != 0, + RI: (status & unix.TIOCM_RI) != 0, }, nil } func nativeOpen(portName string, mode *Mode) (*unixPort, error) { - h, err := syscall.Open(portName, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NDELAY, 0) + h, err := unix.Open(portName, unix.O_RDWR|unix.O_NOCTTY|unix.O_NDELAY, 0) if err != nil { switch err { - case syscall.EBUSY: + case unix.EBUSY: return nil, &PortError{code: PortBusy} - case syscall.EACCES: + case unix.EACCES: return nil, &PortError{code: PermissionDenied} } return nil, err @@ -171,7 +172,7 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { return nil, &PortError{code: InvalidSerialPort} } - syscall.SetNonblock(h, false) + unix.SetNonblock(h, false) port.acquireExclusiveAccess() @@ -232,7 +233,7 @@ func nativeGetPortsList() ([]string, error) { // termios manipulation functions -func setTermSettingsBaudrate(speed int, settings *syscall.Termios) error { +func setTermSettingsBaudrate(speed int, settings *unix.Termios) error { baudrate, ok := baudrateMap[speed] if !ok { return &PortError{code: InvalidSpeed} @@ -248,72 +249,72 @@ func setTermSettingsBaudrate(speed int, settings *syscall.Termios) error { return nil } -func setTermSettingsParity(parity Parity, settings *syscall.Termios) error { +func setTermSettingsParity(parity Parity, settings *unix.Termios) error { switch parity { case NoParity: - settings.Cflag &^= syscall.PARENB - settings.Cflag &^= syscall.PARODD + settings.Cflag &^= unix.PARENB + settings.Cflag &^= unix.PARODD settings.Cflag &^= tcCMSPAR - settings.Iflag &^= syscall.INPCK + settings.Iflag &^= unix.INPCK case OddParity: - settings.Cflag |= syscall.PARENB - settings.Cflag |= syscall.PARODD + settings.Cflag |= unix.PARENB + settings.Cflag |= unix.PARODD settings.Cflag &^= tcCMSPAR - settings.Iflag |= syscall.INPCK + settings.Iflag |= unix.INPCK case EvenParity: - settings.Cflag |= syscall.PARENB - settings.Cflag &^= syscall.PARODD + settings.Cflag |= unix.PARENB + settings.Cflag &^= unix.PARODD settings.Cflag &^= tcCMSPAR - settings.Iflag |= syscall.INPCK + settings.Iflag |= unix.INPCK case MarkParity: if tcCMSPAR == 0 { return &PortError{code: InvalidParity} } - settings.Cflag |= syscall.PARENB - settings.Cflag |= syscall.PARODD + settings.Cflag |= unix.PARENB + settings.Cflag |= unix.PARODD settings.Cflag |= tcCMSPAR - settings.Iflag |= syscall.INPCK + settings.Iflag |= unix.INPCK case SpaceParity: if tcCMSPAR == 0 { return &PortError{code: InvalidParity} } - settings.Cflag |= syscall.PARENB - settings.Cflag &^= syscall.PARODD + settings.Cflag |= unix.PARENB + settings.Cflag &^= unix.PARODD settings.Cflag |= tcCMSPAR - settings.Iflag |= syscall.INPCK + settings.Iflag |= unix.INPCK default: return &PortError{code: InvalidParity} } return nil } -func setTermSettingsDataBits(bits int, settings *syscall.Termios) error { +func setTermSettingsDataBits(bits int, settings *unix.Termios) error { databits, ok := databitsMap[bits] if !ok { return &PortError{code: InvalidDataBits} } // Remove previous databits setting - settings.Cflag &^= syscall.CSIZE + settings.Cflag &^= unix.CSIZE // Set requested databits settings.Cflag |= databits return nil } -func setTermSettingsStopBits(bits StopBits, settings *syscall.Termios) error { +func setTermSettingsStopBits(bits StopBits, settings *unix.Termios) error { switch bits { case OneStopBit: - settings.Cflag &^= syscall.CSTOPB + settings.Cflag &^= unix.CSTOPB case OnePointFiveStopBits: return &PortError{code: InvalidStopBits} case TwoStopBits: - settings.Cflag |= syscall.CSTOPB + settings.Cflag |= unix.CSTOPB default: return &PortError{code: InvalidStopBits} } return nil } -func setTermSettingsCtsRts(enable bool, settings *syscall.Termios) { +func setTermSettingsCtsRts(enable bool, settings *unix.Termios) { if enable { settings.Cflag |= tcCRTSCTS } else { @@ -321,70 +322,70 @@ func setTermSettingsCtsRts(enable bool, settings *syscall.Termios) { } } -func setRawMode(settings *syscall.Termios) { +func setRawMode(settings *unix.Termios) { // Set local mode - settings.Cflag |= syscall.CREAD - settings.Cflag |= syscall.CLOCAL + settings.Cflag |= unix.CREAD + settings.Cflag |= unix.CLOCAL // Set raw mode - settings.Lflag &^= syscall.ICANON - settings.Lflag &^= syscall.ECHO - settings.Lflag &^= syscall.ECHOE - settings.Lflag &^= syscall.ECHOK - settings.Lflag &^= syscall.ECHONL - settings.Lflag &^= syscall.ECHOCTL - settings.Lflag &^= syscall.ECHOPRT - settings.Lflag &^= syscall.ECHOKE - settings.Lflag &^= syscall.ISIG - settings.Lflag &^= syscall.IEXTEN - - settings.Iflag &^= syscall.IXON - settings.Iflag &^= syscall.IXOFF - settings.Iflag &^= syscall.IXANY - settings.Iflag &^= syscall.INPCK - settings.Iflag &^= syscall.IGNPAR - settings.Iflag &^= syscall.PARMRK - settings.Iflag &^= syscall.ISTRIP - settings.Iflag &^= syscall.IGNBRK - settings.Iflag &^= syscall.BRKINT - settings.Iflag &^= syscall.INLCR - settings.Iflag &^= syscall.IGNCR - settings.Iflag &^= syscall.ICRNL + settings.Lflag &^= unix.ICANON + settings.Lflag &^= unix.ECHO + settings.Lflag &^= unix.ECHOE + settings.Lflag &^= unix.ECHOK + settings.Lflag &^= unix.ECHONL + settings.Lflag &^= unix.ECHOCTL + settings.Lflag &^= unix.ECHOPRT + settings.Lflag &^= unix.ECHOKE + settings.Lflag &^= unix.ISIG + settings.Lflag &^= unix.IEXTEN + + settings.Iflag &^= unix.IXON + settings.Iflag &^= unix.IXOFF + settings.Iflag &^= unix.IXANY + settings.Iflag &^= unix.INPCK + settings.Iflag &^= unix.IGNPAR + settings.Iflag &^= unix.PARMRK + settings.Iflag &^= unix.ISTRIP + settings.Iflag &^= unix.IGNBRK + settings.Iflag &^= unix.BRKINT + settings.Iflag &^= unix.INLCR + settings.Iflag &^= unix.IGNCR + settings.Iflag &^= unix.ICRNL settings.Iflag &^= tcIUCLC - settings.Oflag &^= syscall.OPOST + settings.Oflag &^= unix.OPOST // Block reads until at least one char is available (no timeout) - settings.Cc[syscall.VMIN] = 1 - settings.Cc[syscall.VTIME] = 0 + settings.Cc[unix.VMIN] = 1 + settings.Cc[unix.VTIME] = 0 } // native syscall wrapper functions -func (port *unixPort) getTermSettings() (*syscall.Termios, error) { - settings := &syscall.Termios{} +func (port *unixPort) getTermSettings() (*unix.Termios, error) { + settings := &unix.Termios{} err := ioctl(port.handle, ioctlTcgetattr, uintptr(unsafe.Pointer(settings))) return settings, err } -func (port *unixPort) setTermSettings(settings *syscall.Termios) error { +func (port *unixPort) setTermSettings(settings *unix.Termios) error { return ioctl(port.handle, ioctlTcsetattr, uintptr(unsafe.Pointer(settings))) } func (port *unixPort) getModemBitsStatus() (int, error) { var status int - err := ioctl(port.handle, syscall.TIOCMGET, uintptr(unsafe.Pointer(&status))) + err := ioctl(port.handle, unix.TIOCMGET, uintptr(unsafe.Pointer(&status))) return status, err } func (port *unixPort) setModemBitsStatus(status int) error { - return ioctl(port.handle, syscall.TIOCMSET, uintptr(unsafe.Pointer(&status))) + return ioctl(port.handle, unix.TIOCMSET, uintptr(unsafe.Pointer(&status))) } func (port *unixPort) acquireExclusiveAccess() error { - return ioctl(port.handle, syscall.TIOCEXCL, 0) + return ioctl(port.handle, unix.TIOCEXCL, 0) } func (port *unixPort) releaseExclusiveAccess() error { - return ioctl(port.handle, syscall.TIOCNXCL, 0) + return ioctl(port.handle, unix.TIOCNXCL, 0) } diff --git a/zsyscall_darwin.go b/zsyscall_darwin.go index 49cfac4..cef1732 100644 --- a/zsyscall_darwin.go +++ b/zsyscall_darwin.go @@ -10,10 +10,10 @@ package serial // import "go.bug.st/serial.v1" -import "syscall" +import "golang.org/x/sys/unix" func ioctl(fd int, req uint64, data uintptr) (err error) { - _, _, e1 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data)) + _, _, e1 := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data)) if e1 != 0 { err = e1 } diff --git a/zsyscall_freebsd.go b/zsyscall_freebsd.go index 702fd99..9e6765d 100644 --- a/zsyscall_freebsd.go +++ b/zsyscall_freebsd.go @@ -10,10 +10,10 @@ package serial // import "go.bug.st/serial.v1" -import "syscall" +import "golang.org/x/sys/unix" func ioctl(fd int, req uint64, data uintptr) (err error) { - _, _, e1 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data)) + _, _, e1 := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data)) if e1 != 0 { err = e1 } diff --git a/zsyscall_linux.go b/zsyscall_linux.go index 60c2d75..d93c1fb 100644 --- a/zsyscall_linux.go +++ b/zsyscall_linux.go @@ -10,10 +10,10 @@ package serial // import "go.bug.st/serial.v1" -import "syscall" +import "golang.org/x/sys/unix" func ioctl(fd int, req uint64, data uintptr) (err error) { - _, _, e1 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data)) + _, _, e1 := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data)) if e1 != 0 { err = e1 } From 8b7629bfe16d4477e38406d74ababcc3c61cf883 Mon Sep 17 00:00:00 2001 From: Veniamin Albaev Date: Thu, 9 Mar 2017 11:11:06 +0300 Subject: [PATCH 054/181] Added buffer flush methods + serial.Port.ResetInputBuffer() + serial.Port.ResetOutputBuffer() --- serial.go | 6 ++++++ serial_darwin.go | 1 + serial_freebsd.go | 1 + serial_linux.go | 1 + serial_unix.go | 8 ++++++++ serial_windows.go | 15 +++++++++++++++ syscall_windows.go | 3 +++ zsyscall_windows.go | 13 +++++++++++++ 8 files changed, 48 insertions(+) diff --git a/serial.go b/serial.go index d61f658..46eb521 100644 --- a/serial.go +++ b/serial.go @@ -24,6 +24,12 @@ type Port interface { // Returns the number of bytes written. Write(p []byte) (n int, err error) + // ResetInputBuffer Purges port read buffer + ResetInputBuffer() error + + // ResetOutputBuffer Purges port write buffer + ResetOutputBuffer() error + // SetDTR sets the modem status bit DataTerminalReady SetDTR(dtr bool) error diff --git a/serial_darwin.go b/serial_darwin.go index 845b51f..cfbde8f 100644 --- a/serial_darwin.go +++ b/serial_darwin.go @@ -13,3 +13,4 @@ const regexFilter = "^(cu|tty)\\..*" const ioctlTcgetattr = unix.TIOCGETA const ioctlTcsetattr = unix.TIOCSETA +const ioctlTcflsh = unix.TIOCFLUSH diff --git a/serial_freebsd.go b/serial_freebsd.go index 0e54166..a7fde38 100644 --- a/serial_freebsd.go +++ b/serial_freebsd.go @@ -55,3 +55,4 @@ const tcCRTSCTS uint32 = tcCCTS_OFLOW const ioctlTcgetattr = unix.TIOCGETA const ioctlTcsetattr = unix.TIOCSETA +const ioctlTcflsh = unix.TIOCFLUSH diff --git a/serial_linux.go b/serial_linux.go index b41f25b..d05287c 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -62,3 +62,4 @@ const tcCRTSCTS uint32 = 0x80000000 const ioctlTcgetattr = unix.TCGETS const ioctlTcsetattr = unix.TCSETS +const ioctlTcflsh = unix.TCFLSH diff --git a/serial_unix.go b/serial_unix.go index 58ee9c0..69bbceb 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -74,6 +74,14 @@ func (port *unixPort) Write(p []byte) (n int, err error) { return unix.Write(port.handle, p) } +func (port *unixPort) ResetInputBuffer() error { + return ioctl(port.handle, ioctlTcflsh, unix.TCIFLUSH) +} + +func (port *unixPort) ResetOutputBuffer() error { + return ioctl(port.handle, ioctlTcflsh, unix.TCOFLUSH) +} + func (port *unixPort) SetMode(mode *Mode) error { settings, err := port.getTermSettings() if err != nil { diff --git a/serial_windows.go b/serial_windows.go index aabb7be..4a504ef 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -115,6 +115,21 @@ func (port *windowsPort) Write(p []byte) (int, error) { return int(writed), err } +const ( + purgeRxAbort uint32 = 0x0002 + purgeRxClear = 0x0008 + purgeTxAbort = 0x0001 + purgeTxClear = 0x0004 +) + +func (port *windowsPort) ResetInputBuffer() error { + return purgeComm(port.handle, purgeRxClear|purgeRxAbort) +} + +func (port *windowsPort) ResetOutputBuffer() error { + return purgeComm(port.handle, purgeTxClear|purgeTxAbort) +} + const ( dcbBinary uint32 = 0x00000001 dcbParity = 0x00000002 diff --git a/syscall_windows.go b/syscall_windows.go index 529b4df..989a61a 100644 --- a/syscall_windows.go +++ b/syscall_windows.go @@ -23,3 +23,6 @@ package serial // import "go.bug.st/serial.v1" //sys resetEvent(handle syscall.Handle) (err error) = ResetEvent //sys getOverlappedResult(handle syscall.Handle, overlapEvent *syscall.Overlapped, n *uint32, wait bool) (err error) = GetOverlappedResult + +//sys purgeComm(handle syscall.Handle, flags uint32) (err error) = PurgeComm + diff --git a/zsyscall_windows.go b/zsyscall_windows.go index 896fd40..f6005ea 100644 --- a/zsyscall_windows.go +++ b/zsyscall_windows.go @@ -49,6 +49,7 @@ var ( procCreateEventW = modkernel32.NewProc("CreateEventW") procResetEvent = modkernel32.NewProc("ResetEvent") procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") + procPurgeComm = modkernel32.NewProc("PurgeComm") ) func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) { @@ -161,3 +162,15 @@ func getOverlappedResult(handle syscall.Handle, overlapEvent *syscall.Overlapped } return } + +func purgeComm(handle syscall.Handle, flags uint32) (err error) { + r1, _, e1 := syscall.Syscall(procPurgeComm.Addr(), 2, uintptr(handle), uintptr(flags), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} From 4cc831e19211fe5153ff5c9ff78851db4a4711f1 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 12 Mar 2017 21:23:58 +0100 Subject: [PATCH 055/181] Some serial constants replaced by sys/unix package --- serial_linux.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serial_linux.go b/serial_linux.go index d05287c..c8f5927 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -55,10 +55,10 @@ var databitsMap = map[int]uint32{ 8: unix.CS8, } -const tcCMSPAR uint32 = 0 // may be CMSPAR or PAREXT +const tcCMSPAR = unix.CMSPAR const tcIUCLC = unix.IUCLC -const tcCRTSCTS uint32 = 0x80000000 +const tcCRTSCTS uint32 = unix.CRTSCTS const ioctlTcgetattr = unix.TCGETS const ioctlTcsetattr = unix.TCSETS From 4366d1c449e9c39a4e3ff5e9acd318070e49ccc8 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 4 Mar 2017 22:40:50 +0100 Subject: [PATCH 056/181] Tentative support for OpenBSD See #27 --- .travis.yml | 3 ++ serial_darwin_386.go | 4 +++ serial_darwin_amd64.go | 4 +++ serial_freebsd.go | 4 +++ serial_linux.go | 4 +++ serial_openbsd.go | 62 ++++++++++++++++++++++++++++++++++++++++++ serial_unix.go | 6 ++-- syscall_openbsd.go | 9 ++++++ unixutils/pipe.go | 2 +- unixutils/select.go | 2 +- zsyscall_openbsd.go | 21 ++++++++++++++ 11 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 serial_openbsd.go create mode 100644 syscall_openbsd.go create mode 100644 zsyscall_openbsd.go diff --git a/.travis.yml b/.travis.yml index 270c7e5..e962e65 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,9 @@ env: - TEST_OS=darwin TEST_ARCH=amd64 - TEST_OS=freebsd TEST_ARCH=amd64 - TEST_OS=dragonfly TEST_ARCH=amd64 + - TEST_OS=openbsd TEST_ARCH=amd64 + - TEST_OS=openbsd TEST_ARCH=386 + - TEST_OS=openbsd TEST_ARCH=arm matrix: allow_failures: diff --git a/serial_darwin_386.go b/serial_darwin_386.go index 4200f26..9b9e7fd 100644 --- a/serial_darwin_386.go +++ b/serial_darwin_386.go @@ -47,3 +47,7 @@ const tcCCTS_OFLOW uint32 = 0x00010000 const tcCRTS_IFLOW uint32 = 0x00020000 const tcCRTSCTS uint32 = (tcCCTS_OFLOW | tcCRTS_IFLOW) + +func toTermiosSpeedType(speed uint32) uint32 { + return speed +} diff --git a/serial_darwin_amd64.go b/serial_darwin_amd64.go index d7109c0..b0b155d 100644 --- a/serial_darwin_amd64.go +++ b/serial_darwin_amd64.go @@ -47,3 +47,7 @@ const tcCCTS_OFLOW uint64 = 0x00010000 const tcCRTS_IFLOW uint64 = 0x00020000 const tcCRTSCTS uint64 = (tcCCTS_OFLOW | tcCRTS_IFLOW) + +func toTermiosSpeedType(speed uint64) uint64 { + return speed +} diff --git a/serial_freebsd.go b/serial_freebsd.go index a7fde38..4083cd6 100644 --- a/serial_freebsd.go +++ b/serial_freebsd.go @@ -56,3 +56,7 @@ const tcCRTSCTS uint32 = tcCCTS_OFLOW const ioctlTcgetattr = unix.TIOCGETA const ioctlTcsetattr = unix.TIOCSETA const ioctlTcflsh = unix.TIOCFLUSH + +func toTermiosSpeedType(speed uint32) uint32 { + return speed +} diff --git a/serial_linux.go b/serial_linux.go index c8f5927..e49a847 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -63,3 +63,7 @@ const tcCRTSCTS uint32 = unix.CRTSCTS const ioctlTcgetattr = unix.TCGETS const ioctlTcsetattr = unix.TCSETS const ioctlTcflsh = unix.TCFLSH + +func toTermiosSpeedType(speed uint32) uint32 { + return speed +} diff --git a/serial_openbsd.go b/serial_openbsd.go new file mode 100644 index 0000000..f163529 --- /dev/null +++ b/serial_openbsd.go @@ -0,0 +1,62 @@ +// +// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +package serial // import "go.bug.st/serial.v1" + +import "golang.org/x/sys/unix" + +const devFolder = "/dev" +const regexFilter = "^(cu|tty)\\..*" + +// termios manipulation functions + +var baudrateMap = map[int]uint32{ + 0: unix.B9600, // Default to 9600 + 50: unix.B50, + 75: unix.B75, + 110: unix.B110, + 134: unix.B134, + 150: unix.B150, + 200: unix.B200, + 300: unix.B300, + 600: unix.B600, + 1200: unix.B1200, + 1800: unix.B1800, + 2400: unix.B2400, + 4800: unix.B4800, + 9600: unix.B9600, + 19200: unix.B19200, + 38400: unix.B38400, + 57600: unix.B57600, + 115200: unix.B115200, + 230400: unix.B230400, + //460800: unix.B460800, + //921600: unix.B921600, +} + +var databitsMap = map[int]uint32{ + 0: unix.CS8, // Default to 8 bits + 5: unix.CS5, + 6: unix.CS6, + 7: unix.CS7, + 8: unix.CS8, +} + +const tcCMSPAR uint32 = 0 // may be CMSPAR or PAREXT +const tcIUCLC uint32 = 0 + +const tcCCTS_OFLOW uint32 = 0x00010000 +const tcCRTS_IFLOW uint32 = 0x00020000 + +const tcCRTSCTS uint32 = tcCCTS_OFLOW + +const ioctlTcgetattr = unix.TIOCGETA +const ioctlTcsetattr = unix.TIOCSETA +const ioctlTcflsh = unix.TIOCFLUSH + +func toTermiosSpeedType(speed uint32) int32 { + return int32(speed) +} diff --git a/serial_unix.go b/serial_unix.go index 69bbceb..1cf690f 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -// +build linux darwin freebsd +// +build linux darwin freebsd openbsd package serial // import "go.bug.st/serial.v1" @@ -252,8 +252,8 @@ func setTermSettingsBaudrate(speed int, settings *unix.Termios) error { } // set new baudrate settings.Cflag |= baudrate - settings.Ispeed = baudrate - settings.Ospeed = baudrate + settings.Ispeed = toTermiosSpeedType(baudrate) + settings.Ospeed = toTermiosSpeedType(baudrate) return nil } diff --git a/syscall_openbsd.go b/syscall_openbsd.go new file mode 100644 index 0000000..670a257 --- /dev/null +++ b/syscall_openbsd.go @@ -0,0 +1,9 @@ +// +// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +package serial // import "go.bug.st/serial.v1" + +//sys ioctl(fd int, req uint64, data uintptr) (err error) diff --git a/unixutils/pipe.go b/unixutils/pipe.go index 941b6eb..93f3486 100644 --- a/unixutils/pipe.go +++ b/unixutils/pipe.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -// +build linux darwin freebsd +// +build linux darwin freebsd openbsd package unixutils // import "go.bug.st/serial.v1/unixutils" diff --git a/unixutils/select.go b/unixutils/select.go index 7ad60a8..0bd1d9a 100644 --- a/unixutils/select.go +++ b/unixutils/select.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -// +build linux darwin freebsd +// +build linux darwin freebsd openbsd package unixutils // "go.bug.st/serial.v1/unixutils" diff --git a/zsyscall_openbsd.go b/zsyscall_openbsd.go new file mode 100644 index 0000000..3d8cee4 --- /dev/null +++ b/zsyscall_openbsd.go @@ -0,0 +1,21 @@ +// +// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +// This file is machine generated by the command: +// mksyscall.pl serial_openbsd.go +// The generated stub is modified to make it compile under the "serial" package + +package serial // import "go.bug.st/serial.v1" + +import "syscall" + +func ioctl(fd int, req uint64, data uintptr) (err error) { + _, _, e1 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data)) + if e1 != 0 { + err = e1 + } + return +} From c4db4e3956c9d505b6cf388a4bdbfc0c35104f13 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 12 Mar 2017 22:47:07 +0100 Subject: [PATCH 057/181] Allow travis build failure on GOOS=openbsd GOARCH=arm It seems that the package: golang.org/x/sys/unix has a build failure on openbsd/arm: # golang.org/x/sys/unix ../../golang.org/x/sys/unix/flock.go:18: undefined: Flock_t ../../golang.org/x/sys/unix/sockcmsg_unix.go:36: undefined: Cmsghdr ../../golang.org/x/sys/unix/sockcmsg_unix.go:42: undefined: Cmsghdr it seems a failure on the upstream package, so for now let's allow the build failure. This commit removes also the build for dragonfly that is not officially supported. --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e962e65..5c29618 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,14 +15,13 @@ env: - TEST_OS=darwin TEST_ARCH=386 - TEST_OS=darwin TEST_ARCH=amd64 - TEST_OS=freebsd TEST_ARCH=amd64 - - TEST_OS=dragonfly TEST_ARCH=amd64 - TEST_OS=openbsd TEST_ARCH=amd64 - TEST_OS=openbsd TEST_ARCH=386 - TEST_OS=openbsd TEST_ARCH=arm matrix: allow_failures: - - env: TEST_OS=dragonfly TEST_ARCH=amd64 + - env: TEST_OS=openbsd TEST_ARCH=arm before_install: From 1ba11968613aa43d0235895d2a233d636fc1045a Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 22 Jun 2017 15:40:38 +0200 Subject: [PATCH 058/181] Fixed build of USB enumerator for go compiler >=1.8.3 (@facchinm) Fixes #30 --- enumerator/usb_darwin.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/enumerator/usb_darwin.go b/enumerator/usb_darwin.go index b02de12..4cb9f61 100644 --- a/enumerator/usb_darwin.go +++ b/enumerator/usb_darwin.go @@ -152,13 +152,13 @@ func (me *C.io_registry_entry_t) GetStringProperty(key string) (string, error) { } defer C.CFRelease(property) - if ptr := C.CFStringGetCStringPtr(property, 0); ptr != nil { + if ptr := C.CFStringGetCStringPtr((C.CFStringRef)(unsafe.Pointer(property)), 0); ptr != nil { return C.GoString(ptr), nil } // in certain circumstances CFStringGetCStringPtr may return NULL // and we must retrieve the string by copy buff := make([]C.char, 1024) - if C.CFStringGetCString(property, &buff[0], 1024, 0) != C.true { + if C.CFStringGetCString((C.CFStringRef)(property), &buff[0], 1024, 0) != C.true { return "", fmt.Errorf("Property '%s' can't be converted", key) } return C.GoString(&buff[0]), nil @@ -173,7 +173,7 @@ func (me *C.io_registry_entry_t) GetIntProperty(key string, intType C.CFNumberTy } defer C.CFRelease(property) var res int - if C.CFNumberGetValue(property, intType, unsafe.Pointer(&res)) != C.true { + if C.CFNumberGetValue((C.CFNumberRef)(property), intType, unsafe.Pointer(&res)) != C.true { return res, fmt.Errorf("Property '%s' can't be converted or has been truncated", key) } return res, nil From fd75bf38959015cfe7382b520c1da099133aa7bc Mon Sep 17 00:00:00 2001 From: Tobias Kaupat Date: Tue, 25 Jul 2017 13:07:20 +0200 Subject: [PATCH 059/181] Windows: Set DTS via setCommState https://github.com/bugst/go-serial/issues/35 --- serial_windows.go | 68 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/serial_windows.go b/serial_windows.go index 4a504ef..bc54789 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -266,15 +266,41 @@ func (port *windowsPort) SetMode(mode *Mode) error { } func (port *windowsPort) SetDTR(dtr bool) error { - var res bool + // Like for RTS there are problems with the escapeCommFunction + // observed behaviour was that DTR is set from false -> true + // when setting RTS from true -> false + // 1) Connect -> RTS = true (low) DTR = true (low) OKAY + // 2) SetDTR(false) -> RTS = true (low) DTR = false (heigh) OKAY + // 3) SetRTS(false) -> RTS = false (heigh) DTR = true (low) ERROR: DTR toggled + // + // In addition this way the CommState Flags are not updated + /* + var res bool + if dtr { + res = escapeCommFunction(port.handle, commFunctionSetDTR) + } else { + res = escapeCommFunction(port.handle, commFunctionClrDTR) + } + if !res { + return &PortError{} + } + return nil + */ + + // The following seems a more reliable way to do it + + params := &dcb{} + if err := getCommState(port.handle, params); err != nil { + return &PortError{causedBy: err} + } + params.Flags &= dcbDTRControlDisableMask if dtr { - res = escapeCommFunction(port.handle, commFunctionSetDTR) - } else { - res = escapeCommFunction(port.handle, commFunctionClrDTR) + params.Flags |= dcbDTRControlEnable } - if !res { - return &PortError{} + if err := setCommState(port.handle, params); err != nil { + return &PortError{causedBy: err} } + return nil } @@ -283,6 +309,8 @@ func (port *windowsPort) SetRTS(rts bool) error { // it doesn't send USB control message when the RTS bit is // changed, so the following code not always works with // USB-to-serial adapters. + // + // In addition this way the CommState Flags are not updated /* var res bool @@ -313,6 +341,34 @@ func (port *windowsPort) SetRTS(rts bool) error { return nil } +// GetRTS reads the RTS status from the control flags +// TODO: This is not yet implemented for other OS and thus not in the Port interface +func (port *windowsPort) GetRTS() (bool, error) { + params := dcb{} + if err := getCommState(port.handle, ¶ms); err != nil { + return false, &PortError{causedBy: err} + } + + if params.Flags&dcbRTSControlEnable != 0 { + return true, nil + } + return false, nil +} + +// GetDTR reads the DTR status from the control flags +// TODO: This is not yet implemented for other OS and thus not in the Port interface +func (port *windowsPort) GetDTR() (bool, error) { + params := dcb{} + if err := getCommState(port.handle, ¶ms); err != nil { + return false, &PortError{causedBy: err} + } + + if params.Flags&dcbDTRControlEnable != 0 { + return true, nil + } + return false, nil +} + func (port *windowsPort) GetModemStatusBits() (*ModemStatusBits, error) { var bits uint32 if !getCommModemStatus(port.handle, &bits) { From 86b770d21880cbed3c88d5e16127851ec7e3aa8f Mon Sep 17 00:00:00 2001 From: Veniamin Albaev Date: Fri, 30 Jun 2017 17:00:37 +0300 Subject: [PATCH 060/181] Enumerator: Fixed go:generate comment It pointing to mksyscal_windows.go in local folder which already removed --- enumerator/enumerator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enumerator/enumerator.go b/enumerator/enumerator.go index 918db57..3dbc197 100644 --- a/enumerator/enumerator.go +++ b/enumerator/enumerator.go @@ -6,7 +6,7 @@ package enumerator // import "go.bug.st/serial.v1/enumerator" -//go:generate go run ../extras/mksyscall_windows.go -output syscall_windows.go usb_windows.go +//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output syscall_windows.go usb_windows.go // PortDetails contains detailed information about USB serial port. // Use GetDetailedPortsList function to retrieve it. From 04b4ab1e45a5edfcb8f4fd01daa571a2a41405d3 Mon Sep 17 00:00:00 2001 From: Tobias Kaupat Date: Fri, 28 Jul 2017 09:49:49 +0200 Subject: [PATCH 061/181] Remove GetDTR() and GetRTS() --- serial_windows.go | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/serial_windows.go b/serial_windows.go index bc54789..90712d5 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -341,34 +341,6 @@ func (port *windowsPort) SetRTS(rts bool) error { return nil } -// GetRTS reads the RTS status from the control flags -// TODO: This is not yet implemented for other OS and thus not in the Port interface -func (port *windowsPort) GetRTS() (bool, error) { - params := dcb{} - if err := getCommState(port.handle, ¶ms); err != nil { - return false, &PortError{causedBy: err} - } - - if params.Flags&dcbRTSControlEnable != 0 { - return true, nil - } - return false, nil -} - -// GetDTR reads the DTR status from the control flags -// TODO: This is not yet implemented for other OS and thus not in the Port interface -func (port *windowsPort) GetDTR() (bool, error) { - params := dcb{} - if err := getCommState(port.handle, ¶ms); err != nil { - return false, &PortError{causedBy: err} - } - - if params.Flags&dcbDTRControlEnable != 0 { - return true, nil - } - return false, nil -} - func (port *windowsPort) GetModemStatusBits() (*ModemStatusBits, error) { var bits uint32 if !getCommModemStatus(port.handle, &bits) { From c702e0a9aa3305b96f7c6c9c2846ff2320fd354a Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 19 Aug 2018 02:07:48 +0200 Subject: [PATCH 062/181] Added OSX environment to travis builds --- .travis.yml | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/.travis.yml b/.travis.yml index 5c29618..e825252 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,18 @@ language: go +os: + - linux + - osx + go: - 1.7.x - 1.8.x + - 1.9.x + - 1.10.x + # from go 1.8.7, 1.9.4, 1.10rc2 there is a restriction on CGO see https://github.com/bugst/go-serial/issues/50 + # this restriction has some consequences on darwin build + - 1.8.6 + - 1.9.3 go_import_path: go.bug.st/serial.v1 @@ -20,6 +30,37 @@ env: - TEST_OS=openbsd TEST_ARCH=arm matrix: + exclude: + - os: linux # no point in testing go1.8.6 on Linux + go: 1.8.6 + - os: linux # no point in testing go1.9.3 on Linux + go: 1.9.3 + - os: osx # go1.8.x after 1.8.6 is not supported on OSX + go: 1.8.x + - os: osx # go1.9.x after 1.9.3 is not supported on OSX + go: 1.9.x + - os: linux + env: TEST_OS=darwin TEST_ARCH=386 + - os: linux + env: TEST_OS=darwin TEST_ARCH=amd64 + - os: osx + env: TEST_OS=linux TEST_ARCH=386 + - os: osx + env: TEST_OS=linux TEST_ARCH=amd64 + - os: osx + env: TEST_OS=linux TEST_ARCH=arm + - os: osx + env: TEST_OS=windows TEST_ARCH=386 + - os: osx + env: TEST_OS=windows TEST_ARCH=amd64 + - os: osx + env: TEST_OS=freebsd TEST_ARCH=amd64 + - os: osx + env: TEST_OS=openbsd TEST_ARCH=amd64 + - os: osx + env: TEST_OS=openbsd TEST_ARCH=386 + - os: osx + env: TEST_OS=openbsd TEST_ARCH=arm allow_failures: - env: TEST_OS=openbsd TEST_ARCH=arm From 3ee4586c58f95ffba7193e1f76a5d9f031295c66 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 19 Aug 2018 17:46:38 +0200 Subject: [PATCH 063/181] Splitted usb_darwing.go in two different build units based on go version --- .../{usb_darwin.go => usb_go1.10_darwin.go} | 2 + enumerator/usb_go1.9_darwin.go | 231 ++++++++++++++++++ 2 files changed, 233 insertions(+) rename enumerator/{usb_darwin.go => usb_go1.10_darwin.go} (99%) create mode 100644 enumerator/usb_go1.9_darwin.go diff --git a/enumerator/usb_darwin.go b/enumerator/usb_go1.10_darwin.go similarity index 99% rename from enumerator/usb_darwin.go rename to enumerator/usb_go1.10_darwin.go index 4cb9f61..23416fe 100644 --- a/enumerator/usb_darwin.go +++ b/enumerator/usb_go1.10_darwin.go @@ -4,6 +4,8 @@ // license that can be found in the LICENSE file. // +// +build go1.10 + package enumerator // import "go.bug.st/serial.v1/enumerator" // #cgo LDFLAGS: -framework CoreFoundation -framework IOKit -fconstant-cfstrings diff --git a/enumerator/usb_go1.9_darwin.go b/enumerator/usb_go1.9_darwin.go new file mode 100644 index 0000000..f8ed324 --- /dev/null +++ b/enumerator/usb_go1.9_darwin.go @@ -0,0 +1,231 @@ +// +// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +// +build !go1.10 + +// This file is here to keep compatibility with the older versions of go +// and is no more maintained or bugfixed, please update your go version +// to at least 1.10 to get the latest updates. + +// Supported versions on MacOSX +// - any go version earlier than 1.8 +// - go1.8.x series from 1.8.0 to 1.8.6 +// - go1.9.x series from 1.9.0 to 1.9.3 + +// Versions NOT supported on MacOSX +// - go1.8.x series starting from 1.8.7 +// - go1.9.x series starting from 1.9.4 + +package enumerator // import "go.bug.st/serial.v1/enumerator" + +// #cgo LDFLAGS: -framework CoreFoundation -framework IOKit -fconstant-cfstrings +// #include +// #include +// #include +import "C" +import ( + "errors" + "fmt" + "unsafe" +) + +func nativeGetDetailedPortsList() ([]*PortDetails, error) { + var ports []*PortDetails + + services, err := getAllServices("IOSerialBSDClient") + if err != nil { + return nil, &PortEnumerationError{causedBy: err} + } + for _, service := range services { + defer service.Release() + + port, err := extractPortInfo(C.io_registry_entry_t(service)) + if err != nil { + return nil, &PortEnumerationError{causedBy: err} + } + ports = append(ports, port) + } + return ports, nil +} + +func extractPortInfo(service C.io_registry_entry_t) (*PortDetails, error) { + name, err := service.GetStringProperty("IOCalloutDevice") + if err != nil { + return nil, fmt.Errorf("Error extracting port info from device: %s", err.Error()) + } + port := &PortDetails{} + port.Name = name + port.IsUSB = false + + usbDevice := service + for usbDevice.GetClass() != "IOUSBDevice" { + if usbDevice, err = usbDevice.GetParent("IOService"); err != nil { + break + } + } + if err == nil { + // It's an IOUSBDevice + vid, _ := usbDevice.GetIntProperty("idVendor", C.kCFNumberSInt16Type) + pid, _ := usbDevice.GetIntProperty("idProduct", C.kCFNumberSInt16Type) + serialNumber, _ := usbDevice.GetStringProperty("USB Serial Number") + //product, _ := usbDevice.GetStringProperty("USB Product Name") + //manufacturer, _ := usbDevice.GetStringProperty("USB Vendor Name") + //fmt.Println(product + " - " + manufacturer) + + port.IsUSB = true + port.VID = fmt.Sprintf("%04X", vid) + port.PID = fmt.Sprintf("%04X", pid) + port.SerialNumber = serialNumber + } + return port, nil +} + +func getAllServices(serviceType string) ([]C.io_object_t, error) { + i, err := getMatchingServices(serviceMatching(serviceType)) + if err != nil { + return nil, err + } + defer i.Release() + + var services []C.io_object_t + tries := 0 + for tries < 5 { + // Extract all elements from iterator + if service, ok := i.Next(); ok { + services = append(services, service) + continue + } + // If iterator is still valid return the result + if i.IsValid() { + return services, nil + } + // Otherwise empty the result and retry + for _, s := range services { + s.Release() + } + services = []C.io_object_t{} + i.Reset() + tries++ + } + // Give up if the iteration continues to fail... + return nil, fmt.Errorf("IOServiceGetMatchingServices failed, data changed while iterating") +} + +// serviceMatching create a matching dictionary that specifies an IOService class match. +func serviceMatching(serviceType string) C.CFMutableDictionaryRef { + t := C.CString(serviceType) + defer C.free(unsafe.Pointer(t)) + return C.IOServiceMatching(t) +} + +// getMatchingServices look up registered IOService objects that match a matching dictionary. +func getMatchingServices(matcher C.CFMutableDictionaryRef) (C.io_iterator_t, error) { + var i C.io_iterator_t + err := C.IOServiceGetMatchingServices(C.kIOMasterPortDefault, matcher, &i) + if err != C.KERN_SUCCESS { + return 0, fmt.Errorf("IOServiceGetMatchingServices failed (code %d)", err) + } + return i, nil +} + +// CFStringRef + +func cfStringCreateWithString(s string) C.CFStringRef { + c := C.CString(s) + defer C.free(unsafe.Pointer(c)) + return C.CFStringCreateWithCString( + C.kCFAllocatorDefault, c, C.kCFStringEncodingMacRoman) +} + +// io_registry_entry_t + +func (me *C.io_registry_entry_t) GetParent(plane string) (C.io_registry_entry_t, error) { + cPlane := C.CString(plane) + defer C.free(unsafe.Pointer(cPlane)) + var parent C.io_registry_entry_t + err := C.IORegistryEntryGetParentEntry(*me, cPlane, &parent) + if err != 0 { + return 0, errors.New("No parent device available") + } + return parent, nil +} + +func (me *C.io_registry_entry_t) GetClass() string { + obj := (*C.io_object_t)(me) + return obj.GetClass() +} + +func (me *C.io_registry_entry_t) GetStringProperty(key string) (string, error) { + k := cfStringCreateWithString(key) + defer C.CFRelease(C.CFTypeRef(k)) + property := C.IORegistryEntryCreateCFProperty(*me, k, C.kCFAllocatorDefault, 0) + if property == nil { + return "", errors.New("Property not found: " + key) + } + defer C.CFRelease(property) + + if ptr := C.CFStringGetCStringPtr((C.CFStringRef)(unsafe.Pointer(property)), 0); ptr != nil { + return C.GoString(ptr), nil + } + // in certain circumstances CFStringGetCStringPtr may return NULL + // and we must retrieve the string by copy + buff := make([]C.char, 1024) + if C.CFStringGetCString((C.CFStringRef)(property), &buff[0], 1024, 0) != C.true { + return "", fmt.Errorf("Property '%s' can't be converted", key) + } + return C.GoString(&buff[0]), nil +} + +func (me *C.io_registry_entry_t) GetIntProperty(key string, intType C.CFNumberType) (int, error) { + k := cfStringCreateWithString(key) + defer C.CFRelease(C.CFTypeRef(k)) + property := C.IORegistryEntryCreateCFProperty(*me, k, C.kCFAllocatorDefault, 0) + if property == nil { + return 0, errors.New("Property not found: " + key) + } + defer C.CFRelease(property) + var res int + if C.CFNumberGetValue((C.CFNumberRef)(property), intType, unsafe.Pointer(&res)) != C.true { + return res, fmt.Errorf("Property '%s' can't be converted or has been truncated", key) + } + return res, nil +} + +// io_iterator_t + +// IsValid checks if an iterator is still valid. +// Some iterators will be made invalid if changes are made to the +// structure they are iterating over. This function checks the iterator +// is still valid and should be called when Next returns zero. +// An invalid iterator can be Reset and the iteration restarted. +func (me *C.io_iterator_t) IsValid() bool { + return C.IOIteratorIsValid(*me) == C.true +} + +func (me *C.io_iterator_t) Reset() { + C.IOIteratorReset(*me) +} + +func (me *C.io_iterator_t) Next() (C.io_object_t, bool) { + res := C.IOIteratorNext(*me) + return res, res != 0 +} + +func (me *C.io_iterator_t) Release() { + C.IOObjectRelease(C.io_object_t(*me)) +} + +// io_object_t + +func (me *C.io_object_t) Release() { + C.IOObjectRelease(*me) +} + +func (me *C.io_object_t) GetClass() string { + class := make([]C.char, 1024) + C.IOObjectGetClass(*me, &class[0]) + return C.GoString(&class[0]) +} From 3c7a545ca253ffeb9b128e6dfd5f0714f8a2f6ec Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 19 Aug 2018 18:09:25 +0200 Subject: [PATCH 064/181] Removed -fconstant-cfstrings CGO build flag --- enumerator/usb_go1.10_darwin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enumerator/usb_go1.10_darwin.go b/enumerator/usb_go1.10_darwin.go index 23416fe..4a2b7cb 100644 --- a/enumerator/usb_go1.10_darwin.go +++ b/enumerator/usb_go1.10_darwin.go @@ -8,7 +8,7 @@ package enumerator // import "go.bug.st/serial.v1/enumerator" -// #cgo LDFLAGS: -framework CoreFoundation -framework IOKit -fconstant-cfstrings +// #cgo LDFLAGS: -framework CoreFoundation -framework IOKit // #include // #include // #include From ef634e5745e7081faaf3d4d83e7005f194216ed0 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 19 Aug 2018 18:11:11 +0200 Subject: [PATCH 065/181] Removed duplicated methods on (nowadays) equivalent type C.io_*_t This is due to improvements on the cgo builder related to darwin OS. --- enumerator/usb_go1.10_darwin.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/enumerator/usb_go1.10_darwin.go b/enumerator/usb_go1.10_darwin.go index 4a2b7cb..27249f8 100644 --- a/enumerator/usb_go1.10_darwin.go +++ b/enumerator/usb_go1.10_darwin.go @@ -140,11 +140,6 @@ func (me *C.io_registry_entry_t) GetParent(plane string) (C.io_registry_entry_t, return parent, nil } -func (me *C.io_registry_entry_t) GetClass() string { - obj := (*C.io_object_t)(me) - return obj.GetClass() -} - func (me *C.io_registry_entry_t) GetStringProperty(key string) (string, error) { k := cfStringCreateWithString(key) defer C.CFRelease(C.CFTypeRef(k)) @@ -201,10 +196,6 @@ func (me *C.io_iterator_t) Next() (C.io_object_t, bool) { return res, res != 0 } -func (me *C.io_iterator_t) Release() { - C.IOObjectRelease(C.io_object_t(*me)) -} - // io_object_t func (me *C.io_object_t) Release() { From 75aa16d69f91e2c3aba76fd08b66387abd88053a Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 19 Aug 2018 18:13:24 +0200 Subject: [PATCH 066/181] CFRef are no more internally represented as pointer but as integers This is due to improvements in CGO compiler for darwin OS --- enumerator/usb_go1.10_darwin.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/enumerator/usb_go1.10_darwin.go b/enumerator/usb_go1.10_darwin.go index 27249f8..4640918 100644 --- a/enumerator/usb_go1.10_darwin.go +++ b/enumerator/usb_go1.10_darwin.go @@ -144,12 +144,12 @@ func (me *C.io_registry_entry_t) GetStringProperty(key string) (string, error) { k := cfStringCreateWithString(key) defer C.CFRelease(C.CFTypeRef(k)) property := C.IORegistryEntryCreateCFProperty(*me, k, C.kCFAllocatorDefault, 0) - if property == nil { + if property == 0 { return "", errors.New("Property not found: " + key) } defer C.CFRelease(property) - if ptr := C.CFStringGetCStringPtr((C.CFStringRef)(unsafe.Pointer(property)), 0); ptr != nil { + if ptr := C.CFStringGetCStringPtr(C.CFStringRef(property), 0); ptr != nil { return C.GoString(ptr), nil } // in certain circumstances CFStringGetCStringPtr may return NULL @@ -165,7 +165,7 @@ func (me *C.io_registry_entry_t) GetIntProperty(key string, intType C.CFNumberTy k := cfStringCreateWithString(key) defer C.CFRelease(C.CFTypeRef(k)) property := C.IORegistryEntryCreateCFProperty(*me, k, C.kCFAllocatorDefault, 0) - if property == nil { + if property == 0 { return 0, errors.New("Property not found: " + key) } defer C.CFRelease(property) From ee147ffce49faca927bce509eaaed3945b08a43b Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 19 Aug 2018 18:28:40 +0200 Subject: [PATCH 067/181] Added Release method to CFStringRef --- enumerator/usb_go1.10_darwin.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/enumerator/usb_go1.10_darwin.go b/enumerator/usb_go1.10_darwin.go index 4640918..57ba106 100644 --- a/enumerator/usb_go1.10_darwin.go +++ b/enumerator/usb_go1.10_darwin.go @@ -127,6 +127,10 @@ func cfStringCreateWithString(s string) C.CFStringRef { C.kCFAllocatorDefault, c, C.kCFStringEncodingMacRoman) } +func (ref C.CFStringRef) Release() { + C.CFRelease(C.CFTypeRef(ref)) +} + // io_registry_entry_t func (me *C.io_registry_entry_t) GetParent(plane string) (C.io_registry_entry_t, error) { @@ -142,7 +146,7 @@ func (me *C.io_registry_entry_t) GetParent(plane string) (C.io_registry_entry_t, func (me *C.io_registry_entry_t) GetStringProperty(key string) (string, error) { k := cfStringCreateWithString(key) - defer C.CFRelease(C.CFTypeRef(k)) + defer k.Release() property := C.IORegistryEntryCreateCFProperty(*me, k, C.kCFAllocatorDefault, 0) if property == 0 { return "", errors.New("Property not found: " + key) @@ -163,7 +167,7 @@ func (me *C.io_registry_entry_t) GetStringProperty(key string) (string, error) { func (me *C.io_registry_entry_t) GetIntProperty(key string, intType C.CFNumberType) (int, error) { k := cfStringCreateWithString(key) - defer C.CFRelease(C.CFTypeRef(k)) + defer k.Release() property := C.IORegistryEntryCreateCFProperty(*me, k, C.kCFAllocatorDefault, 0) if property == 0 { return 0, errors.New("Property not found: " + key) From 71fe05aedd261c89c5adbd6f7432130238cf9de1 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 19 Aug 2018 18:40:47 +0200 Subject: [PATCH 068/181] Added Release method to CFTypeRef --- enumerator/usb_go1.10_darwin.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/enumerator/usb_go1.10_darwin.go b/enumerator/usb_go1.10_darwin.go index 57ba106..e96901e 100644 --- a/enumerator/usb_go1.10_darwin.go +++ b/enumerator/usb_go1.10_darwin.go @@ -131,6 +131,12 @@ func (ref C.CFStringRef) Release() { C.CFRelease(C.CFTypeRef(ref)) } +// CFTypeRef + +func (ref C.CFTypeRef) Release() { + C.CFRelease(ref) +} + // io_registry_entry_t func (me *C.io_registry_entry_t) GetParent(plane string) (C.io_registry_entry_t, error) { @@ -151,7 +157,7 @@ func (me *C.io_registry_entry_t) GetStringProperty(key string) (string, error) { if property == 0 { return "", errors.New("Property not found: " + key) } - defer C.CFRelease(property) + defer property.Release() if ptr := C.CFStringGetCStringPtr(C.CFStringRef(property), 0); ptr != nil { return C.GoString(ptr), nil @@ -172,7 +178,7 @@ func (me *C.io_registry_entry_t) GetIntProperty(key string, intType C.CFNumberTy if property == 0 { return 0, errors.New("Property not found: " + key) } - defer C.CFRelease(property) + defer property.Release() var res int if C.CFNumberGetValue((C.CFNumberRef)(property), intType, unsafe.Pointer(&res)) != C.true { return res, fmt.Errorf("Property '%s' can't be converted or has been truncated", key) From cdac3b30e16732aa230de7b0f43996f0a3eaa428 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 19 Aug 2018 18:41:00 +0200 Subject: [PATCH 069/181] Removed extra parenthesis --- enumerator/usb_go1.10_darwin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enumerator/usb_go1.10_darwin.go b/enumerator/usb_go1.10_darwin.go index e96901e..2ac257c 100644 --- a/enumerator/usb_go1.10_darwin.go +++ b/enumerator/usb_go1.10_darwin.go @@ -165,7 +165,7 @@ func (me *C.io_registry_entry_t) GetStringProperty(key string) (string, error) { // in certain circumstances CFStringGetCStringPtr may return NULL // and we must retrieve the string by copy buff := make([]C.char, 1024) - if C.CFStringGetCString((C.CFStringRef)(property), &buff[0], 1024, 0) != C.true { + if C.CFStringGetCString(C.CFStringRef(property), &buff[0], 1024, 0) != C.true { return "", fmt.Errorf("Property '%s' can't be converted", key) } return C.GoString(&buff[0]), nil From 6085517433f155a5dda1445f1fb06f9550a90527 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 19 Aug 2018 18:41:24 +0200 Subject: [PATCH 070/181] Made error variable explicit in search loop --- enumerator/usb_go1.10_darwin.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/enumerator/usb_go1.10_darwin.go b/enumerator/usb_go1.10_darwin.go index 2ac257c..cf19fe3 100644 --- a/enumerator/usb_go1.10_darwin.go +++ b/enumerator/usb_go1.10_darwin.go @@ -48,12 +48,13 @@ func extractPortInfo(service C.io_registry_entry_t) (*PortDetails, error) { port.IsUSB = false usbDevice := service + var searchErr error for usbDevice.GetClass() != "IOUSBDevice" { - if usbDevice, err = usbDevice.GetParent("IOService"); err != nil { + if usbDevice, searchErr = usbDevice.GetParent("IOService"); searchErr != nil { break } } - if err == nil { + if searchErr == nil { // It's an IOUSBDevice vid, _ := usbDevice.GetIntProperty("idVendor", C.kCFNumberSInt16Type) pid, _ := usbDevice.GetIntProperty("idProduct", C.kCFNumberSInt16Type) From 338c399730b7c5ea6a9581e539ba8c031ed5ba2e Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 19 Aug 2018 18:51:07 +0200 Subject: [PATCH 071/181] Factored out C.IORegistryEntryCreateCFProperty method --- enumerator/usb_go1.10_darwin.go | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/enumerator/usb_go1.10_darwin.go b/enumerator/usb_go1.10_darwin.go index cf19fe3..357904b 100644 --- a/enumerator/usb_go1.10_darwin.go +++ b/enumerator/usb_go1.10_darwin.go @@ -151,12 +151,20 @@ func (me *C.io_registry_entry_t) GetParent(plane string) (C.io_registry_entry_t, return parent, nil } -func (me *C.io_registry_entry_t) GetStringProperty(key string) (string, error) { +func (me *C.io_registry_entry_t) CreateCFProperty(key string) (C.CFTypeRef, error) { k := cfStringCreateWithString(key) defer k.Release() property := C.IORegistryEntryCreateCFProperty(*me, k, C.kCFAllocatorDefault, 0) if property == 0 { - return "", errors.New("Property not found: " + key) + return 0, errors.New("Property not found: " + key) + } + return property, nil +} + +func (me *C.io_registry_entry_t) GetStringProperty(key string) (string, error) { + property, err := me.CreateCFProperty(key) + if err != nil { + return "", err } defer property.Release() @@ -173,11 +181,9 @@ func (me *C.io_registry_entry_t) GetStringProperty(key string) (string, error) { } func (me *C.io_registry_entry_t) GetIntProperty(key string, intType C.CFNumberType) (int, error) { - k := cfStringCreateWithString(key) - defer k.Release() - property := C.IORegistryEntryCreateCFProperty(*me, k, C.kCFAllocatorDefault, 0) - if property == 0 { - return 0, errors.New("Property not found: " + key) + property, err := me.CreateCFProperty(key) + if err != nil { + return 0, err } defer property.Release() var res int From de1714d049b8e49f2f86fbee82bb0ad1a58e9efb Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 19 Aug 2018 18:52:20 +0200 Subject: [PATCH 072/181] Added portlist cmd-line utility --- portlist/portlist.go | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 portlist/portlist.go diff --git a/portlist/portlist.go b/portlist/portlist.go new file mode 100644 index 0000000..5d4d127 --- /dev/null +++ b/portlist/portlist.go @@ -0,0 +1,37 @@ +// +// Copyright 2014-2018 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +// portlist is a tool to list all the available serial ports. +// Just run it and it will produce an output like: +// +// $ go run portlist.go +// Port: /dev/cu.Bluetooth-Incoming-Port +// Port: /dev/cu.usbmodemFD121 +// USB ID 2341:8053 +// USB serial FB7B6060504B5952302E314AFF08191A +// +package main + +import "fmt" +import "log" +import "go.bug.st/serial.v1/enumerator" + +func main() { + ports, err := enumerator.GetDetailedPortsList() + if err != nil { + log.Fatal(err) + } + if len(ports) == 0 { + return + } + for _, port := range ports { + fmt.Printf("Port: %s\n", port.Name) + if port.IsUSB { + fmt.Printf(" USB ID %s:%s\n", port.VID, port.PID) + fmt.Printf(" USB serial %s\n", port.SerialNumber) + } + } +} From e42339511cf1ce55cc7f71b6acca84e8786fd179 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 19 Aug 2018 18:57:32 +0200 Subject: [PATCH 073/181] Fixed wrong build tags... oops --- enumerator/{usb_go1.10_darwin.go => usb_darwin.go} | 2 +- enumerator/{usb_go1.9_darwin.go => usb_darwin_go1.9.go} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename enumerator/{usb_go1.10_darwin.go => usb_darwin.go} (99%) rename enumerator/{usb_go1.9_darwin.go => usb_darwin_go1.9.go} (99%) diff --git a/enumerator/usb_go1.10_darwin.go b/enumerator/usb_darwin.go similarity index 99% rename from enumerator/usb_go1.10_darwin.go rename to enumerator/usb_darwin.go index 357904b..c72a19a 100644 --- a/enumerator/usb_go1.10_darwin.go +++ b/enumerator/usb_darwin.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -// +build go1.10 +// +build go1.10,darwin package enumerator // import "go.bug.st/serial.v1/enumerator" diff --git a/enumerator/usb_go1.9_darwin.go b/enumerator/usb_darwin_go1.9.go similarity index 99% rename from enumerator/usb_go1.9_darwin.go rename to enumerator/usb_darwin_go1.9.go index f8ed324..24cd32f 100644 --- a/enumerator/usb_go1.9_darwin.go +++ b/enumerator/usb_darwin_go1.9.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -// +build !go1.10 +// +build !go1.10,darwin // This file is here to keep compatibility with the older versions of go // and is no more maintained or bugfixed, please update your go version From 1c2446b92c285a8f7e4fe47b2e55f41960ffb148 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 19 Aug 2018 19:13:36 +0200 Subject: [PATCH 074/181] Implementing compatibility with go latest This should allow darwin to be compatible with all the latest go1.7.x go1.8.x go1.9.x --- .travis.yml | 12 ------------ enumerator/usb_darwin_go1.9.go | 11 +---------- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/.travis.yml b/.travis.yml index e825252..8f4b14e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,10 +9,6 @@ go: - 1.8.x - 1.9.x - 1.10.x - # from go 1.8.7, 1.9.4, 1.10rc2 there is a restriction on CGO see https://github.com/bugst/go-serial/issues/50 - # this restriction has some consequences on darwin build - - 1.8.6 - - 1.9.3 go_import_path: go.bug.st/serial.v1 @@ -31,14 +27,6 @@ env: matrix: exclude: - - os: linux # no point in testing go1.8.6 on Linux - go: 1.8.6 - - os: linux # no point in testing go1.9.3 on Linux - go: 1.9.3 - - os: osx # go1.8.x after 1.8.6 is not supported on OSX - go: 1.8.x - - os: osx # go1.9.x after 1.9.3 is not supported on OSX - go: 1.9.x - os: linux env: TEST_OS=darwin TEST_ARCH=386 - os: linux diff --git a/enumerator/usb_darwin_go1.9.go b/enumerator/usb_darwin_go1.9.go index 24cd32f..442278d 100644 --- a/enumerator/usb_darwin_go1.9.go +++ b/enumerator/usb_darwin_go1.9.go @@ -10,18 +10,9 @@ // and is no more maintained or bugfixed, please update your go version // to at least 1.10 to get the latest updates. -// Supported versions on MacOSX -// - any go version earlier than 1.8 -// - go1.8.x series from 1.8.0 to 1.8.6 -// - go1.9.x series from 1.9.0 to 1.9.3 - -// Versions NOT supported on MacOSX -// - go1.8.x series starting from 1.8.7 -// - go1.9.x series starting from 1.9.4 - package enumerator // import "go.bug.st/serial.v1/enumerator" -// #cgo LDFLAGS: -framework CoreFoundation -framework IOKit -fconstant-cfstrings +// #cgo LDFLAGS: -framework CoreFoundation -framework IOKit // #include // #include // #include From d3b8f81e835f2fd75a5aa69d07567fbe9b773ade Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 27 Aug 2018 14:10:46 +0200 Subject: [PATCH 075/181] Added go1.11 to travis build --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 8f4b14e..6cbc813 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ go: - 1.8.x - 1.9.x - 1.10.x + - 1.11.x go_import_path: go.bug.st/serial.v1 From 219de06186b5c1df0f330d614c853aff36668c63 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 27 Aug 2018 14:18:10 +0200 Subject: [PATCH 076/181] Fixed darwin build for go1.10+ --- enumerator/usb_darwin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enumerator/usb_darwin.go b/enumerator/usb_darwin.go index c72a19a..4fcd4dc 100644 --- a/enumerator/usb_darwin.go +++ b/enumerator/usb_darwin.go @@ -112,7 +112,7 @@ func serviceMatching(serviceType string) C.CFMutableDictionaryRef { // getMatchingServices look up registered IOService objects that match a matching dictionary. func getMatchingServices(matcher C.CFMutableDictionaryRef) (C.io_iterator_t, error) { var i C.io_iterator_t - err := C.IOServiceGetMatchingServices(C.kIOMasterPortDefault, matcher, &i) + err := C.IOServiceGetMatchingServices(C.kIOMasterPortDefault, C.CFDictionaryRef(matcher), &i) if err != C.KERN_SUCCESS { return 0, fmt.Errorf("IOServiceGetMatchingServices failed (code %d)", err) } From 24a6610f0541dca0b50686e8ceb37a7a9f06e6f3 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 2 Dec 2019 19:27:10 +0100 Subject: [PATCH 077/181] Updated license years Updated to 2020 even if we are less than one month ahead. --- LICENSE | 2 +- doc.go | 2 +- enumerator/doc.go | 2 +- enumerator/enumerator.go | 2 +- enumerator/example_getdetailedportlist_test.go | 2 +- enumerator/usb_darwin.go | 2 +- enumerator/usb_darwin_go1.9.go | 2 +- enumerator/usb_freebsd.go | 2 +- enumerator/usb_linux.go | 2 +- enumerator/usb_ole_windows.go | 2 +- enumerator/usb_windows.go | 2 +- enumerator/usb_windows_test.go | 2 +- example_getportlist_test.go | 2 +- example_modem_bits_test.go | 2 +- example_serialport_test.go | 2 +- example_test.go | 2 +- portlist/portlist.go | 2 +- serial.go | 2 +- serial_darwin.go | 2 +- serial_darwin_386.go | 2 +- serial_darwin_amd64.go | 2 +- serial_freebsd.go | 2 +- serial_linux.go | 2 +- serial_openbsd.go | 2 +- serial_unix.go | 2 +- serial_windows.go | 2 +- syscall_darwin.go | 2 +- syscall_freebsd.go | 2 +- syscall_linux.go | 2 +- syscall_openbsd.go | 2 +- syscall_windows.go | 2 +- unixutils/pipe.go | 2 +- unixutils/select.go | 2 +- zsyscall_darwin.go | 2 +- zsyscall_freebsd.go | 2 +- zsyscall_linux.go | 2 +- zsyscall_openbsd.go | 2 +- 37 files changed, 37 insertions(+), 37 deletions(-) diff --git a/LICENSE b/LICENSE index 8596fab..1b8b73b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ -Copyright (c) 2014-2016, Cristian Maglie. +Copyright (c) 2014-2020, Cristian Maglie. All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/doc.go b/doc.go index b6a70e9..71993f8 100644 --- a/doc.go +++ b/doc.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/doc.go b/enumerator/doc.go index d214715..7ffa75d 100644 --- a/enumerator/doc.go +++ b/enumerator/doc.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/enumerator.go b/enumerator/enumerator.go index 3dbc197..ba33f3d 100644 --- a/enumerator/enumerator.go +++ b/enumerator/enumerator.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/example_getdetailedportlist_test.go b/enumerator/example_getdetailedportlist_test.go index 18c7738..fb6c9a1 100644 --- a/enumerator/example_getdetailedportlist_test.go +++ b/enumerator/example_getdetailedportlist_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_darwin.go b/enumerator/usb_darwin.go index 4fcd4dc..4709137 100644 --- a/enumerator/usb_darwin.go +++ b/enumerator/usb_darwin.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_darwin_go1.9.go b/enumerator/usb_darwin_go1.9.go index 442278d..26f842e 100644 --- a/enumerator/usb_darwin_go1.9.go +++ b/enumerator/usb_darwin_go1.9.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_freebsd.go b/enumerator/usb_freebsd.go index d60d13f..460f000 100644 --- a/enumerator/usb_freebsd.go +++ b/enumerator/usb_freebsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_linux.go b/enumerator/usb_linux.go index d2aca90..b67ca95 100644 --- a/enumerator/usb_linux.go +++ b/enumerator/usb_linux.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_ole_windows.go b/enumerator/usb_ole_windows.go index bbc0435..9df6cdf 100644 --- a/enumerator/usb_ole_windows.go +++ b/enumerator/usb_ole_windows.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Lars Knudsen, Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Lars Knudsen, Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_windows.go b/enumerator/usb_windows.go index 12ded68..e9fd0d8 100644 --- a/enumerator/usb_windows.go +++ b/enumerator/usb_windows.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_windows_test.go b/enumerator/usb_windows_test.go index 853c1a2..c227270 100644 --- a/enumerator/usb_windows_test.go +++ b/enumerator/usb_windows_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/example_getportlist_test.go b/example_getportlist_test.go index c8cb35b..4a9a13c 100644 --- a/example_getportlist_test.go +++ b/example_getportlist_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/example_modem_bits_test.go b/example_modem_bits_test.go index 9ae3389..9f1bb27 100644 --- a/example_modem_bits_test.go +++ b/example_modem_bits_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/example_serialport_test.go b/example_serialport_test.go index 6f09659..5a20cd7 100644 --- a/example_serialport_test.go +++ b/example_serialport_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/example_test.go b/example_test.go index 4f56978..2648fa0 100644 --- a/example_test.go +++ b/example_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/portlist/portlist.go b/portlist/portlist.go index 5d4d127..66b0494 100644 --- a/portlist/portlist.go +++ b/portlist/portlist.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2018 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial.go b/serial.go index 46eb521..ea3f873 100644 --- a/serial.go +++ b/serial.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_darwin.go b/serial_darwin.go index cfbde8f..aa70675 100644 --- a/serial_darwin.go +++ b/serial_darwin.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_darwin_386.go b/serial_darwin_386.go index 9b9e7fd..6522c1f 100644 --- a/serial_darwin_386.go +++ b/serial_darwin_386.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_darwin_amd64.go b/serial_darwin_amd64.go index b0b155d..4560692 100644 --- a/serial_darwin_amd64.go +++ b/serial_darwin_amd64.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_freebsd.go b/serial_freebsd.go index 4083cd6..fbd2893 100644 --- a/serial_freebsd.go +++ b/serial_freebsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_linux.go b/serial_linux.go index e49a847..b0e2f3c 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_openbsd.go b/serial_openbsd.go index f163529..e18e41c 100644 --- a/serial_openbsd.go +++ b/serial_openbsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_unix.go b/serial_unix.go index 1cf690f..8cc530e 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_windows.go b/serial_windows.go index 90712d5..0ae145b 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/syscall_darwin.go b/syscall_darwin.go index 670a257..be44e29 100644 --- a/syscall_darwin.go +++ b/syscall_darwin.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/syscall_freebsd.go b/syscall_freebsd.go index 670a257..be44e29 100644 --- a/syscall_freebsd.go +++ b/syscall_freebsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/syscall_linux.go b/syscall_linux.go index 670a257..be44e29 100644 --- a/syscall_linux.go +++ b/syscall_linux.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/syscall_openbsd.go b/syscall_openbsd.go index 670a257..be44e29 100644 --- a/syscall_openbsd.go +++ b/syscall_openbsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/syscall_windows.go b/syscall_windows.go index 989a61a..85707f9 100644 --- a/syscall_windows.go +++ b/syscall_windows.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/unixutils/pipe.go b/unixutils/pipe.go index 93f3486..8965227 100644 --- a/unixutils/pipe.go +++ b/unixutils/pipe.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/unixutils/select.go b/unixutils/select.go index 0bd1d9a..1111473 100644 --- a/unixutils/select.go +++ b/unixutils/select.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/zsyscall_darwin.go b/zsyscall_darwin.go index cef1732..a43c08c 100644 --- a/zsyscall_darwin.go +++ b/zsyscall_darwin.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/zsyscall_freebsd.go b/zsyscall_freebsd.go index 9e6765d..3a5840c 100644 --- a/zsyscall_freebsd.go +++ b/zsyscall_freebsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/zsyscall_linux.go b/zsyscall_linux.go index d93c1fb..d27d0c8 100644 --- a/zsyscall_linux.go +++ b/zsyscall_linux.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/zsyscall_openbsd.go b/zsyscall_openbsd.go index 3d8cee4..94291e6 100644 --- a/zsyscall_openbsd.go +++ b/zsyscall_openbsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2017 Cristian Maglie. All rights reserved. +// Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // From df9502307404d3d5c9998c7496bfe3852dd4a2dc Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 2 Dec 2019 19:51:36 +0100 Subject: [PATCH 078/181] Introducing go.mod for dep management --- .travis.yml | 6 +++--- README.md | 11 +++++------ doc.go | 8 ++++---- enumerator/doc.go | 2 +- enumerator/enumerator.go | 2 +- enumerator/example_getdetailedportlist_test.go | 2 +- enumerator/usb_darwin.go | 2 +- enumerator/usb_darwin_go1.9.go | 2 +- enumerator/usb_freebsd.go | 2 +- enumerator/usb_linux.go | 4 ++-- enumerator/usb_windows.go | 2 +- enumerator/usb_windows_test.go | 2 +- example_getportlist_test.go | 2 +- example_modem_bits_test.go | 2 +- example_serialport_test.go | 2 +- example_test.go | 2 +- go.mod | 9 +++++++++ go.sum | 14 ++++++++++++++ portlist/portlist.go | 2 +- serial.go | 2 +- serial_darwin.go | 2 +- serial_darwin_386.go | 2 +- serial_darwin_amd64.go | 2 +- serial_freebsd.go | 2 +- serial_linux.go | 2 +- serial_openbsd.go | 2 +- serial_unix.go | 5 ++--- serial_windows.go | 2 +- syscall_darwin.go | 2 +- syscall_freebsd.go | 2 +- syscall_linux.go | 2 +- syscall_openbsd.go | 2 +- syscall_windows.go | 2 +- unixutils/pipe.go | 2 +- unixutils/select.go | 2 +- zsyscall_darwin.go | 2 +- zsyscall_freebsd.go | 2 +- zsyscall_linux.go | 2 +- zsyscall_openbsd.go | 2 +- 39 files changed, 71 insertions(+), 50 deletions(-) create mode 100644 go.mod create mode 100644 go.sum diff --git a/.travis.yml b/.travis.yml index 6cbc813..755c306 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ go: - 1.10.x - 1.11.x -go_import_path: go.bug.st/serial.v1 +go_import_path: go.bug.st/serial env: - TEST_OS=linux TEST_ARCH=386 @@ -58,8 +58,8 @@ before_install: script: - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go get github.com/stretchr/testify/require - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go get golang.org/x/sys/windows - - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go build -v go.bug.st/serial.v1 - - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go test -c -v go.bug.st/serial.v1 + - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go build -v go.bug.st/serial + - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go test -c -v go.bug.st/serial notifications: email: diff --git a/README.md b/README.md index 4ff3ccb..b25130f 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,22 @@ [![Build Status](https://travis-ci.org/bugst/go-serial.svg?branch=v1)](https://travis-ci.org/bugst/go-serial) -# go.bug.st/serial.v1 +# go.bug.st/serial A cross-platform serial library for go-lang. ## Documentation and examples -See the godoc here: https://godoc.org/go.bug.st/serial.v1 +See the godoc here: https://godoc.org/go.bug.st/serial ## Development -If you want to contribute to the development of this library, you must clone this git repository directly into your `src` folder under `src/go.bug.st/serial.v1` and checkout the branch `v1`. +If you want to contribute to the development of this library, you must clone this git repository directly into your `src` folder under `src/go.bug.st/serial` and checkout the branch `v1`. ``` cd $GOPATH mkdir -p src/go.bug.st/ -git clone https://github.com/bugst/go-serial.git -b v1 src/go.bug.st/serial.v1 -go test go.bug.st/serial.v1 +git clone https://github.com/bugst/go-serial.git -b v1 src/go.bug.st/serial +go test go.bug.st/serial ``` ## What's new in v1 @@ -32,4 +32,3 @@ https://github.com/bugst/go-serial/pull/5/files The software is release under a BSD 3-clause license https://github.com/bugst/go-serial/blob/v1/LICENSE - diff --git a/doc.go b/doc.go index 71993f8..e54b87e 100644 --- a/doc.go +++ b/doc.go @@ -7,10 +7,10 @@ /* Package serial is a cross-platform serial library for the go language. -The canonical import for this library is go.bug.st/serial.v1 so the import line +The canonical import for this library is go.bug.st/serial so the import line is the following: - import "go.bug.st/serial.v1" + import "go.bug.st/serial" It is possible to get the list of available serial ports with the GetPortsList function: @@ -84,7 +84,7 @@ cable or a microcontroller development board) is possible to retrieve the USB metadata, like VID/PID or USB Serial Number, with the GetDetailedPortsList function in the enumerator package: - import "go.bug.st/serial.v1/enumerator" + import "go.bug.st/serial/enumerator" ports, err := enumerator.GetDetailedPortsList() if err != nil { @@ -110,4 +110,4 @@ Unfortunately the USB enumeration package for darwin (MacOSX) requires cgo to access the IOKit framework. This means that if you need USB enumeration on darwin you're forced to use cgo. */ -package serial // import "go.bug.st/serial.v1" +package serial diff --git a/enumerator/doc.go b/enumerator/doc.go index 7ffa75d..c3fc1cd 100644 --- a/enumerator/doc.go +++ b/enumerator/doc.go @@ -18,4 +18,4 @@ required in order to access the IOKit Framework. This means that the library cannot be easily cross compiled for GOOS=darwing targets. */ -package enumerator // import "go.bug.st/serial.v1/enumerator" +package enumerator diff --git a/enumerator/enumerator.go b/enumerator/enumerator.go index ba33f3d..0100a98 100644 --- a/enumerator/enumerator.go +++ b/enumerator/enumerator.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package enumerator // import "go.bug.st/serial.v1/enumerator" +package enumerator //go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output syscall_windows.go usb_windows.go diff --git a/enumerator/example_getdetailedportlist_test.go b/enumerator/example_getdetailedportlist_test.go index fb6c9a1..5b94e22 100644 --- a/enumerator/example_getdetailedportlist_test.go +++ b/enumerator/example_getdetailedportlist_test.go @@ -8,7 +8,7 @@ package enumerator_test import "fmt" import "log" -import "go.bug.st/serial.v1/enumerator" +import "go.bug.st/serial/enumerator" func ExampleGetDetailedPortsList() { ports, err := enumerator.GetDetailedPortsList() diff --git a/enumerator/usb_darwin.go b/enumerator/usb_darwin.go index 4709137..aa8b2fc 100644 --- a/enumerator/usb_darwin.go +++ b/enumerator/usb_darwin.go @@ -6,7 +6,7 @@ // +build go1.10,darwin -package enumerator // import "go.bug.st/serial.v1/enumerator" +package enumerator // #cgo LDFLAGS: -framework CoreFoundation -framework IOKit // #include diff --git a/enumerator/usb_darwin_go1.9.go b/enumerator/usb_darwin_go1.9.go index 26f842e..3ec911d 100644 --- a/enumerator/usb_darwin_go1.9.go +++ b/enumerator/usb_darwin_go1.9.go @@ -10,7 +10,7 @@ // and is no more maintained or bugfixed, please update your go version // to at least 1.10 to get the latest updates. -package enumerator // import "go.bug.st/serial.v1/enumerator" +package enumerator // #cgo LDFLAGS: -framework CoreFoundation -framework IOKit // #include diff --git a/enumerator/usb_freebsd.go b/enumerator/usb_freebsd.go index 460f000..3a43a85 100644 --- a/enumerator/usb_freebsd.go +++ b/enumerator/usb_freebsd.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package enumerator // import "go.bug.st/serial.v1/enumerator" +package enumerator func nativeGetDetailedPortsList() ([]*PortDetails, error) { // TODO diff --git a/enumerator/usb_linux.go b/enumerator/usb_linux.go index b67ca95..6133c63 100644 --- a/enumerator/usb_linux.go +++ b/enumerator/usb_linux.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package enumerator // import "go.bug.st/serial.v1/enumerator" +package enumerator import ( "bufio" @@ -12,7 +12,7 @@ import ( "os" "path/filepath" - "go.bug.st/serial.v1" + "go.bug.st/serial" ) func nativeGetDetailedPortsList() ([]*PortDetails, error) { diff --git a/enumerator/usb_windows.go b/enumerator/usb_windows.go index e9fd0d8..ab8cb49 100644 --- a/enumerator/usb_windows.go +++ b/enumerator/usb_windows.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package enumerator // import "go.bug.st/serial.v1/enumerator" +package enumerator import ( "fmt" diff --git a/enumerator/usb_windows_test.go b/enumerator/usb_windows_test.go index c227270..94016fd 100644 --- a/enumerator/usb_windows_test.go +++ b/enumerator/usb_windows_test.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package enumerator // import "go.bug.st/serial.v1/enumerator" +package enumerator import ( "testing" diff --git a/example_getportlist_test.go b/example_getportlist_test.go index 4a9a13c..54b2dc3 100644 --- a/example_getportlist_test.go +++ b/example_getportlist_test.go @@ -8,7 +8,7 @@ package serial_test import "fmt" import "log" -import "go.bug.st/serial.v1" +import "go.bug.st/serial" func ExampleGetPortsList() { ports, err := serial.GetPortsList() diff --git a/example_modem_bits_test.go b/example_modem_bits_test.go index 9f1bb27..a10d828 100644 --- a/example_modem_bits_test.go +++ b/example_modem_bits_test.go @@ -7,7 +7,7 @@ package serial_test import "log" -import "go.bug.st/serial.v1" +import "go.bug.st/serial" import "fmt" import "time" diff --git a/example_serialport_test.go b/example_serialport_test.go index 5a20cd7..91b8c79 100644 --- a/example_serialport_test.go +++ b/example_serialport_test.go @@ -8,7 +8,7 @@ package serial_test import "fmt" import "log" -import "go.bug.st/serial.v1" +import "go.bug.st/serial" func ExampleSerialPort_SetMode() { port, err := serial.Open("/dev/ttyACM0", &serial.Mode{}) diff --git a/example_test.go b/example_test.go index 2648fa0..8b0e747 100644 --- a/example_test.go +++ b/example_test.go @@ -8,7 +8,7 @@ package serial_test import "fmt" import "log" -import "go.bug.st/serial.v1" +import "go.bug.st/serial" // This example prints the list of serial ports and use the first one // to send a string "10,20,30" and prints the response on the screen. diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2035b85 --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module go.bug.st/serial + +go 1.13 + +require ( + github.com/creack/goselect v0.1.1 + github.com/stretchr/testify v1.4.0 + golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e6f7dc3 --- /dev/null +++ b/go.sum @@ -0,0 +1,14 @@ +github.com/creack/goselect v0.1.1 h1:tiSSgKE1eJtxs1h/VgGQWuXUP0YS4CDIFMp6vaI1ls0= +github.com/creack/goselect v0.1.1/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU= +golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/portlist/portlist.go b/portlist/portlist.go index 66b0494..5a55435 100644 --- a/portlist/portlist.go +++ b/portlist/portlist.go @@ -17,7 +17,7 @@ package main import "fmt" import "log" -import "go.bug.st/serial.v1/enumerator" +import "go.bug.st/serial/enumerator" func main() { ports, err := enumerator.GetDetailedPortsList() diff --git a/serial.go b/serial.go index ea3f873..1bf5bb9 100644 --- a/serial.go +++ b/serial.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial.v1" +package serial //go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go diff --git a/serial_darwin.go b/serial_darwin.go index aa70675..7bce6c9 100644 --- a/serial_darwin.go +++ b/serial_darwin.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial.v1" +package serial import "golang.org/x/sys/unix" diff --git a/serial_darwin_386.go b/serial_darwin_386.go index 6522c1f..fd637df 100644 --- a/serial_darwin_386.go +++ b/serial_darwin_386.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial.v1" +package serial import "golang.org/x/sys/unix" diff --git a/serial_darwin_amd64.go b/serial_darwin_amd64.go index 4560692..34b8d7a 100644 --- a/serial_darwin_amd64.go +++ b/serial_darwin_amd64.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial.v1" +package serial import "golang.org/x/sys/unix" diff --git a/serial_freebsd.go b/serial_freebsd.go index fbd2893..a56a4bf 100644 --- a/serial_freebsd.go +++ b/serial_freebsd.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial.v1" +package serial import "golang.org/x/sys/unix" diff --git a/serial_linux.go b/serial_linux.go index b0e2f3c..1c8fd8d 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial.v1" +package serial import "golang.org/x/sys/unix" diff --git a/serial_openbsd.go b/serial_openbsd.go index e18e41c..5f626f3 100644 --- a/serial_openbsd.go +++ b/serial_openbsd.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial.v1" +package serial import "golang.org/x/sys/unix" diff --git a/serial_unix.go b/serial_unix.go index 8cc530e..6b8f8e9 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -6,7 +6,7 @@ // +build linux darwin freebsd openbsd -package serial // import "go.bug.st/serial.v1" +package serial import ( "io/ioutil" @@ -16,8 +16,7 @@ import ( "unsafe" "golang.org/x/sys/unix" - - "go.bug.st/serial.v1/unixutils" + "go.bug.st/serial/unixutils" ) type unixPort struct { diff --git a/serial_windows.go b/serial_windows.go index 0ae145b..fdd2eaa 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial.v1" +package serial /* diff --git a/syscall_darwin.go b/syscall_darwin.go index be44e29..5ee0a2b 100644 --- a/syscall_darwin.go +++ b/syscall_darwin.go @@ -4,6 +4,6 @@ // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial.v1" +package serial //sys ioctl(fd int, req uint64, data uintptr) (err error) diff --git a/syscall_freebsd.go b/syscall_freebsd.go index be44e29..5ee0a2b 100644 --- a/syscall_freebsd.go +++ b/syscall_freebsd.go @@ -4,6 +4,6 @@ // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial.v1" +package serial //sys ioctl(fd int, req uint64, data uintptr) (err error) diff --git a/syscall_linux.go b/syscall_linux.go index be44e29..5ee0a2b 100644 --- a/syscall_linux.go +++ b/syscall_linux.go @@ -4,6 +4,6 @@ // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial.v1" +package serial //sys ioctl(fd int, req uint64, data uintptr) (err error) diff --git a/syscall_openbsd.go b/syscall_openbsd.go index be44e29..5ee0a2b 100644 --- a/syscall_openbsd.go +++ b/syscall_openbsd.go @@ -4,6 +4,6 @@ // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial.v1" +package serial //sys ioctl(fd int, req uint64, data uintptr) (err error) diff --git a/syscall_windows.go b/syscall_windows.go index 85707f9..ccd9508 100644 --- a/syscall_windows.go +++ b/syscall_windows.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -package serial // import "go.bug.st/serial.v1" +package serial //sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) = advapi32.RegEnumValueW diff --git a/unixutils/pipe.go b/unixutils/pipe.go index 8965227..a6be332 100644 --- a/unixutils/pipe.go +++ b/unixutils/pipe.go @@ -6,7 +6,7 @@ // +build linux darwin freebsd openbsd -package unixutils // import "go.bug.st/serial.v1/unixutils" +package unixutils import "syscall" import "fmt" diff --git a/unixutils/select.go b/unixutils/select.go index 1111473..40b591c 100644 --- a/unixutils/select.go +++ b/unixutils/select.go @@ -6,7 +6,7 @@ // +build linux darwin freebsd openbsd -package unixutils // "go.bug.st/serial.v1/unixutils" +package unixutils import ( "time" diff --git a/zsyscall_darwin.go b/zsyscall_darwin.go index a43c08c..9fd8772 100644 --- a/zsyscall_darwin.go +++ b/zsyscall_darwin.go @@ -8,7 +8,7 @@ // mksyscall.pl serial_darwin.go // The generated stub is modified to make it compile under the "serial" package -package serial // import "go.bug.st/serial.v1" +package serial import "golang.org/x/sys/unix" diff --git a/zsyscall_freebsd.go b/zsyscall_freebsd.go index 3a5840c..d35b1f3 100644 --- a/zsyscall_freebsd.go +++ b/zsyscall_freebsd.go @@ -8,7 +8,7 @@ // mksyscall.pl serial_freebsd.go // The generated stub is modified to make it compile under the "serial" package -package serial // import "go.bug.st/serial.v1" +package serial import "golang.org/x/sys/unix" diff --git a/zsyscall_linux.go b/zsyscall_linux.go index d27d0c8..1054f29 100644 --- a/zsyscall_linux.go +++ b/zsyscall_linux.go @@ -8,7 +8,7 @@ // mksyscall.pl serial_linux.go // The generated stub is modified to make it compile under the "serial" package -package serial // import "go.bug.st/serial.v1" +package serial import "golang.org/x/sys/unix" diff --git a/zsyscall_openbsd.go b/zsyscall_openbsd.go index 94291e6..7c63d52 100644 --- a/zsyscall_openbsd.go +++ b/zsyscall_openbsd.go @@ -8,7 +8,7 @@ // mksyscall.pl serial_openbsd.go // The generated stub is modified to make it compile under the "serial" package -package serial // import "go.bug.st/serial.v1" +package serial import "syscall" From f2a9ea925f9c8efabacd3d4e6ee854f18017aa14 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 2 Dec 2019 20:00:32 +0100 Subject: [PATCH 079/181] Updates to readme --- README.md | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index b25130f..79b2110 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Status](https://travis-ci.org/bugst/go-serial.svg?branch=v1)](https://travis-ci.org/bugst/go-serial) +[![Build Status](https://travis-ci.org/bugst/go-serial.svg?branch=master)](https://travis-ci.org/bugst/go-serial) # go.bug.st/serial @@ -8,24 +8,11 @@ A cross-platform serial library for go-lang. See the godoc here: https://godoc.org/go.bug.st/serial -## Development +## go.mod transition -If you want to contribute to the development of this library, you must clone this git repository directly into your `src` folder under `src/go.bug.st/serial` and checkout the branch `v1`. +This library now support `go.mod` with the import `go.bug.st/serial`. -``` -cd $GOPATH -mkdir -p src/go.bug.st/ -git clone https://github.com/bugst/go-serial.git -b v1 src/go.bug.st/serial -go test go.bug.st/serial -``` - -## What's new in v1 - -There are some API improvements, in particular object naming is now more idiomatic, class names are less redundant (for example `serial.SerialPort` is now called `serial.Port`), some internal class fields, constants or enumerations are now private and some methods have been moved into the proper interface. - -If you come from the version v0 and want to see the full list of API changes, please check this pull request: - -https://github.com/bugst/go-serial/pull/5/files +If you came from the pre-`go.mod` era please update your import paths from `go.bug.st/serial.v1` to `go.bug.st/serial` to receive new updates. Anyway, the latest "`v1`" release should still be avaiable using the old import. ## License From 15b0a663d51a1c35f67104e9d4fe4753436f39bc Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 3 Dec 2019 16:39:18 +0100 Subject: [PATCH 080/181] Switch to gh-actions (WIP 1/2) (#67) --- .github/workflows/test.yaml | 36 +++++++++++++++++++ .travis.yml | 70 ------------------------------------- 2 files changed, 36 insertions(+), 70 deletions(-) create mode 100644 .github/workflows/test.yaml delete mode 100644 .travis.yml diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..6d6a719 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,36 @@ +name: test + +on: + push: + branches: + - master + pull_request: + +jobs: + native-os-build: + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macOS-latest] + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-go@v1 + with: + go-version: "1.13" + - name: Build native + run: GOARCH=amd64 go build -v ./... + shell: bash + - name: Run unit tests + run: go test -v ./... + shell: bash + - name: Cross-build for 386 + if: matrix.os != 'macOS-latest' + run: GOARCH=386 go build -v ./... + shell: bash + - name: Cross-build for arm + if: matrix.os != 'macOS-latest' + run: GOARCH=arm go build -v ./... + shell: bash + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 755c306..0000000 --- a/.travis.yml +++ /dev/null @@ -1,70 +0,0 @@ -language: go - -os: - - linux - - osx - -go: - - 1.7.x - - 1.8.x - - 1.9.x - - 1.10.x - - 1.11.x - -go_import_path: go.bug.st/serial - -env: - - TEST_OS=linux TEST_ARCH=386 - - TEST_OS=linux TEST_ARCH=amd64 - - TEST_OS=linux TEST_ARCH=arm - - TEST_OS=windows TEST_ARCH=386 - - TEST_OS=windows TEST_ARCH=amd64 - - TEST_OS=darwin TEST_ARCH=386 - - TEST_OS=darwin TEST_ARCH=amd64 - - TEST_OS=freebsd TEST_ARCH=amd64 - - TEST_OS=openbsd TEST_ARCH=amd64 - - TEST_OS=openbsd TEST_ARCH=386 - - TEST_OS=openbsd TEST_ARCH=arm - -matrix: - exclude: - - os: linux - env: TEST_OS=darwin TEST_ARCH=386 - - os: linux - env: TEST_OS=darwin TEST_ARCH=amd64 - - os: osx - env: TEST_OS=linux TEST_ARCH=386 - - os: osx - env: TEST_OS=linux TEST_ARCH=amd64 - - os: osx - env: TEST_OS=linux TEST_ARCH=arm - - os: osx - env: TEST_OS=windows TEST_ARCH=386 - - os: osx - env: TEST_OS=windows TEST_ARCH=amd64 - - os: osx - env: TEST_OS=freebsd TEST_ARCH=amd64 - - os: osx - env: TEST_OS=openbsd TEST_ARCH=amd64 - - os: osx - env: TEST_OS=openbsd TEST_ARCH=386 - - os: osx - env: TEST_OS=openbsd TEST_ARCH=arm - allow_failures: - - env: TEST_OS=openbsd TEST_ARCH=arm - -before_install: - -script: - - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go get github.com/stretchr/testify/require - - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go get golang.org/x/sys/windows - - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go build -v go.bug.st/serial - - GOARM=5 GO386=387 GOOS=$TEST_OS GOARCH=$TEST_ARCH go test -c -v go.bug.st/serial - -notifications: - email: - recipients: - - c.maglie@bug.st - on_success: change - on_failure: always - From 96546e190f4921fe3cc1585af89703c4e8a3b17f Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 3 Dec 2019 16:48:45 +0100 Subject: [PATCH 081/181] Added some cross-os builds --- .github/workflows/test.yaml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 6d6a719..98ddb74 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -34,3 +34,24 @@ jobs: run: GOARCH=arm go build -v ./... shell: bash + cross-os-build: + strategy: + matrix: + go-os-pairs: + - "freebsd amd64" + - "openbsd amd64" + - "openbsd 386" + - "openbsd arm" + + runs-on: "ubuntu-latest" + + steps: + - uses: actions/checkout@v1 + - uses: actions/setup-go@v1 + with: + go-version: "1.13" + - name: Cross-build + run: | + set ${{ matrix.go-os-pairs }} + GOOS=$1 GOARCH=$2 go build -v ./... + shell: bash From 88e081691b5b1cbe104605613bb8a584f562093d Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 3 Dec 2019 16:53:39 +0100 Subject: [PATCH 082/181] Fixed enumerator build for openbsd (unsupported operation) --- enumerator/usb_openbsd.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 enumerator/usb_openbsd.go diff --git a/enumerator/usb_openbsd.go b/enumerator/usb_openbsd.go new file mode 100644 index 0000000..3a43a85 --- /dev/null +++ b/enumerator/usb_openbsd.go @@ -0,0 +1,12 @@ +// +// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +package enumerator + +func nativeGetDetailedPortsList() ([]*PortDetails, error) { + // TODO + return nil, &PortEnumerationError{} +} From d1a463e3240c82e534a2ed6404b25e19778bddbc Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 3 Dec 2019 16:56:42 +0100 Subject: [PATCH 083/181] Updated build-passing badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 79b2110..dc3588f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Status](https://travis-ci.org/bugst/go-serial.svg?branch=master)](https://travis-ci.org/bugst/go-serial) +[![Build Status](https://github.com/bugst/go-serial/workflows/test/badge.svg)](https://github.com/bugst/go-serial/actions?workflow=test) # go.bug.st/serial From 6a673bf917a61cbd72b5ccb6c9e1f4cea39684f4 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 3 Dec 2019 20:26:03 +0100 Subject: [PATCH 084/181] Transparently handle EINTR during Read This has been observed in particular on MacOS, in this case just retry the call without throwing the error back to the user. Related to: https://github.com/arduino/arduino-cli/issues/504 https://github.com/arduino/arduino-pro-ide/issues/157 --- serial_unix.go | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/serial_unix.go b/serial_unix.go index 6b8f8e9..8831fc2 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -15,8 +15,8 @@ import ( "sync" "unsafe" - "golang.org/x/sys/unix" "go.bug.st/serial/unixutils" + "golang.org/x/sys/unix" ) type unixPort struct { @@ -51,7 +51,7 @@ func (port *unixPort) Close() error { return nil } -func (port *unixPort) Read(p []byte) (n int, err error) { +func (port *unixPort) Read(p []byte) (int, error) { port.closeLock.RLock() defer port.closeLock.RUnlock() if !port.opened { @@ -59,14 +59,23 @@ func (port *unixPort) Read(p []byte) (n int, err error) { } fds := unixutils.NewFDSet(port.handle, port.closeSignal.ReadFD()) - res, err := unixutils.Select(fds, nil, fds, -1) - if err != nil { - return 0, err - } - if res.IsReadable(port.closeSignal.ReadFD()) { - return 0, &PortError{code: PortClosed} + for { + res, err := unixutils.Select(fds, nil, fds, -1) + if err == unix.EINTR { + continue + } + if err != nil { + return 0, err + } + if res.IsReadable(port.closeSignal.ReadFD()) { + return 0, &PortError{code: PortClosed} + } + n, err := unix.Read(port.handle, p) + if err == unix.EINTR { + continue + } + return n, err } - return unix.Read(port.handle, p) } func (port *unixPort) Write(p []byte) (n int, err error) { From 900c828b750235e07f7a8d08c51a4abbbacaba27 Mon Sep 17 00:00:00 2001 From: giacomoberlin <31045160+giacomoberlin@users.noreply.github.com> Date: Wed, 16 Aug 2017 06:49:34 +0200 Subject: [PATCH 085/181] fixed API procSetupDiGetDeviceRegistryPropertyW confusingly, in this prototype Microsoft decided to split bufSize and reqSize --- enumerator/syscall_windows.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/enumerator/syscall_windows.go b/enumerator/syscall_windows.go index cd598c7..d969274 100644 --- a/enumerator/syscall_windows.go +++ b/enumerator/syscall_windows.go @@ -106,8 +106,8 @@ func setupDiOpenDevRegKey(set devicesSet, devInfo *devInfoData, scope dicsScope, return } -func setupDiGetDeviceRegistryProperty(set devicesSet, devInfo *devInfoData, property deviceProperty, propertyType *uint32, outValue *byte, outSize *uint32, reqSize *uint32) (res bool) { - r0, _, _ := syscall.Syscall9(procSetupDiGetDeviceRegistryPropertyW.Addr(), 7, uintptr(set), uintptr(unsafe.Pointer(devInfo)), uintptr(property), uintptr(unsafe.Pointer(propertyType)), uintptr(unsafe.Pointer(outValue)), uintptr(unsafe.Pointer(outSize)), uintptr(unsafe.Pointer(reqSize)), 0, 0) +func setupDiGetDeviceRegistryProperty(set devicesSet, devInfo *devInfoData, property deviceProperty, propertyType *uint32, outValue *byte, bufSize uint32, reqSize *uint32) (res bool) { + r0, _, _ := syscall.Syscall9(procSetupDiGetDeviceRegistryPropertyW.Addr(), 7, uintptr(set), uintptr(unsafe.Pointer(devInfo)), uintptr(property), uintptr(unsafe.Pointer(propertyType)), uintptr(unsafe.Pointer(outValue)), uintptr(bufSize), uintptr(unsafe.Pointer(reqSize)), 0, 0) res = r0 != 0 return } From 4df0da8377dcf8b49f3726b1e0869f8cf8cbf4db Mon Sep 17 00:00:00 2001 From: giacomoberlin <31045160+giacomoberlin@users.noreply.github.com> Date: Wed, 16 Aug 2017 07:05:12 +0200 Subject: [PATCH 086/181] completed retrievePortDetailsFromDevInfo in retrievePortDetailsFromDevInfo: - updated call to setupDiGetDeviceRegistryProperty with the same style used elsewhere (first get the reqSize, then call) - use of generic variable names, in case the function is extended to fetch other values than spdrpFriendlyName (or spdrpDeviceDesc) - re-enabled the assignment of details.Product --- enumerator/usb_windows.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/enumerator/usb_windows.go b/enumerator/usb_windows.go index ab8cb49..7ebe854 100644 --- a/enumerator/usb_windows.go +++ b/enumerator/usb_windows.go @@ -60,7 +60,7 @@ func parseDeviceID(deviceID string, details *PortDetails) { //sys setupDiEnumDeviceInfo(set devicesSet, index uint32, info *devInfoData) (err error) = setupapi.SetupDiEnumDeviceInfo //sys setupDiGetDeviceInstanceId(set devicesSet, devInfo *devInfoData, devInstanceId unsafe.Pointer, devInstanceIdSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiGetDeviceInstanceIdW //sys setupDiOpenDevRegKey(set devicesSet, devInfo *devInfoData, scope dicsScope, hwProfile uint32, keyType uint32, samDesired regsam) (hkey syscall.Handle, err error) = setupapi.SetupDiOpenDevRegKey -//sys setupDiGetDeviceRegistryProperty(set devicesSet, devInfo *devInfoData, property deviceProperty, propertyType *uint32, outValue *byte, outSize *uint32, reqSize *uint32) (res bool) = setupapi.SetupDiGetDeviceRegistryPropertyW +//sys setupDiGetDeviceRegistryProperty(set devicesSet, devInfo *devInfoData, property deviceProperty, propertyType *uint32, outValue *byte, bufSize uint32, reqSize *uint32) (res bool) = setupapi.SetupDiGetDeviceRegistryPropertyW // Device registry property codes // (Codes marked as read-only (R) may only be used for @@ -291,11 +291,15 @@ func retrievePortDetailsFromDevInfo(device *deviceInfo, details *PortDetails) er } parseDeviceID(deviceID, details) - var friendlyName [1024]uint16 - friendlyNameP := (*byte)(unsafe.Pointer(&friendlyName[0])) - friendlyNameSize := uint32(len(friendlyName) * 2) - if setupDiGetDeviceRegistryProperty(device.set, &device.data, spdrpDeviceDesc /* spdrpFriendlyName */, nil, friendlyNameP, &friendlyNameSize, nil) { - //details.Product = syscall.UTF16ToString(friendlyName[:]) + /* spdrpDeviceDesc returns a generic name, e.g.: "CDC-ACM", which will be the same for 2 identical devices attached + while spdrpFriendlyName returns a specific name, e.g.: "CDC-ACM (COM44)", + the result of spdrpFriendlyName is therefore unique and suitable as an alternative string to for a port choice */ + n := uint32(0) + setupDiGetDeviceRegistryProperty(device.set, &device.data, /* spdrpDeviceDesc */ spdrpFriendlyName, nil, nil, 0, &n) + buff := make([]uint16, n*2) + buffP := (*byte)(unsafe.Pointer(&buff[0])) + if setupDiGetDeviceRegistryProperty(device.set, &device.data, /* spdrpDeviceDesc */ spdrpFriendlyName, nil, buffP, n, &n) { + details.Product = syscall.UTF16ToString(buff[:]) } return nil From f0d91f6e0dc22ea585adb3870fd5407a66ef2acb Mon Sep 17 00:00:00 2001 From: giacomoberlin <31045160+giacomoberlin@users.noreply.github.com> Date: Wed, 16 Aug 2017 07:12:40 +0200 Subject: [PATCH 087/181] Product in PortDetails struct un-commented Product member of the struct PortDetails. This goes with the pull request "completed retrievePortDetailsFromDevInfo #40". Apologies for not submitting together... --- enumerator/enumerator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enumerator/enumerator.go b/enumerator/enumerator.go index 0100a98..91bb6e3 100644 --- a/enumerator/enumerator.go +++ b/enumerator/enumerator.go @@ -18,7 +18,7 @@ type PortDetails struct { SerialNumber string // Manufacturer string - // Product string + Product string } // GetDetailedPortsList retrieve ports details like USB VID/PID. From 820cffa34181a301eae8e3e2397aa4240fab0387 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 18 Apr 2020 22:22:31 +0100 Subject: [PATCH 088/181] Added doc for new USB Product field (windows-only) --- enumerator/enumerator.go | 5 ++++- portlist/portlist.go | 16 +++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/enumerator/enumerator.go b/enumerator/enumerator.go index 91bb6e3..41f88fe 100644 --- a/enumerator/enumerator.go +++ b/enumerator/enumerator.go @@ -18,7 +18,10 @@ type PortDetails struct { SerialNumber string // Manufacturer string - Product string + + // Product is an OS-dependent string that describes the serial port, it may + // be not always available and it may be different across OS. + Product string } // GetDetailedPortsList retrieve ports details like USB VID/PID. diff --git a/portlist/portlist.go b/portlist/portlist.go index 5a55435..cbf4c8c 100644 --- a/portlist/portlist.go +++ b/portlist/portlist.go @@ -15,9 +15,12 @@ // package main -import "fmt" -import "log" -import "go.bug.st/serial/enumerator" +import ( + "fmt" + "log" + + "go.bug.st/serial/enumerator" +) func main() { ports, err := enumerator.GetDetailedPortsList() @@ -29,9 +32,12 @@ func main() { } for _, port := range ports { fmt.Printf("Port: %s\n", port.Name) + if port.Product != "" { + fmt.Printf(" Product Name: %s\n", port.Product) + } if port.IsUSB { - fmt.Printf(" USB ID %s:%s\n", port.VID, port.PID) - fmt.Printf(" USB serial %s\n", port.SerialNumber) + fmt.Printf(" USB ID : %s:%s\n", port.VID, port.PID) + fmt.Printf(" USB serial : %s\n", port.SerialNumber) } } } From 9f6eea0188e1ffe4ba7190c8e7bf3c08667f20d5 Mon Sep 17 00:00:00 2001 From: martin-zaber Date: Thu, 21 Mar 2019 09:51:03 -0700 Subject: [PATCH 089/181] Fixing getPortList on windows without any ports --- serial_windows.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/serial_windows.go b/serial_windows.go index 0ae145b..88c14c2 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -30,7 +30,10 @@ func nativeGetPortsList() ([]string, error) { } var h syscall.Handle - if syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE, subKey, 0, syscall.KEY_READ, &h) != nil { + if err := syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE, subKey, 0, syscall.KEY_READ, &h); err != nil { + if errno, isErrno := err.(syscall.Errno); isErrno && errno == syscall.ERROR_FILE_NOT_FOUND { + return []string{}, nil + } return nil, &PortError{code: ErrorEnumeratingPorts} } defer syscall.RegCloseKey(h) From 1131ba52fdadb0d07a0fe7332e3b58aa85bfaa61 Mon Sep 17 00:00:00 2001 From: Andrey Demenev Date: Sun, 3 Feb 2019 03:35:32 +1000 Subject: [PATCH 090/181] Do not return -1 from Read() and Write() Closes #55 --- serial_unix.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/serial_unix.go b/serial_unix.go index 8831fc2..c796c30 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -74,12 +74,19 @@ func (port *unixPort) Read(p []byte) (int, error) { if err == unix.EINTR { continue } + if n < 0 { // Do not return -1 unix errors + n = 0 + } return n, err } } func (port *unixPort) Write(p []byte) (n int, err error) { - return unix.Write(port.handle, p) + n, err = unix.Write(port.handle, p) + if n < 0 { // Do not return -1 unix errors + n = 0 + } + return } func (port *unixPort) ResetInputBuffer() error { From 1b760a82af99fbfe5a084c31231d4fdb0ef8df9f Mon Sep 17 00:00:00 2001 From: angri Date: Fri, 15 Sep 2017 19:27:16 +0300 Subject: [PATCH 091/181] allow Close() to be called from other goroutine than Read The only way to interrupt running Read() method of io.Reader is to call Close from another goroutine. What happens is that go runtime builtin race detector complains about unsynchronised access to "opened" member, which is set in Close and checked in Read. It doesn't seem to be dangerous, but 1) you never know, and 2) it makes your life more difficult when you need to debug some other data races in a complex project that uses serial. Proposed fix changes simple access to "opened" to atomic operations. It's also supposed to fix #38 --- serial_unix.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/serial_unix.go b/serial_unix.go index c796c30..c33bca8 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -13,6 +13,7 @@ import ( "regexp" "strings" "sync" + "sync/atomic" "unsafe" "go.bug.st/serial/unixutils" @@ -24,16 +25,18 @@ type unixPort struct { closeLock sync.RWMutex closeSignal *unixutils.Pipe - opened bool + opened uint32 } func (port *unixPort) Close() error { + if !atomic.CompareAndSwapUint32(&port.opened, 1, 0) { + return &PortError{code: PortClosed} + } // Close port port.releaseExclusiveAccess() if err := unix.Close(port.handle); err != nil { return err } - port.opened = false if port.closeSignal != nil { // Send close signal to all pending reads (if any) @@ -54,7 +57,7 @@ func (port *unixPort) Close() error { func (port *unixPort) Read(p []byte) (int, error) { port.closeLock.RLock() defer port.closeLock.RUnlock() - if !port.opened { + if atomic.LoadUint32(&port.opened) != 1 { return 0, &PortError{code: PortClosed} } @@ -169,7 +172,7 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { } port := &unixPort{ handle: h, - opened: true, + opened: 1, } // Setup serial port From 6500d68fc8f04a6a79c5d70a9070536b8103ef7b Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 19 Apr 2020 00:20:16 +0200 Subject: [PATCH 092/181] Added test to detect correct behaviour in Close and Read ops --- .github/workflows/test.yaml | 6 +++++- go.sum | 1 + serial_linux_test.go | 41 +++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 serial_linux_test.go diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 98ddb74..ff195d1 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -22,8 +22,12 @@ jobs: - name: Build native run: GOARCH=amd64 go build -v ./... shell: bash + - name: Install socat + if: matrix.os == 'ubuntu-latest' + run: sudo apt-get install socat + shell: bash - name: Run unit tests - run: go test -v ./... + run: go test -v -race ./... shell: bash - name: Cross-build for 386 if: matrix.os != 'macOS-latest' diff --git a/go.sum b/go.sum index e6f7dc3..b1c6e18 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,7 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU= golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/serial_linux_test.go b/serial_linux_test.go new file mode 100644 index 0000000..219efd5 --- /dev/null +++ b/serial_linux_test.go @@ -0,0 +1,41 @@ +// +// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// Testing code idea and fix thanks to @angri +// https://github.com/bugst/go-serial/pull/42 +// + +package serial + +import ( + "context" + "os/exec" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestSerialReadAndCloseConcurrency(t *testing.T) { + + // Run this test with race detector to actually test that + // the correct multitasking behaviour is happening. + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + cmd := exec.CommandContext(ctx, "socat", "STDIO", "pty,link=/tmp/faketty") + require.NoError(t, cmd.Start()) + go cmd.Wait() + // let our fake serial port node to appear + time.Sleep(time.Millisecond * 100) + + port, err := Open("/tmp/faketty", &Mode{}) + require.NoError(t, err) + buf := make([]byte, 100) + go port.Read(buf) + // let port.Read to start + time.Sleep(time.Millisecond * 1) + port.Close() +} From 545e830e04d5b857d32ca048923f379de6ee0589 Mon Sep 17 00:00:00 2001 From: Tobias Kaupat Date: Mon, 27 Nov 2017 18:28:08 +0100 Subject: [PATCH 093/181] Allow multiple calls to close --- serial_windows.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/serial_windows.go b/serial_windows.go index 6ccaa86..01fc9a6 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -17,7 +17,7 @@ package serial */ -import "syscall" +import ("syscall") type windowsPort struct { handle syscall.Handle @@ -58,6 +58,12 @@ func nativeGetPortsList() ([]string, error) { } func (port *windowsPort) Close() error { + defer func() { + port.handle = 0 + }() + if port.handle == 0 { + return nil + } return syscall.CloseHandle(port.handle) } From f0282e0b50e296f54fdad6a7b7b0eae14b62522b Mon Sep 17 00:00:00 2001 From: Tobias Kaupat Date: Thu, 30 Nov 2017 12:08:22 +0100 Subject: [PATCH 094/181] Use mutex on Close() to avoid data races on windows --- serial_windows.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/serial_windows.go b/serial_windows.go index 01fc9a6..e2c40f5 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -17,9 +17,13 @@ package serial */ -import ("syscall") +import ( + "syscall" + "sync" +) type windowsPort struct { + mu sync.Mutex handle syscall.Handle } @@ -58,8 +62,10 @@ func nativeGetPortsList() ([]string, error) { } func (port *windowsPort) Close() error { + port.mu.Lock() defer func() { port.handle = 0 + port.mu.Unlock() }() if port.handle == 0 { return nil From 66bfcede429ff7f79e7a12d48d656166f9d9c8ab Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 19 Apr 2020 00:47:22 +0200 Subject: [PATCH 095/181] Added test for port double close --- serial_linux_test.go | 15 +++++++++++++++ serial_unix.go | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/serial_linux_test.go b/serial_linux_test.go index 219efd5..5eaea0f 100644 --- a/serial_linux_test.go +++ b/serial_linux_test.go @@ -39,3 +39,18 @@ func TestSerialReadAndCloseConcurrency(t *testing.T) { time.Sleep(time.Millisecond * 1) port.Close() } + +func TestDoubleCloseIsNoop(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + cmd := exec.CommandContext(ctx, "socat", "STDIO", "pty,link=/tmp/faketty") + require.NoError(t, cmd.Start()) + go cmd.Wait() + // let our fake serial port node to appear + time.Sleep(time.Millisecond * 100) + + port, err := Open("/tmp/faketty", &Mode{}) + require.NoError(t, err) + require.NoError(t, port.Close()) + require.NoError(t, port.Close()) +} diff --git a/serial_unix.go b/serial_unix.go index c33bca8..2e72473 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -30,8 +30,9 @@ type unixPort struct { func (port *unixPort) Close() error { if !atomic.CompareAndSwapUint32(&port.opened, 1, 0) { - return &PortError{code: PortClosed} + return nil } + // Close port port.releaseExclusiveAccess() if err := unix.Close(port.handle); err != nil { From 49ab90eff3d2b2747212259972a95162b2c973bf Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 20 Apr 2020 00:08:51 +0200 Subject: [PATCH 096/181] Updates to readme --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dc3588f..b05796c 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,16 @@ See the godoc here: https://godoc.org/go.bug.st/serial This library now support `go.mod` with the import `go.bug.st/serial`. -If you came from the pre-`go.mod` era please update your import paths from `go.bug.st/serial.v1` to `go.bug.st/serial` to receive new updates. Anyway, the latest "`v1`" release should still be avaiable using the old import. +If you came from the pre-`go.mod` era please update your import paths from `go.bug.st/serial.v1` to `go.bug.st/serial` to receive new updates. Anyway, the latest `v1` release should still be avaiable using the old import. + +## Credits + +:sparkles: Thanks to all awesome [contributors]! :sparkles: ## License -The software is release under a BSD 3-clause license +The software is release under a [BSD 3-clause license] + +[contributors]: https://github.com/bugst/go-serial/graphs/contributors +[BSD 3-clause license]: https://github.com/bugst/go-serial/blob/master/LICENSE -https://github.com/bugst/go-serial/blob/v1/LICENSE From 640e755b9433574aa3e73b230d98c187c962e85d Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Mon, 20 Apr 2020 01:58:37 -0400 Subject: [PATCH 097/181] Wait for socat to be ready before running full test. The -D option tells it to print something just before starting the transfer phase, which should be enough to ensure that all the file links are available without having to guess a suitable timeout. --- serial_linux_test.go | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/serial_linux_test.go b/serial_linux_test.go index 5eaea0f..01e3923 100644 --- a/serial_linux_test.go +++ b/serial_linux_test.go @@ -18,6 +18,20 @@ import ( "github.com/stretchr/testify/require" ) +func startSocatAndWaitForPort(t *testing.T, ctx context.Context) *exec.Cmd { + cmd := exec.CommandContext(ctx, "socat", "-D", "STDIO", "pty,link=/tmp/faketty") + r, err := cmd.StderrPipe() + require.NoError(t, err) + require.NoError(t, cmd.Start()) + // Let our fake serial port node appear. + // socat will write to stderr before starting transfer phase; + // we don't really care what, just that it did, because then it's ready. + buf := make([]byte, 1024) + _, err = r.Read(buf) + require.NoError(t, err) + return cmd +} + func TestSerialReadAndCloseConcurrency(t *testing.T) { // Run this test with race detector to actually test that @@ -25,11 +39,8 @@ func TestSerialReadAndCloseConcurrency(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - cmd := exec.CommandContext(ctx, "socat", "STDIO", "pty,link=/tmp/faketty") - require.NoError(t, cmd.Start()) + cmd := startSocatAndWaitForPort(t, ctx) go cmd.Wait() - // let our fake serial port node to appear - time.Sleep(time.Millisecond * 100) port, err := Open("/tmp/faketty", &Mode{}) require.NoError(t, err) @@ -43,11 +54,8 @@ func TestSerialReadAndCloseConcurrency(t *testing.T) { func TestDoubleCloseIsNoop(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - cmd := exec.CommandContext(ctx, "socat", "STDIO", "pty,link=/tmp/faketty") - require.NoError(t, cmd.Start()) + cmd := startSocatAndWaitForPort(t, ctx) go cmd.Wait() - // let our fake serial port node to appear - time.Sleep(time.Millisecond * 100) port, err := Open("/tmp/faketty", &Mode{}) require.NoError(t, err) From f88d9008048b8a5ec2c2afcc5ad270063de06cfa Mon Sep 17 00:00:00 2001 From: Lukas Becker <36208965+lukasbeckercode@users.noreply.github.com> Date: Fri, 8 May 2020 12:43:23 +0200 Subject: [PATCH 098/181] changed the example_test.go so it stops reading when "\n\r" is received --- example_test.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/example_test.go b/example_test.go index 8b0e747..72378f3 100644 --- a/example_test.go +++ b/example_test.go @@ -6,7 +6,10 @@ package serial_test -import "fmt" +import ( + "fmt" + "strings" +) import "log" import "go.bug.st/serial" @@ -48,18 +51,25 @@ func Example_sendAndReceive() { fmt.Printf("Sent %v bytes\n", n) // Read and print the response + buff := make([]byte, 100) for { // Reads up to 100 bytes n, err := port.Read(buff) if err != nil { log.Fatal(err) - break + } if n == 0 { fmt.Println("\nEOF") break } - fmt.Printf("%v", string(buff[:n])) + + if strings.Contains(string(buff[:n]), "\n\r") { //if we receive a newline and carriage return, stop reading + + fmt.Printf("%v", string(buff)) //Print the data we received + break + } + } } From b0154211af2435afe0e49e82c0885bf37ca2f0a1 Mon Sep 17 00:00:00 2001 From: Lukas Becker <36208965+lukasbeckercode@users.noreply.github.com> Date: Fri, 8 May 2020 13:38:56 +0200 Subject: [PATCH 099/181] removed empty line Co-authored-by: Cristian Maglie --- example_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/example_test.go b/example_test.go index 72378f3..98b624c 100644 --- a/example_test.go +++ b/example_test.go @@ -58,7 +58,6 @@ func Example_sendAndReceive() { n, err := port.Read(buff) if err != nil { log.Fatal(err) - } if n == 0 { fmt.Println("\nEOF") From 578b2ec83207347ce9bdc7748890388ac407f2aa Mon Sep 17 00:00:00 2001 From: Lukas Becker <36208965+lukasbeckercode@users.noreply.github.com> Date: Fri, 8 May 2020 13:40:05 +0200 Subject: [PATCH 100/181] only check for newline instead of newline and carraiage return Co-authored-by: Cristian Maglie --- example_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/example_test.go b/example_test.go index 98b624c..5447199 100644 --- a/example_test.go +++ b/example_test.go @@ -64,11 +64,11 @@ func Example_sendAndReceive() { break } - if strings.Contains(string(buff[:n]), "\n\r") { //if we receive a newline and carriage return, stop reading + fmt.Printf("%s", string(buff[:n])) - fmt.Printf("%v", string(buff)) //Print the data we received + // If we receive a newline stop reading + if strings.Contains(string(buff[:n]), "\n") { break } - } } From eb474d2569e57b1a47c1d388dc2d9f75194a5e34 Mon Sep 17 00:00:00 2001 From: Maddie Zhan Date: Thu, 10 Sep 2020 16:57:12 +0800 Subject: [PATCH 101/181] Use golang.org/x/sys/unix provided ioctl functions --- go.mod | 2 +- go.sum | 4 ++-- serial_unix.go | 21 ++++++++------------- syscall_darwin.go | 9 --------- syscall_freebsd.go | 9 --------- syscall_linux.go | 9 --------- syscall_openbsd.go | 9 --------- zsyscall_darwin.go | 21 --------------------- zsyscall_freebsd.go | 21 --------------------- zsyscall_linux.go | 21 --------------------- zsyscall_openbsd.go | 21 --------------------- 11 files changed, 11 insertions(+), 136 deletions(-) delete mode 100644 syscall_darwin.go delete mode 100644 syscall_freebsd.go delete mode 100644 syscall_linux.go delete mode 100644 syscall_openbsd.go delete mode 100644 zsyscall_darwin.go delete mode 100644 zsyscall_freebsd.go delete mode 100644 zsyscall_linux.go delete mode 100644 zsyscall_openbsd.go diff --git a/go.mod b/go.mod index 2035b85..685d2bc 100644 --- a/go.mod +++ b/go.mod @@ -5,5 +5,5 @@ go 1.13 require ( github.com/creack/goselect v0.1.1 github.com/stretchr/testify v1.4.0 - golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 + golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 ) diff --git a/go.sum b/go.sum index b1c6e18..9f04ea9 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU= -golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= diff --git a/serial_unix.go b/serial_unix.go index 2e72473..f9e887c 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -14,7 +14,6 @@ import ( "strings" "sync" "sync/atomic" - "unsafe" "go.bug.st/serial/unixutils" "golang.org/x/sys/unix" @@ -94,11 +93,11 @@ func (port *unixPort) Write(p []byte) (n int, err error) { } func (port *unixPort) ResetInputBuffer() error { - return ioctl(port.handle, ioctlTcflsh, unix.TCIFLUSH) + return unix.IoctlSetInt(port.handle, ioctlTcflsh, unix.TCIFLUSH) } func (port *unixPort) ResetOutputBuffer() error { - return ioctl(port.handle, ioctlTcflsh, unix.TCOFLUSH) + return unix.IoctlSetInt(port.handle, ioctlTcflsh, unix.TCOFLUSH) } func (port *unixPort) SetMode(mode *Mode) error { @@ -390,29 +389,25 @@ func setRawMode(settings *unix.Termios) { // native syscall wrapper functions func (port *unixPort) getTermSettings() (*unix.Termios, error) { - settings := &unix.Termios{} - err := ioctl(port.handle, ioctlTcgetattr, uintptr(unsafe.Pointer(settings))) - return settings, err + return unix.IoctlGetTermios(port.handle, ioctlTcgetattr) } func (port *unixPort) setTermSettings(settings *unix.Termios) error { - return ioctl(port.handle, ioctlTcsetattr, uintptr(unsafe.Pointer(settings))) + return unix.IoctlSetTermios(port.handle, ioctlTcsetattr, settings) } func (port *unixPort) getModemBitsStatus() (int, error) { - var status int - err := ioctl(port.handle, unix.TIOCMGET, uintptr(unsafe.Pointer(&status))) - return status, err + return unix.IoctlGetInt(port.handle, unix.TIOCMGET) } func (port *unixPort) setModemBitsStatus(status int) error { - return ioctl(port.handle, unix.TIOCMSET, uintptr(unsafe.Pointer(&status))) + return unix.IoctlSetPointerInt(port.handle, unix.TIOCMSET, status) } func (port *unixPort) acquireExclusiveAccess() error { - return ioctl(port.handle, unix.TIOCEXCL, 0) + return unix.IoctlSetInt(port.handle, unix.TIOCEXCL, 0) } func (port *unixPort) releaseExclusiveAccess() error { - return ioctl(port.handle, unix.TIOCNXCL, 0) + return unix.IoctlSetInt(port.handle, unix.TIOCNXCL, 0) } diff --git a/syscall_darwin.go b/syscall_darwin.go deleted file mode 100644 index 5ee0a2b..0000000 --- a/syscall_darwin.go +++ /dev/null @@ -1,9 +0,0 @@ -// -// Copyright 2014-2020 Cristian Maglie. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -package serial - -//sys ioctl(fd int, req uint64, data uintptr) (err error) diff --git a/syscall_freebsd.go b/syscall_freebsd.go deleted file mode 100644 index 5ee0a2b..0000000 --- a/syscall_freebsd.go +++ /dev/null @@ -1,9 +0,0 @@ -// -// Copyright 2014-2020 Cristian Maglie. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -package serial - -//sys ioctl(fd int, req uint64, data uintptr) (err error) diff --git a/syscall_linux.go b/syscall_linux.go deleted file mode 100644 index 5ee0a2b..0000000 --- a/syscall_linux.go +++ /dev/null @@ -1,9 +0,0 @@ -// -// Copyright 2014-2020 Cristian Maglie. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -package serial - -//sys ioctl(fd int, req uint64, data uintptr) (err error) diff --git a/syscall_openbsd.go b/syscall_openbsd.go deleted file mode 100644 index 5ee0a2b..0000000 --- a/syscall_openbsd.go +++ /dev/null @@ -1,9 +0,0 @@ -// -// Copyright 2014-2020 Cristian Maglie. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -package serial - -//sys ioctl(fd int, req uint64, data uintptr) (err error) diff --git a/zsyscall_darwin.go b/zsyscall_darwin.go deleted file mode 100644 index 9fd8772..0000000 --- a/zsyscall_darwin.go +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright 2014-2020 Cristian Maglie. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -// This file is machine generated by the command: -// mksyscall.pl serial_darwin.go -// The generated stub is modified to make it compile under the "serial" package - -package serial - -import "golang.org/x/sys/unix" - -func ioctl(fd int, req uint64, data uintptr) (err error) { - _, _, e1 := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data)) - if e1 != 0 { - err = e1 - } - return -} diff --git a/zsyscall_freebsd.go b/zsyscall_freebsd.go deleted file mode 100644 index d35b1f3..0000000 --- a/zsyscall_freebsd.go +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright 2014-2020 Cristian Maglie. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -// This file is machine generated by the command: -// mksyscall.pl serial_freebsd.go -// The generated stub is modified to make it compile under the "serial" package - -package serial - -import "golang.org/x/sys/unix" - -func ioctl(fd int, req uint64, data uintptr) (err error) { - _, _, e1 := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data)) - if e1 != 0 { - err = e1 - } - return -} diff --git a/zsyscall_linux.go b/zsyscall_linux.go deleted file mode 100644 index 1054f29..0000000 --- a/zsyscall_linux.go +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright 2014-2020 Cristian Maglie. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -// This file is machine generated by the command: -// mksyscall.pl serial_linux.go -// The generated stub is modified to make it compile under the "serial" package - -package serial - -import "golang.org/x/sys/unix" - -func ioctl(fd int, req uint64, data uintptr) (err error) { - _, _, e1 := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data)) - if e1 != 0 { - err = e1 - } - return -} diff --git a/zsyscall_openbsd.go b/zsyscall_openbsd.go deleted file mode 100644 index 7c63d52..0000000 --- a/zsyscall_openbsd.go +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright 2014-2020 Cristian Maglie. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -// This file is machine generated by the command: -// mksyscall.pl serial_openbsd.go -// The generated stub is modified to make it compile under the "serial" package - -package serial - -import "syscall" - -func ioctl(fd int, req uint64, data uintptr) (err error) { - _, _, e1 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data)) - if e1 != 0 { - err = e1 - } - return -} From 00e63fcc18cc62eed600c46205e64625d0e6304c Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 11 May 2020 00:02:01 +0100 Subject: [PATCH 102/181] small cosmetic fix --- serial_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serial_windows.go b/serial_windows.go index e2c40f5..ec328f9 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -75,7 +75,6 @@ func (port *windowsPort) Close() error { func (port *windowsPort) Read(p []byte) (int, error) { var readed uint32 - params := &dcb{} ev, err := createOverlappedEvent() if err != nil { return 0, err @@ -107,6 +106,7 @@ func (port *windowsPort) Read(p []byte) (int, error) { // a serial port is alive in Windows is to check if the SetCommState // function fails. + params := &dcb{} getCommState(port.handle, params) if err := setCommState(port.handle, params); err != nil { port.Close() From 57b513a432c80124b080b60bc96992fb05cbb646 Mon Sep 17 00:00:00 2001 From: Tobias Kohlbau Date: Sun, 7 Feb 2021 08:32:41 +0100 Subject: [PATCH 103/181] add support for darwin arm64 Support arm64 which is used by the new lineup of computers from apple. Signed-off-by: Tobias Kohlbau --- serial_darwin_amd64.go => serial_darwin_64.go | 3 +++ 1 file changed, 3 insertions(+) rename serial_darwin_amd64.go => serial_darwin_64.go (96%) diff --git a/serial_darwin_amd64.go b/serial_darwin_64.go similarity index 96% rename from serial_darwin_amd64.go rename to serial_darwin_64.go index 34b8d7a..885f114 100644 --- a/serial_darwin_amd64.go +++ b/serial_darwin_64.go @@ -1,3 +1,6 @@ +// +build darwin +// +build amd64 arm64 + // // Copyright 2014-2020 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style From be77090773afeed3d4d8b9505bc66cc9c4e3cc59 Mon Sep 17 00:00:00 2001 From: Cliff Brake Date: Fri, 19 Feb 2021 14:24:33 -0500 Subject: [PATCH 104/181] update goselect to get riscv support --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 685d2bc..05bb3fa 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module go.bug.st/serial go 1.13 require ( - github.com/creack/goselect v0.1.1 + github.com/creack/goselect v0.1.2 github.com/stretchr/testify v1.4.0 golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 ) diff --git a/go.sum b/go.sum index 9f04ea9..0473d34 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/creack/goselect v0.1.1 h1:tiSSgKE1eJtxs1h/VgGQWuXUP0YS4CDIFMp6vaI1ls0= github.com/creack/goselect v0.1.1/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= +github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0= +github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= From e18766f4d123dc4a6a23a0e8ee0bb43eef3afed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Polomsk=C3=BD?= Date: Sat, 9 May 2020 08:40:06 +0200 Subject: [PATCH 105/181] update go generate Replace old mkwinsyscall call by the new version which is recommended by Go since version 1.13.3. --- serial.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serial.go b/serial.go index 1bf5bb9..d50e1f7 100644 --- a/serial.go +++ b/serial.go @@ -6,7 +6,7 @@ package serial -//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go +//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go syscall_windows.go // Port is the interface for a serial Port type Port interface { From b62cd2386c084db5d12d73bd5d3029b2a81a4af1 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 26 Feb 2021 13:07:44 +0100 Subject: [PATCH 106/181] Added support for macosx 10.11+ Seems like, starting from 10.11, IOUSBDevice has been renamed to IOUSBHostDevice. --- enumerator/usb_darwin.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/enumerator/usb_darwin.go b/enumerator/usb_darwin.go index aa8b2fc..76f6ea0 100644 --- a/enumerator/usb_darwin.go +++ b/enumerator/usb_darwin.go @@ -47,9 +47,13 @@ func extractPortInfo(service C.io_registry_entry_t) (*PortDetails, error) { port.Name = name port.IsUSB = false + validUSBDeviceClass := map[string]bool{ + "IOUSBDevice": true, + "IOUSBHostDevice": true, + } usbDevice := service var searchErr error - for usbDevice.GetClass() != "IOUSBDevice" { + for !validUSBDeviceClass[usbDevice.GetClass()] { if usbDevice, searchErr = usbDevice.GetParent("IOService"); searchErr != nil { break } From 233eb2a09bc26b2a3160e9b88639e586c36c7911 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 26 Feb 2021 13:20:02 +0100 Subject: [PATCH 107/181] Refresh run of go generate with the latest mksyscall --- enumerator/enumerator.go | 2 +- enumerator/syscall_windows.go | 39 ++++++++++++++++++++++++++++------- zsyscall_windows.go | 2 +- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/enumerator/enumerator.go b/enumerator/enumerator.go index 41f88fe..552330e 100644 --- a/enumerator/enumerator.go +++ b/enumerator/enumerator.go @@ -6,7 +6,7 @@ package enumerator -//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output syscall_windows.go usb_windows.go +//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output syscall_windows.go usb_windows.go // PortDetails contains detailed information about USB serial port. // Use GetDetailedPortsList function to retrieve it. diff --git a/enumerator/syscall_windows.go b/enumerator/syscall_windows.go index d969274..1dbcd9c 100644 --- a/enumerator/syscall_windows.go +++ b/enumerator/syscall_windows.go @@ -1,4 +1,4 @@ -// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT +// Code generated by 'go generate'; DO NOT EDIT. package enumerator @@ -11,6 +11,31 @@ import ( var _ unsafe.Pointer +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + var ( modsetupapi = windows.NewLazySystemDLL("setupapi.dll") @@ -36,7 +61,7 @@ func _setupDiClassGuidsFromNameInternal(class *uint16, guid *guid, guidSize uint r1, _, e1 := syscall.Syscall6(procSetupDiClassGuidsFromNameW.Addr(), 4, uintptr(unsafe.Pointer(class)), uintptr(unsafe.Pointer(guid)), uintptr(guidSize), uintptr(unsafe.Pointer(requiredSize)), 0, 0) if r1 == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -49,7 +74,7 @@ func setupDiGetClassDevs(guid *guid, enumerator *string, hwndParent uintptr, fla set = devicesSet(r0) if set == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -61,7 +86,7 @@ func setupDiDestroyDeviceInfoList(set devicesSet) (err error) { r1, _, e1 := syscall.Syscall(procSetupDiDestroyDeviceInfoList.Addr(), 1, uintptr(set), 0, 0) if r1 == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -73,7 +98,7 @@ func setupDiEnumDeviceInfo(set devicesSet, index uint32, info *devInfoData) (err r1, _, e1 := syscall.Syscall(procSetupDiEnumDeviceInfo.Addr(), 3, uintptr(set), uintptr(index), uintptr(unsafe.Pointer(info))) if r1 == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -85,7 +110,7 @@ func setupDiGetDeviceInstanceId(set devicesSet, devInfo *devInfoData, devInstanc r1, _, e1 := syscall.Syscall6(procSetupDiGetDeviceInstanceIdW.Addr(), 5, uintptr(set), uintptr(unsafe.Pointer(devInfo)), uintptr(devInstanceId), uintptr(devInstanceIdSize), uintptr(unsafe.Pointer(requiredSize)), 0) if r1 == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } @@ -98,7 +123,7 @@ func setupDiOpenDevRegKey(set devicesSet, devInfo *devInfoData, scope dicsScope, hkey = syscall.Handle(r0) if hkey == 0 { if e1 != 0 { - err = error(e1) + err = errnoErr(e1) } else { err = syscall.EINVAL } diff --git a/zsyscall_windows.go b/zsyscall_windows.go index f6005ea..9c42379 100644 --- a/zsyscall_windows.go +++ b/zsyscall_windows.go @@ -1,4 +1,4 @@ -// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT +// Code generated by 'go generate'; DO NOT EDIT. package serial From 7183520fbb06b6322ae6d46e367c9e6ee02ac9af Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 6 Jan 2021 18:00:47 +0000 Subject: [PATCH 108/181] run of go-fmt --- enumerator/usb_windows.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/enumerator/usb_windows.go b/enumerator/usb_windows.go index 7ebe854..24be4e1 100644 --- a/enumerator/usb_windows.go +++ b/enumerator/usb_windows.go @@ -102,7 +102,7 @@ const ( spdrpExclusive = 0x0000001A // Device is exclusive-access = R/W spdrpCharacteristics = 0x0000001B // Device Characteristics = R/W spdrpAddress = 0x0000001C // Device Address = R - spdrpUINumberDescFormat = 0X0000001D // UiNumberDescFormat = R/W + spdrpUINumberDescFormat = 0x0000001D // UiNumberDescFormat = R/W spdrpDevicePowerData = 0x0000001E // Device Power Data = R spdrpRemovalPolicy = 0x0000001F // Removal Policy = R spdrpRemovalPolicyHWDefault = 0x00000020 // Hardware Removal Policy = R @@ -295,10 +295,10 @@ func retrievePortDetailsFromDevInfo(device *deviceInfo, details *PortDetails) er while spdrpFriendlyName returns a specific name, e.g.: "CDC-ACM (COM44)", the result of spdrpFriendlyName is therefore unique and suitable as an alternative string to for a port choice */ n := uint32(0) - setupDiGetDeviceRegistryProperty(device.set, &device.data, /* spdrpDeviceDesc */ spdrpFriendlyName, nil, nil, 0, &n) + setupDiGetDeviceRegistryProperty(device.set, &device.data, spdrpFriendlyName /* spdrpDeviceDesc */, nil, nil, 0, &n) buff := make([]uint16, n*2) buffP := (*byte)(unsafe.Pointer(&buff[0])) - if setupDiGetDeviceRegistryProperty(device.set, &device.data, /* spdrpDeviceDesc */ spdrpFriendlyName, nil, buffP, n, &n) { + if setupDiGetDeviceRegistryProperty(device.set, &device.data, spdrpFriendlyName /* spdrpDeviceDesc */, nil, buffP, n, &n) { details.Product = syscall.UTF16ToString(buff[:]) } From 57ac200f9842a9784ce24ddcc34ff0ad0a59851e Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 6 Jan 2021 23:38:12 +0000 Subject: [PATCH 109/181] windows: improved serial number detection on composite USB devices --- enumerator/syscall_windows.go | 29 +++++++++++++++++++ enumerator/usb_windows.go | 53 ++++++++++++++++++++++++++++++++++- 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/enumerator/syscall_windows.go b/enumerator/syscall_windows.go index 1dbcd9c..2c6b379 100644 --- a/enumerator/syscall_windows.go +++ b/enumerator/syscall_windows.go @@ -38,6 +38,7 @@ func errnoErr(e syscall.Errno) error { var ( modsetupapi = windows.NewLazySystemDLL("setupapi.dll") + modcfgmgr32 = windows.NewLazySystemDLL("cfgmgr32.dll") procSetupDiClassGuidsFromNameW = modsetupapi.NewProc("SetupDiClassGuidsFromNameW") procSetupDiGetClassDevsW = modsetupapi.NewProc("SetupDiGetClassDevsW") @@ -46,6 +47,10 @@ var ( procSetupDiGetDeviceInstanceIdW = modsetupapi.NewProc("SetupDiGetDeviceInstanceIdW") procSetupDiOpenDevRegKey = modsetupapi.NewProc("SetupDiOpenDevRegKey") procSetupDiGetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiGetDeviceRegistryPropertyW") + procCM_Get_Parent = modcfgmgr32.NewProc("CM_Get_Parent") + procCM_Get_Device_ID_Size = modcfgmgr32.NewProc("CM_Get_Device_ID_Size") + procCM_Get_Device_IDW = modcfgmgr32.NewProc("CM_Get_Device_IDW") + procCM_MapCrToWin32Err = modcfgmgr32.NewProc("CM_MapCrToWin32Err") ) func setupDiClassGuidsFromNameInternal(class string, guid *guid, guidSize uint32, requiredSize *uint32) (err error) { @@ -136,3 +141,27 @@ func setupDiGetDeviceRegistryProperty(set devicesSet, devInfo *devInfoData, prop res = r0 != 0 return } + +func cmGetParent(outParentDev *devInstance, dev devInstance, flags uint32) (cmErr cmError) { + r0, _, _ := syscall.Syscall(procCM_Get_Parent.Addr(), 3, uintptr(unsafe.Pointer(outParentDev)), uintptr(dev), uintptr(flags)) + cmErr = cmError(r0) + return +} + +func cmGetDeviceIDSize(outLen *uint32, dev devInstance, flags uint32) (cmErr cmError) { + r0, _, _ := syscall.Syscall(procCM_Get_Device_ID_Size.Addr(), 3, uintptr(unsafe.Pointer(outLen)), uintptr(dev), uintptr(flags)) + cmErr = cmError(r0) + return +} + +func cmGetDeviceID(dev devInstance, buffer unsafe.Pointer, bufferSize uint32, flags uint32) (err cmError) { + r0, _, _ := syscall.Syscall6(procCM_Get_Device_IDW.Addr(), 4, uintptr(dev), uintptr(buffer), uintptr(bufferSize), uintptr(flags), 0, 0) + err = cmError(r0) + return +} + +func cmMapCrToWin32Err(cmErr cmError, defaultErr uint32) (err uint32) { + r0, _, _ := syscall.Syscall(procCM_MapCrToWin32Err.Addr(), 2, uintptr(cmErr), uintptr(defaultErr), 0) + err = uint32(r0) + return +} diff --git a/enumerator/usb_windows.go b/enumerator/usb_windows.go index 24be4e1..f8e86a5 100644 --- a/enumerator/usb_windows.go +++ b/enumerator/usb_windows.go @@ -62,6 +62,11 @@ func parseDeviceID(deviceID string, details *PortDetails) { //sys setupDiOpenDevRegKey(set devicesSet, devInfo *devInfoData, scope dicsScope, hwProfile uint32, keyType uint32, samDesired regsam) (hkey syscall.Handle, err error) = setupapi.SetupDiOpenDevRegKey //sys setupDiGetDeviceRegistryProperty(set devicesSet, devInfo *devInfoData, property deviceProperty, propertyType *uint32, outValue *byte, bufSize uint32, reqSize *uint32) (res bool) = setupapi.SetupDiGetDeviceRegistryPropertyW +//sys cmGetParent(outParentDev *devInstance, dev devInstance, flags uint32) (cmErr cmError) = cfgmgr32.CM_Get_Parent +//sys cmGetDeviceIDSize(outLen *uint32, dev devInstance, flags uint32) (cmErr cmError) = cfgmgr32.CM_Get_Device_ID_Size +//sys cmGetDeviceID(dev devInstance, buffer unsafe.Pointer, bufferSize uint32, flags uint32) (err cmError) = cfgmgr32.CM_Get_Device_IDW +//sys cmMapCrToWin32Err(cmErr cmError, defaultErr uint32) (err uint32) = cfgmgr32.CM_MapCrToWin32Err + // Device registry property codes // (Codes marked as read-only (R) may only be used for // SetupDiGetDeviceRegistryProperty) @@ -194,14 +199,46 @@ func (set devicesSet) destroy() { setupDiDestroyDeviceInfoList(set) } +type cmError uint32 + // https://msdn.microsoft.com/en-us/library/windows/hardware/ff552344(v=vs.85).aspx type devInfoData struct { size uint32 guid guid - devInst uint32 + devInst devInstance reserved uintptr } +type devInstance uint32 + +func cmConvertError(cmErr cmError) error { + if cmErr == 0 { + return nil + } + winErr := cmMapCrToWin32Err(cmErr, 0) + return fmt.Errorf("error %d", winErr) +} + +func (dev devInstance) getParent() (devInstance, error) { + var res devInstance + errN := cmGetParent(&res, dev, 0) + return res, cmConvertError(errN) +} + +func (dev devInstance) GetDeviceID() (string, error) { + var size uint32 + cmErr := cmGetDeviceIDSize(&size, dev, 0) + if err := cmConvertError(cmErr); err != nil { + return "", err + } + buff := make([]uint16, size) + cmErr = cmGetDeviceID(dev, unsafe.Pointer(&buff[0]), uint32(len(buff)), 0) + if err := cmConvertError(cmErr); err != nil { + return "", err + } + return windows.UTF16ToString(buff[:]), nil +} + type deviceInfo struct { set devicesSet data devInfoData @@ -291,6 +328,20 @@ func retrievePortDetailsFromDevInfo(device *deviceInfo, details *PortDetails) er } parseDeviceID(deviceID, details) + // On composite USB devices the serial number is usually reported on the parent + // device, so let's navigate up one level and see if we can get this information + if details.IsUSB && details.SerialNumber == "" { + if parentInfo, err := device.data.devInst.getParent(); err == nil { + if parentDeviceID, err := parentInfo.GetDeviceID(); err == nil { + d := &PortDetails{} + parseDeviceID(parentDeviceID, d) + if details.VID == d.VID && details.PID == d.PID { + details.SerialNumber = d.SerialNumber + } + } + } + } + /* spdrpDeviceDesc returns a generic name, e.g.: "CDC-ACM", which will be the same for 2 identical devices attached while spdrpFriendlyName returns a specific name, e.g.: "CDC-ACM (COM44)", the result of spdrpFriendlyName is therefore unique and suitable as an alternative string to for a port choice */ From f355dbf6081e7f1e8b12d832608eea18a91ef302 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 12 Mar 2017 22:44:28 +0200 Subject: [PATCH 110/181] windows: correcly signal PortClosed error on blocked Read() This commit fix regression test TestConcurrentReadAndWrite. Even if the test checks purpose is another it has detected anyway that the returned error is incorrect. Another specific test for closing port detection will be added in the future. --- serial_windows.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/serial_windows.go b/serial_windows.go index ec328f9..46803c6 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -82,14 +82,15 @@ func (port *windowsPort) Read(p []byte) (int, error) { defer syscall.CloseHandle(ev.HEvent) for { err := syscall.ReadFile(port.handle, p, &readed, ev) + if err == syscall.ERROR_IO_PENDING { + err = getOverlappedResult(port.handle, ev, &readed, true) + } switch err { case nil: // operation completed successfully - case syscall.ERROR_IO_PENDING: - // wait for overlapped I/O to complete - if err := getOverlappedResult(port.handle, ev, &readed, true); err != nil { - return int(readed), err - } + case syscall.ERROR_OPERATION_ABORTED: + // port may have been closed + return int(readed), &PortError{code: PortClosed, causedBy: err} default: // error happened return int(readed), err From a63b28875ff5b7f52b2c41992656afac36c66a34 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 27 Jul 2017 13:53:59 +0200 Subject: [PATCH 111/181] linux: detect port disconnection during Read See https://stackoverflow.com/a/34945814/1655275 > pselect signals that file descriptor is ready and > ioctl(fd, FIONREAD, &len) returns zero len. Failure to detect this condition has been revealed by the testsuite: === RUN TestDisconnectingPortDetection 2017/07/27 13:56:37 PR - Connecting to Probe 2017/07/27 13:56:37 > Searching for port 2341:8037 2017/07/27 13:56:37 Detected port '/dev/ttyACM0' 2341:8037 2017/07/27 13:56:37 Using '/dev/ttyACM0' 2017/07/27 13:56:37 Starting test (timeout 20s) 2017/07/27 13:56:37 PR - Turn ON target 2017/07/27 13:56:37 TR - Connecting to Target 2017/07/27 13:56:37 > Searching for port 2341:8036 2017/07/27 13:56:37 Detected port '/dev/ttyACM0' 2341:8037 [...cut...] 2017/07/27 13:56:46 > Searching for port 2341:8036 2017/07/27 13:56:46 Detected port '/dev/ttyACM0' 2341:8037 2017/07/27 13:56:46 Detected port '/dev/ttyACM1' 2341:8036 2017/07/27 13:56:46 Using '/dev/ttyACM1' 2017/07/27 13:56:46 T2 - Make a Read call 2017/07/27 13:56:46 T1 - Delay 200ms before disconnecting target 2017/07/27 13:56:46 T1 - Disconnect target 2017/07/27 13:56:46 PR - Turn OFF target 2017/07/27 13:56:46 T2 - Read returned: n=0 err=nil --- FAIL: TestDisconnectingPortDetection (9.18s) Error Trace: serial_test.go:100 Error: An error is expected but got nil. %s Messages: Read returned no errors this commit fix the problem above. --- serial_unix.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/serial_unix.go b/serial_unix.go index f9e887c..c921389 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -77,6 +77,12 @@ func (port *unixPort) Read(p []byte) (int, error) { if err == unix.EINTR { continue } + // Linux: when the port is disconnected during a read operation + // the port is left in a "readable with zero-length-data" state. + // https://stackoverflow.com/a/34945814/1655275 + if n == 0 && err == nil { + return 0, &PortError{code: PortClosed} + } if n < 0 { // Do not return -1 unix errors n = 0 } From 2cb14f049cbdf8b8852253a1a8faf2c24699636d Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 10 May 2020 23:42:16 +0200 Subject: [PATCH 112/181] SetReadTimeout: unix implementation --- serial.go | 13 +++++++++++++ serial_unix.go | 30 +++++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/serial.go b/serial.go index d50e1f7..08f227a 100644 --- a/serial.go +++ b/serial.go @@ -6,6 +6,8 @@ package serial +import "time" + //go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go syscall_windows.go // Port is the interface for a serial Port @@ -40,10 +42,17 @@ type Port interface { // modem status bits for the serial port (CTS, DSR, etc...) GetModemStatusBits() (*ModemStatusBits, error) + // SetReadTimeout sets the timeout for the Read operation or use serial.NoTimeout + // to disable read timeout. + SetReadTimeout(t time.Duration) error + // Close the serial port Close() error } +// NoTimeout should be used as a parameter to SetReadTimeout to disable timeout. +var NoTimeout time.Duration = -1 + // ModemStatusBits contains all the modem status bits for a serial port (CTS, DSR, etc...). // It can be retrieved with the Port.GetModemStatusBits() method. type ModemStatusBits struct { @@ -125,6 +134,8 @@ const ( InvalidParity // InvalidStopBits the selected number of stop bits is not valid or not supported InvalidStopBits + // InvalidTimeoutValue the timeout value is not valid or not supported + InvalidTimeoutValue // ErrorEnumeratingPorts an error occurred while listing serial port ErrorEnumeratingPorts // PortClosed the port has been closed while the operation is in progress @@ -152,6 +163,8 @@ func (e PortError) EncodedErrorString() string { return "Port parity invalid or not supported" case InvalidStopBits: return "Port stop bits invalid or not supported" + case InvalidTimeoutValue: + return "Timeout value invalid or not supported" case ErrorEnumeratingPorts: return "Could not enumerate serial ports" case PortClosed: diff --git a/serial_unix.go b/serial_unix.go index c921389..0143aa2 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -14,6 +14,7 @@ import ( "strings" "sync" "sync/atomic" + "time" "go.bug.st/serial/unixutils" "golang.org/x/sys/unix" @@ -22,6 +23,7 @@ import ( type unixPort struct { handle int + readTimeout time.Duration closeLock sync.RWMutex closeSignal *unixutils.Pipe opened uint32 @@ -61,9 +63,18 @@ func (port *unixPort) Read(p []byte) (int, error) { return 0, &PortError{code: PortClosed} } + var deadline time.Time + if port.readTimeout != NoTimeout { + deadline = time.Now().Add(port.readTimeout) + } + fds := unixutils.NewFDSet(port.handle, port.closeSignal.ReadFD()) for { - res, err := unixutils.Select(fds, nil, fds, -1) + timeout := time.Duration(-1) + if port.readTimeout != NoTimeout { + timeout = deadline.Sub(time.Now()) + } + res, err := unixutils.Select(fds, nil, fds, timeout) if err == unix.EINTR { continue } @@ -73,6 +84,10 @@ func (port *unixPort) Read(p []byte) (int, error) { if res.IsReadable(port.closeSignal.ReadFD()) { return 0, &PortError{code: PortClosed} } + if !res.IsReadable(port.handle) { + // Timeout happened + return 0, nil + } n, err := unix.Read(port.handle, p) if err == unix.EINTR { continue @@ -152,6 +167,14 @@ func (port *unixPort) SetRTS(rts bool) error { return port.setModemBitsStatus(status) } +func (port *unixPort) SetReadTimeout(timeout time.Duration) error { + if timeout < 0 && timeout != NoTimeout { + return &PortError{code: InvalidTimeoutValue} + } + port.readTimeout = timeout + return nil +} + func (port *unixPort) GetModemStatusBits() (*ModemStatusBits, error) { status, err := port.getModemBitsStatus() if err != nil { @@ -177,8 +200,9 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { return nil, err } port := &unixPort{ - handle: h, - opened: 1, + handle: h, + opened: 1, + readTimeout: NoTimeout, } // Setup serial port From 23dbc8b9ba7d733ce0f3902715bdf61891790de6 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 11 May 2020 00:01:36 +0100 Subject: [PATCH 113/181] SetReadTimeout: windows implementation --- serial_windows.go | 54 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/serial_windows.go b/serial_windows.go index 46803c6..6b6d0ee 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -18,13 +18,15 @@ package serial */ import ( - "syscall" "sync" + "syscall" + "time" ) type windowsPort struct { - mu sync.Mutex - handle syscall.Handle + mu sync.Mutex + handle syscall.Handle + readTimeoutCycles int64 } func nativeGetPortsList() ([]string, error) { @@ -80,6 +82,8 @@ func (port *windowsPort) Read(p []byte) (int, error) { return 0, err } defer syscall.CloseHandle(ev.HEvent) + + cycles := int64(0) for { err := syscall.ReadFile(port.handle, p, &readed, ev) if err == syscall.ERROR_IO_PENDING { @@ -103,6 +107,14 @@ func (port *windowsPort) Read(p []byte) (int, error) { return 0, err } + if port.readTimeoutCycles != -1 { + cycles++ + if cycles == port.readTimeoutCycles { + // Timeout + return 0, nil + } + } + // At the moment it seems that the only reliable way to check if // a serial port is alive in Windows is to check if the SetCommState // function fails. @@ -370,6 +382,31 @@ func (port *windowsPort) GetModemStatusBits() (*ModemStatusBits, error) { }, nil } +func (port *windowsPort) SetReadTimeout(timeout time.Duration) error { + var cycles, cycleDuration int64 + if timeout == NoTimeout { + cycles = -1 + cycleDuration = 1000 + } else { + cycles = timeout.Milliseconds()/1000 + 1 + cycleDuration = timeout.Milliseconds() / cycles + } + + err := setCommTimeouts(port.handle, &commTimeouts{ + ReadIntervalTimeout: 0xFFFFFFFF, + ReadTotalTimeoutMultiplier: 0xFFFFFFFF, + ReadTotalTimeoutConstant: uint32(cycleDuration), + WriteTotalTimeoutConstant: 0, + WriteTotalTimeoutMultiplier: 0, + }) + if err != nil { + return &PortError{code: InvalidTimeoutValue, causedBy: err} + } + port.readTimeoutCycles = cycles + + return nil +} + func createOverlappedEvent() (*syscall.Overlapped, error) { h, err := createEvent(nil, true, false, nil) return &syscall.Overlapped{HEvent: h}, err @@ -435,18 +472,9 @@ func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { return nil, &PortError{code: InvalidSerialPort} } - // Set timeouts to 1 second - timeouts := &commTimeouts{ - ReadIntervalTimeout: 0xFFFFFFFF, - ReadTotalTimeoutMultiplier: 0xFFFFFFFF, - ReadTotalTimeoutConstant: 1000, // 1 sec - WriteTotalTimeoutConstant: 0, - WriteTotalTimeoutMultiplier: 0, - } - if setCommTimeouts(port.handle, timeouts) != nil { + if port.SetReadTimeout(NoTimeout) != nil { port.Close() return nil, &PortError{code: InvalidSerialPort} } - return port, nil } From 25a9426221426a8c434214749d3dd9e409c7df5e Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 29 Jun 2021 16:40:56 +0200 Subject: [PATCH 114/181] Move regex compile outside loop --- serial_unix.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/serial_unix.go b/serial_unix.go index f9e887c..e4dad89 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -220,6 +220,10 @@ func nativeGetPortsList() ([]string, error) { } ports := make([]string, 0, len(files)) + regex, err := regexp.Compile(regexFilter) + if err != nil { + return nil, err + } for _, f := range files { // Skip folders if f.IsDir() { @@ -227,11 +231,7 @@ func nativeGetPortsList() ([]string, error) { } // Keep only devices with the correct name - match, err := regexp.MatchString(regexFilter, f.Name()) - if err != nil { - return nil, err - } - if !match { + if !regex.MatchString(f.Name()) { continue } From cff9b2347bb476de6a8c6551f4c381b0d7d6d82f Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 1 Jul 2021 00:00:16 +0200 Subject: [PATCH 115/181] Allow setting custom baudrates on serial ports (mac and linux version) Co-authored-by: Jan-Philipp Benecke --- serial_darwin.go | 15 +++++++++++++++ serial_linux.go | 28 ++++++++++++++++++++++++++++ serial_unix.go | 35 +++++++++++++++-------------------- 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/serial_darwin.go b/serial_darwin.go index 7bce6c9..8b27a82 100644 --- a/serial_darwin.go +++ b/serial_darwin.go @@ -14,3 +14,18 @@ const regexFilter = "^(cu|tty)\\..*" const ioctlTcgetattr = unix.TIOCGETA const ioctlTcsetattr = unix.TIOCSETA const ioctlTcflsh = unix.TIOCFLUSH + +func setTermSettingsBaudrate(speed int, settings *unix.Termios) (error, bool) { + baudrate, ok := baudrateMap[speed] + if !ok { + return nil, true + } + settings.Ispeed = toTermiosSpeedType(baudrate) + settings.Ospeed = toTermiosSpeedType(baudrate) + return nil, false +} + +func (port *unixPort) setSpecialBaudrate(speed uint32) error { + const kIOSSIOSPEED = 0x80045402 + return unix.IoctlSetPointerInt(port.handle, kIOSSIOSPEED, int(speed)) +} diff --git a/serial_linux.go b/serial_linux.go index 1c8fd8d..eab3803 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -67,3 +67,31 @@ const ioctlTcflsh = unix.TCFLSH func toTermiosSpeedType(speed uint32) uint32 { return speed } + +func setTermSettingsBaudrate(speed int, settings *unix.Termios) (error, bool) { + baudrate, ok := baudrateMap[speed] + if !ok { + return nil, true + } + // revert old baudrate + for _, rate := range baudrateMap { + settings.Cflag &^= rate + } + // set new baudrate + settings.Cflag |= baudrate + settings.Ispeed = toTermiosSpeedType(baudrate) + settings.Ospeed = toTermiosSpeedType(baudrate) + return nil, false +} + +func (port *unixPort) setSpecialBaudrate(speed uint32) error { + settings, err := unix.IoctlGetTermios(port.handle, unix.TCGETS2) + if err != nil { + return err + } + settings.Cflag &^= unix.CBAUD + settings.Cflag |= unix.BOTHER + settings.Ispeed = speed + settings.Ospeed = speed + return unix.IoctlSetTermios(port.handle, unix.TCSETS2, settings) +} diff --git a/serial_unix.go b/serial_unix.go index 0143aa2..a4084c5 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -126,9 +126,6 @@ func (port *unixPort) SetMode(mode *Mode) error { if err != nil { return err } - if err := setTermSettingsBaudrate(mode.BaudRate, settings); err != nil { - return err - } if err := setTermSettingsParity(mode.Parity, settings); err != nil { return err } @@ -138,7 +135,21 @@ func (port *unixPort) SetMode(mode *Mode) error { if err := setTermSettingsStopBits(mode.StopBits, settings); err != nil { return err } - return port.setTermSettings(settings) + requireSpecialBaudrate := false + if err, special := setTermSettingsBaudrate(mode.BaudRate, settings); err != nil { + return err + } else if special { + requireSpecialBaudrate = true + } + if err := port.setTermSettings(settings); err != nil { + return err + } + if requireSpecialBaudrate { + if err := port.setSpecialBaudrate(uint32(mode.BaudRate)); err != nil { + return err + } + } + return nil } func (port *unixPort) SetDTR(dtr bool) error { @@ -289,22 +300,6 @@ func nativeGetPortsList() ([]string, error) { // termios manipulation functions -func setTermSettingsBaudrate(speed int, settings *unix.Termios) error { - baudrate, ok := baudrateMap[speed] - if !ok { - return &PortError{code: InvalidSpeed} - } - // revert old baudrate - for _, rate := range baudrateMap { - settings.Cflag &^= rate - } - // set new baudrate - settings.Cflag |= baudrate - settings.Ispeed = toTermiosSpeedType(baudrate) - settings.Ospeed = toTermiosSpeedType(baudrate) - return nil -} - func setTermSettingsParity(parity Parity, settings *unix.Termios) error { switch parity { case NoParity: From 8ed18bb9977e267f46f56f0eacd6761fa7562853 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 1 Jul 2021 00:19:22 +0200 Subject: [PATCH 116/181] Fixed custom baud rate settings for MacOSX --- serial_unix.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/serial_unix.go b/serial_unix.go index a4084c5..143818e 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -145,6 +145,8 @@ func (port *unixPort) SetMode(mode *Mode) error { return err } if requireSpecialBaudrate { + // MacOSX require this one to be the last operation otherwise an + // 'Invalid serial port' error is produced. if err := port.setSpecialBaudrate(uint32(mode.BaudRate)); err != nil { return err } @@ -217,11 +219,6 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { } // Setup serial port - if port.SetMode(mode) != nil { - port.Close() - return nil, &PortError{code: InvalidSerialPort} - } - settings, err := port.getTermSettings() if err != nil { port.Close() @@ -239,6 +236,13 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { return nil, &PortError{code: InvalidSerialPort} } + // MacOSX require that this operation is the last one otherwise an + // 'Invalid serial port' error is returned... don't know why... + if port.SetMode(mode) != nil { + port.Close() + return nil, &PortError{code: InvalidSerialPort} + } + unix.SetNonblock(h, false) port.acquireExclusiveAccess() From 9d74cb081f09348d86bd21e1f5d69bd8e7846b37 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 1 Jul 2021 00:48:39 +0200 Subject: [PATCH 117/181] Added *bsd placeholders --- serial_freebsd.go | 23 +++++++++++++++++++++++ serial_openbsd.go | 23 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/serial_freebsd.go b/serial_freebsd.go index a56a4bf..b648b52 100644 --- a/serial_freebsd.go +++ b/serial_freebsd.go @@ -60,3 +60,26 @@ const ioctlTcflsh = unix.TIOCFLUSH func toTermiosSpeedType(speed uint32) uint32 { return speed } + +func setTermSettingsBaudrate(speed int, settings *unix.Termios) (error, bool) { + baudrate, ok := baudrateMap[speed] + if !ok { + return nil, true + } + // XXX: Is Cflag really needed + // revert old baudrate + for _, rate := range baudrateMap { + settings.Cflag &^= rate + } + // set new baudrate + settings.Cflag |= baudrate + + settings.Ispeed = toTermiosSpeedType(baudrate) + settings.Ospeed = toTermiosSpeedType(baudrate) + return nil, false +} + +func (port *unixPort) setSpecialBaudrate(speed uint32) error { + // TODO: unimplemented + return &PortError{code: InvalidSpeed} +} diff --git a/serial_openbsd.go b/serial_openbsd.go index 5f626f3..ad68957 100644 --- a/serial_openbsd.go +++ b/serial_openbsd.go @@ -60,3 +60,26 @@ const ioctlTcflsh = unix.TIOCFLUSH func toTermiosSpeedType(speed uint32) int32 { return int32(speed) } + +func setTermSettingsBaudrate(speed int, settings *unix.Termios) (error, bool) { + baudrate, ok := baudrateMap[speed] + if !ok { + return nil, true + } + // XXX: Is Cflag really needed + // revert old baudrate + for _, rate := range baudrateMap { + settings.Cflag &^= rate + } + // set new baudrate + settings.Cflag |= baudrate + + settings.Ispeed = toTermiosSpeedType(baudrate) + settings.Ospeed = toTermiosSpeedType(baudrate) + return nil, false +} + +func (port *unixPort) setSpecialBaudrate(speed uint32) error { + // TODO: unimplemented + return &PortError{code: InvalidSpeed} +} From 0213833089b847b728117beb756ed2b1e066843e Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 13 Jul 2021 13:27:10 +0200 Subject: [PATCH 118/181] Fixed build for linux/ppc64le --- .github/workflows/test.yaml | 1 + serial_linux.go | 12 ------------ serial_specialbaudrate_linux.go | 24 ++++++++++++++++++++++++ serial_specialbaudrate_linux_ppc64le.go | 12 ++++++++++++ 4 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 serial_specialbaudrate_linux.go create mode 100644 serial_specialbaudrate_linux_ppc64le.go diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index ff195d1..94707b7 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -46,6 +46,7 @@ jobs: - "openbsd amd64" - "openbsd 386" - "openbsd arm" + - "linux ppc64le" runs-on: "ubuntu-latest" diff --git a/serial_linux.go b/serial_linux.go index eab3803..1a447d8 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -83,15 +83,3 @@ func setTermSettingsBaudrate(speed int, settings *unix.Termios) (error, bool) { settings.Ospeed = toTermiosSpeedType(baudrate) return nil, false } - -func (port *unixPort) setSpecialBaudrate(speed uint32) error { - settings, err := unix.IoctlGetTermios(port.handle, unix.TCGETS2) - if err != nil { - return err - } - settings.Cflag &^= unix.CBAUD - settings.Cflag |= unix.BOTHER - settings.Ispeed = speed - settings.Ospeed = speed - return unix.IoctlSetTermios(port.handle, unix.TCSETS2, settings) -} diff --git a/serial_specialbaudrate_linux.go b/serial_specialbaudrate_linux.go new file mode 100644 index 0000000..d6baffb --- /dev/null +++ b/serial_specialbaudrate_linux.go @@ -0,0 +1,24 @@ +// +build linux +// +build !ppc64le + +// +// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +package serial + +import "golang.org/x/sys/unix" + +func (port *unixPort) setSpecialBaudrate(speed uint32) error { + settings, err := unix.IoctlGetTermios(port.handle, unix.TCGETS2) + if err != nil { + return err + } + settings.Cflag &^= unix.CBAUD + settings.Cflag |= unix.BOTHER + settings.Ispeed = speed + settings.Ospeed = speed + return unix.IoctlSetTermios(port.handle, unix.TCSETS2, settings) +} diff --git a/serial_specialbaudrate_linux_ppc64le.go b/serial_specialbaudrate_linux_ppc64le.go new file mode 100644 index 0000000..4db8cc7 --- /dev/null +++ b/serial_specialbaudrate_linux_ppc64le.go @@ -0,0 +1,12 @@ +// +// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +package serial + +func (port *unixPort) setSpecialBaudrate(speed uint32) error { + // TODO: unimplemented + return &PortError{code: InvalidSpeed} +} From 9cebdc6aa3036d196e0732194475c766b354ab89 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 20 Jul 2021 12:54:16 +0200 Subject: [PATCH 119/181] Small update to docs --- enumerator/doc.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/enumerator/doc.go b/enumerator/doc.go index c3fc1cd..c2329bf 100644 --- a/enumerator/doc.go +++ b/enumerator/doc.go @@ -7,15 +7,9 @@ /* Package enumerator is a golang cross-platform library for USB serial port discovery. -WARNING: this library is still beta-testing code! please consider the library -and the API as *unstable*. Beware that, even if at this point it's unlike to -happen, the API may be subject to change until this notice is removed from -the documentation. - This library has been tested on Linux, Windows and Mac and uses specific OS services to enumerate USB PID/VID, in particular on MacOSX the use of cgo is required in order to access the IOKit Framework. This means that the library -cannot be easily cross compiled for GOOS=darwing targets. - +cannot be easily cross compiled for darwin/* targets. */ package enumerator From adc54fb194a0b4327132ea44610a8e731bae4a3b Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 20 Jul 2021 12:56:48 +0200 Subject: [PATCH 120/181] Updated license years --- LICENSE | 2 +- doc.go | 2 +- enumerator/doc.go | 2 +- enumerator/enumerator.go | 2 +- enumerator/example_getdetailedportlist_test.go | 11 +++++++---- enumerator/usb_darwin.go | 2 +- enumerator/usb_darwin_go1.9.go | 2 +- enumerator/usb_freebsd.go | 2 +- enumerator/usb_linux.go | 2 +- enumerator/usb_ole_windows.go | 2 +- enumerator/usb_openbsd.go | 2 +- enumerator/usb_windows.go | 2 +- enumerator/usb_windows_test.go | 2 +- example_getportlist_test.go | 11 +++++++---- example_modem_bits_test.go | 13 ++++++++----- example_serialport_test.go | 11 +++++++---- example_test.go | 7 ++++--- portlist/portlist.go | 2 +- serial.go | 2 +- serial_darwin.go | 2 +- serial_darwin_386.go | 2 +- serial_darwin_64.go | 2 +- serial_freebsd.go | 2 +- serial_linux.go | 2 +- serial_linux_test.go | 2 +- serial_openbsd.go | 2 +- serial_unix.go | 2 +- serial_windows.go | 2 +- syscall_windows.go | 3 +-- unixutils/pipe.go | 8 +++++--- unixutils/select.go | 2 +- 31 files changed, 63 insertions(+), 49 deletions(-) diff --git a/LICENSE b/LICENSE index 1b8b73b..50ee44b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ -Copyright (c) 2014-2020, Cristian Maglie. +Copyright (c) 2014-2021, Cristian Maglie. All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/doc.go b/doc.go index e54b87e..9705eb0 100644 --- a/doc.go +++ b/doc.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/doc.go b/enumerator/doc.go index c2329bf..818180d 100644 --- a/enumerator/doc.go +++ b/enumerator/doc.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/enumerator.go b/enumerator/enumerator.go index 552330e..6f0f32d 100644 --- a/enumerator/enumerator.go +++ b/enumerator/enumerator.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/example_getdetailedportlist_test.go b/enumerator/example_getdetailedportlist_test.go index 5b94e22..0d3bae4 100644 --- a/enumerator/example_getdetailedportlist_test.go +++ b/enumerator/example_getdetailedportlist_test.go @@ -1,14 +1,17 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package enumerator_test -import "fmt" -import "log" -import "go.bug.st/serial/enumerator" +import ( + "fmt" + "log" + + "go.bug.st/serial/enumerator" +) func ExampleGetDetailedPortsList() { ports, err := enumerator.GetDetailedPortsList() diff --git a/enumerator/usb_darwin.go b/enumerator/usb_darwin.go index 76f6ea0..fdf68c9 100644 --- a/enumerator/usb_darwin.go +++ b/enumerator/usb_darwin.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_darwin_go1.9.go b/enumerator/usb_darwin_go1.9.go index 3ec911d..167c44b 100644 --- a/enumerator/usb_darwin_go1.9.go +++ b/enumerator/usb_darwin_go1.9.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_freebsd.go b/enumerator/usb_freebsd.go index 3a43a85..739e222 100644 --- a/enumerator/usb_freebsd.go +++ b/enumerator/usb_freebsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_linux.go b/enumerator/usb_linux.go index 6133c63..bad2323 100644 --- a/enumerator/usb_linux.go +++ b/enumerator/usb_linux.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_ole_windows.go b/enumerator/usb_ole_windows.go index 9df6cdf..c02356a 100644 --- a/enumerator/usb_ole_windows.go +++ b/enumerator/usb_ole_windows.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Lars Knudsen, Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Lars Knudsen, Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_openbsd.go b/enumerator/usb_openbsd.go index 3a43a85..739e222 100644 --- a/enumerator/usb_openbsd.go +++ b/enumerator/usb_openbsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_windows.go b/enumerator/usb_windows.go index f8e86a5..b69e115 100644 --- a/enumerator/usb_windows.go +++ b/enumerator/usb_windows.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_windows_test.go b/enumerator/usb_windows_test.go index 94016fd..df5223a 100644 --- a/enumerator/usb_windows_test.go +++ b/enumerator/usb_windows_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/example_getportlist_test.go b/example_getportlist_test.go index 54b2dc3..fe3bf61 100644 --- a/example_getportlist_test.go +++ b/example_getportlist_test.go @@ -1,14 +1,17 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package serial_test -import "fmt" -import "log" -import "go.bug.st/serial" +import ( + "fmt" + "log" + + "go.bug.st/serial" +) func ExampleGetPortsList() { ports, err := serial.GetPortsList() diff --git a/example_modem_bits_test.go b/example_modem_bits_test.go index a10d828..298271e 100644 --- a/example_modem_bits_test.go +++ b/example_modem_bits_test.go @@ -1,15 +1,18 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package serial_test -import "log" -import "go.bug.st/serial" -import "fmt" -import "time" +import ( + "fmt" + "log" + "time" + + "go.bug.st/serial" +) func ExampleGetSetModemBits() { // Open the first serial port detected at 9600bps N81 diff --git a/example_serialport_test.go b/example_serialport_test.go index 91b8c79..544cd42 100644 --- a/example_serialport_test.go +++ b/example_serialport_test.go @@ -1,14 +1,17 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package serial_test -import "fmt" -import "log" -import "go.bug.st/serial" +import ( + "fmt" + "log" + + "go.bug.st/serial" +) func ExampleSerialPort_SetMode() { port, err := serial.Open("/dev/ttyACM0", &serial.Mode{}) diff --git a/example_test.go b/example_test.go index 5447199..63866bb 100644 --- a/example_test.go +++ b/example_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // @@ -8,10 +8,11 @@ package serial_test import ( "fmt" + "log" "strings" + + "go.bug.st/serial" ) -import "log" -import "go.bug.st/serial" // This example prints the list of serial ports and use the first one // to send a string "10,20,30" and prints the response on the screen. diff --git a/portlist/portlist.go b/portlist/portlist.go index cbf4c8c..a198b81 100644 --- a/portlist/portlist.go +++ b/portlist/portlist.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial.go b/serial.go index 08f227a..3e4f3b1 100644 --- a/serial.go +++ b/serial.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_darwin.go b/serial_darwin.go index 8b27a82..ea1b99f 100644 --- a/serial_darwin.go +++ b/serial_darwin.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_darwin_386.go b/serial_darwin_386.go index fd637df..850f6b7 100644 --- a/serial_darwin_386.go +++ b/serial_darwin_386.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_darwin_64.go b/serial_darwin_64.go index 885f114..7f72e66 100644 --- a/serial_darwin_64.go +++ b/serial_darwin_64.go @@ -2,7 +2,7 @@ // +build amd64 arm64 // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_freebsd.go b/serial_freebsd.go index b648b52..ca1ef76 100644 --- a/serial_freebsd.go +++ b/serial_freebsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_linux.go b/serial_linux.go index 1a447d8..e4a2d45 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_linux_test.go b/serial_linux_test.go index 01e3923..2eda7f3 100644 --- a/serial_linux_test.go +++ b/serial_linux_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_openbsd.go b/serial_openbsd.go index ad68957..e9d71a4 100644 --- a/serial_openbsd.go +++ b/serial_openbsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_unix.go b/serial_unix.go index 143818e..541660c 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_windows.go b/serial_windows.go index 6b6d0ee..af9a620 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/syscall_windows.go b/syscall_windows.go index ccd9508..baa4deb 100644 --- a/syscall_windows.go +++ b/syscall_windows.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // @@ -25,4 +25,3 @@ package serial //sys getOverlappedResult(handle syscall.Handle, overlapEvent *syscall.Overlapped, n *uint32, wait bool) (err error) = GetOverlappedResult //sys purgeComm(handle syscall.Handle, flags uint32) (err error) = PurgeComm - diff --git a/unixutils/pipe.go b/unixutils/pipe.go index a6be332..f187ce9 100644 --- a/unixutils/pipe.go +++ b/unixutils/pipe.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // @@ -8,8 +8,10 @@ package unixutils -import "syscall" -import "fmt" +import ( + "fmt" + "syscall" +) // Pipe represents a unix-pipe type Pipe struct { diff --git a/unixutils/select.go b/unixutils/select.go index 40b591c..e945ec1 100644 --- a/unixutils/select.go +++ b/unixutils/select.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2020 Cristian Maglie. All rights reserved. +// Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // From 40f997f235cb541dd9970492bc652d9f66a91a27 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 27 Aug 2021 16:19:34 +0200 Subject: [PATCH 121/181] Fixed runtime build problem with darwin/amd64 For some strange reason the interaction between the go-sqlite3 lib and the go-serial produced a weird panic as described in: https://github.com/bugst/go-serial/issues/115 https://github.com/bugst/go-serial/issues/116 The solution is to just upgrade the x/unix package to the latest version available at the moment. Fix #115 Fix #116 --- go.mod | 2 +- go.sum | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 05bb3fa..3d001f0 100644 --- a/go.mod +++ b/go.mod @@ -5,5 +5,5 @@ go 1.13 require ( github.com/creack/goselect v0.1.2 github.com/stretchr/testify v1.4.0 - golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 + golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf ) diff --git a/go.sum b/go.sum index 0473d34..9671380 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/creack/goselect v0.1.1 h1:tiSSgKE1eJtxs1h/VgGQWuXUP0YS4CDIFMp6vaI1ls0= -github.com/creack/goselect v0.1.1/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0= github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= @@ -9,8 +7,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= From 9fed730fb289a5f66992aeed23582d14ebcd26e3 Mon Sep 17 00:00:00 2001 From: Stepan Date: Mon, 6 Sep 2021 18:40:01 +0900 Subject: [PATCH 122/181] Added detection of UART ports (/dev/ttymxc*) --- serial_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serial_linux.go b/serial_linux.go index e4a2d45..1d6edb3 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -9,7 +9,7 @@ package serial import "golang.org/x/sys/unix" const devFolder = "/dev" -const regexFilter = "(ttyS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO)[0-9]{1,3}" +const regexFilter = "(ttyS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO|ttymxc)[0-9]{1,3}" // termios manipulation functions From 2cf9e2e28135003a758215f7b97bf8a1b74bbcc5 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 4 Jan 2022 17:11:35 +0100 Subject: [PATCH 123/181] Remove old go1.9- compatibilty files --- enumerator/usb_darwin.go | 2 - enumerator/usb_darwin_go1.9.go | 222 --------------------------------- 2 files changed, 224 deletions(-) delete mode 100644 enumerator/usb_darwin_go1.9.go diff --git a/enumerator/usb_darwin.go b/enumerator/usb_darwin.go index fdf68c9..b105ad3 100644 --- a/enumerator/usb_darwin.go +++ b/enumerator/usb_darwin.go @@ -4,8 +4,6 @@ // license that can be found in the LICENSE file. // -// +build go1.10,darwin - package enumerator // #cgo LDFLAGS: -framework CoreFoundation -framework IOKit diff --git a/enumerator/usb_darwin_go1.9.go b/enumerator/usb_darwin_go1.9.go deleted file mode 100644 index 167c44b..0000000 --- a/enumerator/usb_darwin_go1.9.go +++ /dev/null @@ -1,222 +0,0 @@ -// -// Copyright 2014-2021 Cristian Maglie. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -// +build !go1.10,darwin - -// This file is here to keep compatibility with the older versions of go -// and is no more maintained or bugfixed, please update your go version -// to at least 1.10 to get the latest updates. - -package enumerator - -// #cgo LDFLAGS: -framework CoreFoundation -framework IOKit -// #include -// #include -// #include -import "C" -import ( - "errors" - "fmt" - "unsafe" -) - -func nativeGetDetailedPortsList() ([]*PortDetails, error) { - var ports []*PortDetails - - services, err := getAllServices("IOSerialBSDClient") - if err != nil { - return nil, &PortEnumerationError{causedBy: err} - } - for _, service := range services { - defer service.Release() - - port, err := extractPortInfo(C.io_registry_entry_t(service)) - if err != nil { - return nil, &PortEnumerationError{causedBy: err} - } - ports = append(ports, port) - } - return ports, nil -} - -func extractPortInfo(service C.io_registry_entry_t) (*PortDetails, error) { - name, err := service.GetStringProperty("IOCalloutDevice") - if err != nil { - return nil, fmt.Errorf("Error extracting port info from device: %s", err.Error()) - } - port := &PortDetails{} - port.Name = name - port.IsUSB = false - - usbDevice := service - for usbDevice.GetClass() != "IOUSBDevice" { - if usbDevice, err = usbDevice.GetParent("IOService"); err != nil { - break - } - } - if err == nil { - // It's an IOUSBDevice - vid, _ := usbDevice.GetIntProperty("idVendor", C.kCFNumberSInt16Type) - pid, _ := usbDevice.GetIntProperty("idProduct", C.kCFNumberSInt16Type) - serialNumber, _ := usbDevice.GetStringProperty("USB Serial Number") - //product, _ := usbDevice.GetStringProperty("USB Product Name") - //manufacturer, _ := usbDevice.GetStringProperty("USB Vendor Name") - //fmt.Println(product + " - " + manufacturer) - - port.IsUSB = true - port.VID = fmt.Sprintf("%04X", vid) - port.PID = fmt.Sprintf("%04X", pid) - port.SerialNumber = serialNumber - } - return port, nil -} - -func getAllServices(serviceType string) ([]C.io_object_t, error) { - i, err := getMatchingServices(serviceMatching(serviceType)) - if err != nil { - return nil, err - } - defer i.Release() - - var services []C.io_object_t - tries := 0 - for tries < 5 { - // Extract all elements from iterator - if service, ok := i.Next(); ok { - services = append(services, service) - continue - } - // If iterator is still valid return the result - if i.IsValid() { - return services, nil - } - // Otherwise empty the result and retry - for _, s := range services { - s.Release() - } - services = []C.io_object_t{} - i.Reset() - tries++ - } - // Give up if the iteration continues to fail... - return nil, fmt.Errorf("IOServiceGetMatchingServices failed, data changed while iterating") -} - -// serviceMatching create a matching dictionary that specifies an IOService class match. -func serviceMatching(serviceType string) C.CFMutableDictionaryRef { - t := C.CString(serviceType) - defer C.free(unsafe.Pointer(t)) - return C.IOServiceMatching(t) -} - -// getMatchingServices look up registered IOService objects that match a matching dictionary. -func getMatchingServices(matcher C.CFMutableDictionaryRef) (C.io_iterator_t, error) { - var i C.io_iterator_t - err := C.IOServiceGetMatchingServices(C.kIOMasterPortDefault, matcher, &i) - if err != C.KERN_SUCCESS { - return 0, fmt.Errorf("IOServiceGetMatchingServices failed (code %d)", err) - } - return i, nil -} - -// CFStringRef - -func cfStringCreateWithString(s string) C.CFStringRef { - c := C.CString(s) - defer C.free(unsafe.Pointer(c)) - return C.CFStringCreateWithCString( - C.kCFAllocatorDefault, c, C.kCFStringEncodingMacRoman) -} - -// io_registry_entry_t - -func (me *C.io_registry_entry_t) GetParent(plane string) (C.io_registry_entry_t, error) { - cPlane := C.CString(plane) - defer C.free(unsafe.Pointer(cPlane)) - var parent C.io_registry_entry_t - err := C.IORegistryEntryGetParentEntry(*me, cPlane, &parent) - if err != 0 { - return 0, errors.New("No parent device available") - } - return parent, nil -} - -func (me *C.io_registry_entry_t) GetClass() string { - obj := (*C.io_object_t)(me) - return obj.GetClass() -} - -func (me *C.io_registry_entry_t) GetStringProperty(key string) (string, error) { - k := cfStringCreateWithString(key) - defer C.CFRelease(C.CFTypeRef(k)) - property := C.IORegistryEntryCreateCFProperty(*me, k, C.kCFAllocatorDefault, 0) - if property == nil { - return "", errors.New("Property not found: " + key) - } - defer C.CFRelease(property) - - if ptr := C.CFStringGetCStringPtr((C.CFStringRef)(unsafe.Pointer(property)), 0); ptr != nil { - return C.GoString(ptr), nil - } - // in certain circumstances CFStringGetCStringPtr may return NULL - // and we must retrieve the string by copy - buff := make([]C.char, 1024) - if C.CFStringGetCString((C.CFStringRef)(property), &buff[0], 1024, 0) != C.true { - return "", fmt.Errorf("Property '%s' can't be converted", key) - } - return C.GoString(&buff[0]), nil -} - -func (me *C.io_registry_entry_t) GetIntProperty(key string, intType C.CFNumberType) (int, error) { - k := cfStringCreateWithString(key) - defer C.CFRelease(C.CFTypeRef(k)) - property := C.IORegistryEntryCreateCFProperty(*me, k, C.kCFAllocatorDefault, 0) - if property == nil { - return 0, errors.New("Property not found: " + key) - } - defer C.CFRelease(property) - var res int - if C.CFNumberGetValue((C.CFNumberRef)(property), intType, unsafe.Pointer(&res)) != C.true { - return res, fmt.Errorf("Property '%s' can't be converted or has been truncated", key) - } - return res, nil -} - -// io_iterator_t - -// IsValid checks if an iterator is still valid. -// Some iterators will be made invalid if changes are made to the -// structure they are iterating over. This function checks the iterator -// is still valid and should be called when Next returns zero. -// An invalid iterator can be Reset and the iteration restarted. -func (me *C.io_iterator_t) IsValid() bool { - return C.IOIteratorIsValid(*me) == C.true -} - -func (me *C.io_iterator_t) Reset() { - C.IOIteratorReset(*me) -} - -func (me *C.io_iterator_t) Next() (C.io_object_t, bool) { - res := C.IOIteratorNext(*me) - return res, res != 0 -} - -func (me *C.io_iterator_t) Release() { - C.IOObjectRelease(C.io_object_t(*me)) -} - -// io_object_t - -func (me *C.io_object_t) Release() { - C.IOObjectRelease(*me) -} - -func (me *C.io_object_t) GetClass() string { - class := make([]C.char, 1024) - C.IOObjectGetClass(*me, &class[0]) - return C.GoString(&class[0]) -} From c3f2459bdd62ba4e71fa6942b13a604be77e75e2 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 4 Jan 2022 17:12:23 +0100 Subject: [PATCH 124/181] Update minimal go version to 1.17 and do a run of go mod tidy --- go.mod | 10 ++++++++-- go.sum | 8 ++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 3d001f0..80dddac 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,15 @@ module go.bug.st/serial -go 1.13 +go 1.17 require ( github.com/creack/goselect v0.1.2 - github.com/stretchr/testify v1.4.0 + github.com/stretchr/testify v1.7.0 golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf ) + +require ( + github.com/davecgh/go-spew v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect +) diff --git a/go.sum b/go.sum index 9671380..2abf8f5 100644 --- a/go.sum +++ b/go.sum @@ -5,11 +5,11 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 0e95fd4d4b7f130f125426ffba0ac96871094a85 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 4 Jan 2022 17:27:57 +0100 Subject: [PATCH 125/181] Updated test workflows to run on go1.17 --- .github/workflows/test.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 94707b7..7a43621 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -18,7 +18,7 @@ jobs: - uses: actions/checkout@v1 - uses: actions/setup-go@v1 with: - go-version: "1.13" + go-version: "1.17" - name: Build native run: GOARCH=amd64 go build -v ./... shell: bash @@ -54,7 +54,7 @@ jobs: - uses: actions/checkout@v1 - uses: actions/setup-go@v1 with: - go-version: "1.13" + go-version: "1.17" - name: Cross-build run: | set ${{ matrix.go-os-pairs }} From b27526b25dd7475befcca1f2f6b89403a649b447 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 18 Feb 2022 11:45:32 +0100 Subject: [PATCH 126/181] Skip enumerating ports that are not accessible for any reason Otherwise ports like /dev/ttyS0 may be listed in case of "access denied" errors. --- serial_unix.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/serial_unix.go b/serial_unix.go index 541660c..3508796 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -286,10 +286,7 @@ func nativeGetPortsList() ([]string, error) { if strings.HasPrefix(f.Name(), "ttyS") { port, err := nativeOpen(portName, &Mode{}) if err != nil { - serr, ok := err.(*PortError) - if ok && serr.Code() == InvalidSerialPort { - continue - } + continue } else { port.Close() } From 5e9cceab810b23beb4a73208cd5326bcf9805e5a Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 18 Feb 2022 12:42:15 +0100 Subject: [PATCH 127/181] Fix potential small timeout bug on unix implementation A very small port.readTimeout may lead to a negative timeout if the elapsed time between: deadline = time.Now().Add(port.readTimeout) and timeout = time.Until(deadline) is longer than port.readTimeout. Fix #134 --- serial_unix.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/serial_unix.go b/serial_unix.go index 6a492ad..d7c7078 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -72,7 +72,11 @@ func (port *unixPort) Read(p []byte) (int, error) { for { timeout := time.Duration(-1) if port.readTimeout != NoTimeout { - timeout = deadline.Sub(time.Now()) + timeout = time.Until(deadline) + if timeout < 0 { + // a negative timeout means "no-timeout" in Select(...) + timeout = 0 + } } res, err := unixutils.Select(fds, nil, fds, timeout) if err == unix.EINTR { From 980038c3ea2f7ce387e4c29aeb8adfea70c1e8de Mon Sep 17 00:00:00 2001 From: Tobias Kaupat Date: Fri, 2 Mar 2018 16:35:56 +0100 Subject: [PATCH 128/181] Allow to set initial RTS and DTR state after connect --- serial.go | 10 ++++++---- serial_unix.go | 3 +++ serial_windows.go | 8 ++++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/serial.go b/serial.go index 3e4f3b1..674069b 100644 --- a/serial.go +++ b/serial.go @@ -74,10 +74,12 @@ func GetPortsList() ([]string, error) { // Mode describes a serial port configuration. type Mode struct { - BaudRate int // The serial port bitrate (aka Baudrate) - DataBits int // Size of the character (must be 5, 6, 7 or 8) - Parity Parity // Parity (see Parity type for more info) - StopBits StopBits // Stop bits (see StopBits type for more info) + BaudRate int // The serial port bitrate (aka Baudrate) + DataBits int // Size of the character (must be 5, 6, 7 or 8) + Parity Parity // Parity (see Parity type for more info) + StopBits StopBits // Stop bits (see StopBits type for more info) + InitialRTS bool // Initial state for RTS + InitialDTR bool // Initial state for DTR } // Parity describes a serial port parity setting diff --git a/serial_unix.go b/serial_unix.go index d7c7078..2f7e1d1 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -235,6 +235,9 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { // Explicitly disable RTS/CTS flow control setTermSettingsCtsRts(false, settings) + port.SetDTR(mode.InitialDTR) + port.SetRTS(mode.InitialRTS) + if port.setTermSettings(settings) != nil { port.Close() return nil, &PortError{code: InvalidSerialPort} diff --git a/serial_windows.go b/serial_windows.go index af9a620..dd0715c 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -451,9 +451,13 @@ func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { return nil, &PortError{code: InvalidSerialPort} } params.Flags &= dcbRTSControlDisbaleMask - params.Flags |= dcbRTSControlEnable + if mode.InitialRTS { + params.Flags |= dcbRTSControlEnable + } params.Flags &= dcbDTRControlDisableMask - params.Flags |= dcbDTRControlEnable + if mode.InitialDTR { + params.Flags |= dcbDTRControlEnable + } params.Flags &^= dcbOutXCTSFlow params.Flags &^= dcbOutXDSRFlow params.Flags &^= dcbDSRSensitivity From 3c9f14262a33bf0e6ee4d647088b1ec755e02da9 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 1 Sep 2022 16:18:52 +0200 Subject: [PATCH 129/181] Default status bits to 'on' --- serial.go | 20 +++++++++++++------- serial_unix.go | 23 ++++++++++++++++++++--- serial_windows.go | 15 ++++++++++----- 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/serial.go b/serial.go index 674069b..43f0f74 100644 --- a/serial.go +++ b/serial.go @@ -53,7 +53,7 @@ type Port interface { // NoTimeout should be used as a parameter to SetReadTimeout to disable timeout. var NoTimeout time.Duration = -1 -// ModemStatusBits contains all the modem status bits for a serial port (CTS, DSR, etc...). +// ModemStatusBits contains all the modem input status bits for a serial port (CTS, DSR, etc...). // It can be retrieved with the Port.GetModemStatusBits() method. type ModemStatusBits struct { CTS bool // ClearToSend status @@ -62,6 +62,13 @@ type ModemStatusBits struct { DCD bool // DataCarrierDetect status } +// ModemOutputBits contains all the modem output bits for a serial port. +// This is used in the Mode.InitialStatusBits struct to specify the initial status of the bits. +type ModemOutputBits struct { + RTS bool // ReadyToSend status + DTR bool // DataTerminalReady status +} + // Open opens the serial port using the specified modes func Open(portName string, mode *Mode) (Port, error) { return nativeOpen(portName, mode) @@ -74,12 +81,11 @@ func GetPortsList() ([]string, error) { // Mode describes a serial port configuration. type Mode struct { - BaudRate int // The serial port bitrate (aka Baudrate) - DataBits int // Size of the character (must be 5, 6, 7 or 8) - Parity Parity // Parity (see Parity type for more info) - StopBits StopBits // Stop bits (see StopBits type for more info) - InitialRTS bool // Initial state for RTS - InitialDTR bool // Initial state for DTR + BaudRate int // The serial port bitrate (aka Baudrate) + DataBits int // Size of the character (must be 5, 6, 7 or 8) + Parity Parity // Parity (see Parity type for more info) + StopBits StopBits // Stop bits (see StopBits type for more info) + InitialStatusBits *ModemOutputBits // Initial output modem bits status (if nil defaults to DTR=true and RTS=true) } // Parity describes a serial port parity setting diff --git a/serial_unix.go b/serial_unix.go index 2f7e1d1..4a8ef66 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -235,14 +235,31 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { // Explicitly disable RTS/CTS flow control setTermSettingsCtsRts(false, settings) - port.SetDTR(mode.InitialDTR) - port.SetRTS(mode.InitialRTS) - if port.setTermSettings(settings) != nil { port.Close() return nil, &PortError{code: InvalidSerialPort} } + if mode.InitialStatusBits != nil { + status, err := port.getModemBitsStatus() + if err != nil { + return nil, &PortError{code: InvalidSerialPort, causedBy: err} + } + if mode.InitialStatusBits.DTR { + status |= unix.TIOCM_DTR + } else { + status &^= unix.TIOCM_DTR + } + if mode.InitialStatusBits.RTS { + status |= unix.TIOCM_RTS + } else { + status &^= unix.TIOCM_RTS + } + if err := port.setModemBitsStatus(status); err != nil { + return nil, &PortError{code: InvalidSerialPort, causedBy: err} + } + } + // MacOSX require that this operation is the last one otherwise an // 'Invalid serial port' error is returned... don't know why... if port.SetMode(mode) != nil { diff --git a/serial_windows.go b/serial_windows.go index dd0715c..4b652b5 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -450,13 +450,18 @@ func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { port.Close() return nil, &PortError{code: InvalidSerialPort} } - params.Flags &= dcbRTSControlDisbaleMask - if mode.InitialRTS { - params.Flags |= dcbRTSControlEnable - } params.Flags &= dcbDTRControlDisableMask - if mode.InitialDTR { + params.Flags &= dcbRTSControlDisbaleMask + if mode.InitialStatusBits == nil { params.Flags |= dcbDTRControlEnable + params.Flags |= dcbRTSControlEnable + } else { + if mode.InitialStatusBits.DTR { + params.Flags |= dcbDTRControlEnable + } + if mode.InitialStatusBits.RTS { + params.Flags |= dcbRTSControlEnable + } } params.Flags &^= dcbOutXCTSFlow params.Flags &^= dcbOutXDSRFlow From 3099149a031a72dcd4b85593c5eceadeb4d0b111 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sat, 3 Sep 2022 18:04:47 +0200 Subject: [PATCH 130/181] Added a doc notice for posix 'pulse' problem --- serial.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/serial.go b/serial.go index 43f0f74..e072d42 100644 --- a/serial.go +++ b/serial.go @@ -64,6 +64,9 @@ type ModemStatusBits struct { // ModemOutputBits contains all the modem output bits for a serial port. // This is used in the Mode.InitialStatusBits struct to specify the initial status of the bits. +// Note: Linux and MacOSX (and basically all unix-based systems) can not set the status bits +// before opening the port, even if the initial state of the bit is set to false they will go +// anyway to true for a few milliseconds, resulting in a small pulse. type ModemOutputBits struct { RTS bool // ReadyToSend status DTR bool // DataTerminalReady status From 1bf551e0252c0dee88785679d45170060b19a602 Mon Sep 17 00:00:00 2001 From: Jack Doan Date: Fri, 30 Sep 2022 13:22:52 -0500 Subject: [PATCH 131/181] Fix ResetInputBuffer and ResetOutputBuffer on Darwin --- serial_darwin.go | 8 ++++++++ serial_resetbuf_linux_bsd.go | 19 +++++++++++++++++++ serial_unix.go | 8 -------- 3 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 serial_resetbuf_linux_bsd.go diff --git a/serial_darwin.go b/serial_darwin.go index ea1b99f..f30dc8d 100644 --- a/serial_darwin.go +++ b/serial_darwin.go @@ -29,3 +29,11 @@ func (port *unixPort) setSpecialBaudrate(speed uint32) error { const kIOSSIOSPEED = 0x80045402 return unix.IoctlSetPointerInt(port.handle, kIOSSIOSPEED, int(speed)) } + +func (port *unixPort) ResetInputBuffer() error { + return unix.IoctlSetPointerInt(port.handle, ioctlTcflsh, unix.TCIFLUSH) +} + +func (port *unixPort) ResetOutputBuffer() error { + return unix.IoctlSetPointerInt(port.handle, ioctlTcflsh, unix.TCOFLUSH) +} diff --git a/serial_resetbuf_linux_bsd.go b/serial_resetbuf_linux_bsd.go new file mode 100644 index 0000000..c6e1a4a --- /dev/null +++ b/serial_resetbuf_linux_bsd.go @@ -0,0 +1,19 @@ +// +// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +// +build linux freebsd openbsd + +package serial + +import "golang.org/x/sys/unix" + +func (port *unixPort) ResetInputBuffer() error { + return unix.IoctlSetInt(port.handle, ioctlTcflsh, unix.TCIFLUSH) +} + +func (port *unixPort) ResetOutputBuffer() error { + return unix.IoctlSetInt(port.handle, ioctlTcflsh, unix.TCOFLUSH) +} diff --git a/serial_unix.go b/serial_unix.go index 4a8ef66..e171001 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -117,14 +117,6 @@ func (port *unixPort) Write(p []byte) (n int, err error) { return } -func (port *unixPort) ResetInputBuffer() error { - return unix.IoctlSetInt(port.handle, ioctlTcflsh, unix.TCIFLUSH) -} - -func (port *unixPort) ResetOutputBuffer() error { - return unix.IoctlSetInt(port.handle, ioctlTcflsh, unix.TCOFLUSH) -} - func (port *unixPort) SetMode(mode *Mode) error { settings, err := port.getTermSettings() if err != nil { From 1b32e598713681cbac2986178ce88fe926d32190 Mon Sep 17 00:00:00 2001 From: Frank Palazzolo Date: Mon, 14 Nov 2022 08:58:11 -0500 Subject: [PATCH 132/181] Remove unnecessary code, originally for checking disconnects The old code is no more needed when using overlapped I/O, getOverlappedEvent() returns all required error codes. Calling getCommState()/setCommState() every second in some applications contributes to other downstream issues. For examples: https://github.com/bugst/go-serial/issues/60#issuecomment-625749122 https://github.com/arduino/arduino-ide/issues/375 --- serial_windows.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/serial_windows.go b/serial_windows.go index 4b652b5..96b021c 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -114,17 +114,6 @@ func (port *windowsPort) Read(p []byte) (int, error) { return 0, nil } } - - // At the moment it seems that the only reliable way to check if - // a serial port is alive in Windows is to check if the SetCommState - // function fails. - - params := &dcb{} - getCommState(port.handle, params) - if err := setCommState(port.handle, params); err != nil { - port.Close() - return 0, err - } } } From 5d3a516f129c780bbacec69cdb4caf06de49a1b7 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 24 Nov 2022 17:14:03 +0100 Subject: [PATCH 133/181] Removed port-alive-check loop on Windows implementation --- serial_windows.go | 81 +++++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 48 deletions(-) diff --git a/serial_windows.go b/serial_windows.go index 96b021c..1a3db7a 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -24,9 +24,8 @@ import ( ) type windowsPort struct { - mu sync.Mutex - handle syscall.Handle - readTimeoutCycles int64 + mu sync.Mutex + handle syscall.Handle } func nativeGetPortsList() ([]string, error) { @@ -83,38 +82,26 @@ func (port *windowsPort) Read(p []byte) (int, error) { } defer syscall.CloseHandle(ev.HEvent) - cycles := int64(0) - for { - err := syscall.ReadFile(port.handle, p, &readed, ev) - if err == syscall.ERROR_IO_PENDING { - err = getOverlappedResult(port.handle, ev, &readed, true) - } - switch err { - case nil: - // operation completed successfully - case syscall.ERROR_OPERATION_ABORTED: - // port may have been closed - return int(readed), &PortError{code: PortClosed, causedBy: err} - default: - // error happened - return int(readed), err - } - - if readed > 0 { - return int(readed), nil - } - if err := resetEvent(ev.HEvent); err != nil { - return 0, err - } - - if port.readTimeoutCycles != -1 { - cycles++ - if cycles == port.readTimeoutCycles { - // Timeout - return 0, nil - } - } + err = syscall.ReadFile(port.handle, p, &readed, ev) + if err == syscall.ERROR_IO_PENDING { + err = getOverlappedResult(port.handle, ev, &readed, true) + } + switch err { + case nil: + // operation completed successfully + case syscall.ERROR_OPERATION_ABORTED: + // port may have been closed + return int(readed), &PortError{code: PortClosed, causedBy: err} + default: + // error happened + return int(readed), err } + if readed > 0 { + return int(readed), nil + } + + // Timeout + return 0, nil } func (port *windowsPort) Write(p []byte) (int, error) { @@ -372,26 +359,24 @@ func (port *windowsPort) GetModemStatusBits() (*ModemStatusBits, error) { } func (port *windowsPort) SetReadTimeout(timeout time.Duration) error { - var cycles, cycleDuration int64 - if timeout == NoTimeout { - cycles = -1 - cycleDuration = 1000 - } else { - cycles = timeout.Milliseconds()/1000 + 1 - cycleDuration = timeout.Milliseconds() / cycles - } - - err := setCommTimeouts(port.handle, &commTimeouts{ + commTimeouts := &commTimeouts{ ReadIntervalTimeout: 0xFFFFFFFF, ReadTotalTimeoutMultiplier: 0xFFFFFFFF, - ReadTotalTimeoutConstant: uint32(cycleDuration), + ReadTotalTimeoutConstant: 0xFFFFFFFE, WriteTotalTimeoutConstant: 0, WriteTotalTimeoutMultiplier: 0, - }) - if err != nil { + } + if timeout != NoTimeout { + ms := timeout.Milliseconds() + if ms > 0xFFFFFFFE || ms < 0 { + return &PortError{code: InvalidTimeoutValue} + } + commTimeouts.ReadTotalTimeoutConstant = uint32(ms) + } + + if err := setCommTimeouts(port.handle, commTimeouts); err != nil { return &PortError{code: InvalidTimeoutValue, causedBy: err} } - port.readTimeoutCycles = cycles return nil } From 376ad6baaf064039c145c366f5e8822aedd5ffec Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 2 Jan 2023 12:56:19 +0100 Subject: [PATCH 134/181] Retry port enumeration 5 times before giving up (macosx) We observer errors getting the IOCalloutDevice string, this is a tentative fix based on the hypotesis that the enumerator is called too early during the USB enumeration phase. --- enumerator/usb_darwin.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/enumerator/usb_darwin.go b/enumerator/usb_darwin.go index b105ad3..99dba0e 100644 --- a/enumerator/usb_darwin.go +++ b/enumerator/usb_darwin.go @@ -14,6 +14,7 @@ import "C" import ( "errors" "fmt" + "time" "unsafe" ) @@ -37,12 +38,20 @@ func nativeGetDetailedPortsList() ([]*PortDetails, error) { } func extractPortInfo(service C.io_registry_entry_t) (*PortDetails, error) { - name, err := service.GetStringProperty("IOCalloutDevice") - if err != nil { - return nil, fmt.Errorf("Error extracting port info from device: %s", err.Error()) - } port := &PortDetails{} - port.Name = name + // If called too early the port may still not be ready or fully enumerated + // so we retry 5 times before returning error. + for retries := 5; retries > 0; retries-- { + name, err := service.GetStringProperty("IOCalloutDevice") + if err == nil { + port.Name = name + break + } + if retries == 0 { + return nil, fmt.Errorf("error extracting port info from device: %w", err) + } + time.Sleep(50 * time.Millisecond) + } port.IsUSB = false validUSBDeviceClass := map[string]bool{ From 75542ba31ae866b467db201441ac0d86157088d0 Mon Sep 17 00:00:00 2001 From: Jaime Date: Wed, 4 May 2022 12:03:56 +0200 Subject: [PATCH 135/181] Added support for sending breaks --- serial.go | 3 +++ serial_darwin.go | 2 ++ serial_freebsd.go | 2 ++ serial_linux.go | 2 ++ serial_openbsd.go | 2 ++ serial_unix.go | 15 +++++++++++++++ serial_windows.go | 4 ++++ 7 files changed, 30 insertions(+) diff --git a/serial.go b/serial.go index e072d42..2bb7f3e 100644 --- a/serial.go +++ b/serial.go @@ -48,6 +48,9 @@ type Port interface { // Close the serial port Close() error + + // Break sends a break for a determined time + Break(time.Duration) error } // NoTimeout should be used as a parameter to SetReadTimeout to disable timeout. diff --git a/serial_darwin.go b/serial_darwin.go index f30dc8d..6c448a7 100644 --- a/serial_darwin.go +++ b/serial_darwin.go @@ -14,6 +14,8 @@ const regexFilter = "^(cu|tty)\\..*" const ioctlTcgetattr = unix.TIOCGETA const ioctlTcsetattr = unix.TIOCSETA const ioctlTcflsh = unix.TIOCFLUSH +const ioctlTioccbrk = unix.TIOCCBRK +const ioctlTiocsbrk = unix.TIOCSBRK func setTermSettingsBaudrate(speed int, settings *unix.Termios) (error, bool) { baudrate, ok := baudrateMap[speed] diff --git a/serial_freebsd.go b/serial_freebsd.go index ca1ef76..1ccaf33 100644 --- a/serial_freebsd.go +++ b/serial_freebsd.go @@ -56,6 +56,8 @@ const tcCRTSCTS uint32 = tcCCTS_OFLOW const ioctlTcgetattr = unix.TIOCGETA const ioctlTcsetattr = unix.TIOCSETA const ioctlTcflsh = unix.TIOCFLUSH +const ioctlTioccbrk = unix.TIOCCBRK +const ioctlTiocsbrk = unix.TIOCSBRK func toTermiosSpeedType(speed uint32) uint32 { return speed diff --git a/serial_linux.go b/serial_linux.go index 1d6edb3..116474d 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -63,6 +63,8 @@ const tcCRTSCTS uint32 = unix.CRTSCTS const ioctlTcgetattr = unix.TCGETS const ioctlTcsetattr = unix.TCSETS const ioctlTcflsh = unix.TCFLSH +const ioctlTioccbrk = unix.TIOCCBRK +const ioctlTiocsbrk = unix.TIOCSBRK func toTermiosSpeedType(speed uint32) uint32 { return speed diff --git a/serial_openbsd.go b/serial_openbsd.go index e9d71a4..bf38bba 100644 --- a/serial_openbsd.go +++ b/serial_openbsd.go @@ -56,6 +56,8 @@ const tcCRTSCTS uint32 = tcCCTS_OFLOW const ioctlTcgetattr = unix.TIOCGETA const ioctlTcsetattr = unix.TIOCSETA const ioctlTcflsh = unix.TIOCFLUSH +const ioctlTioccbrk = unix.TIOCCBRK +const ioctlTiocsbrk = unix.TIOCSBRK func toTermiosSpeedType(speed uint32) int32 { return int32(speed) diff --git a/serial_unix.go b/serial_unix.go index e171001..0e83018 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -4,6 +4,7 @@ // license that can be found in the LICENSE file. // +//go:build linux || darwin || freebsd || openbsd // +build linux darwin freebsd openbsd package serial @@ -117,6 +118,20 @@ func (port *unixPort) Write(p []byte) (n int, err error) { return } +func (port *unixPort) Break(t time.Duration) error { + if err := unix.IoctlSetInt(port.handle, ioctlTiocsbrk, 0); err != nil { + return err + } + + time.Sleep(t) + + if err := unix.IoctlSetInt(port.handle, ioctlTioccbrk, 0); err != nil { + return err + } + + return nil +} + func (port *unixPort) SetMode(mode *Mode) error { settings, err := port.getTermSettings() if err != nil { diff --git a/serial_windows.go b/serial_windows.go index 1a3db7a..e84ea08 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -381,6 +381,10 @@ func (port *windowsPort) SetReadTimeout(timeout time.Duration) error { return nil } +func (port *windowsPort) Break(d time.Duration) error { + return &PortError{code: FunctionNotImplemented} +} + func createOverlappedEvent() (*syscall.Overlapped, error) { h, err := createEvent(nil, true, false, nil) return &syscall.Overlapped{HEvent: h}, err From 68f27d6a45b909c1425346fbc5b9718a2b96064e Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 4 Sep 2022 17:28:51 +0200 Subject: [PATCH 136/181] Upgrade x/sys dependency --- go.mod | 2 +- go.sum | 2 + zsyscall_windows.go | 123 ++++++++++++++++---------------------------- 3 files changed, 48 insertions(+), 79 deletions(-) diff --git a/go.mod b/go.mod index 80dddac..c3abe90 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/creack/goselect v0.1.2 github.com/stretchr/testify v1.7.0 - golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf + golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 ) require ( diff --git a/go.sum b/go.sum index 2abf8f5..dac8e08 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,8 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= diff --git a/zsyscall_windows.go b/zsyscall_windows.go index 9c42379..e848c9f 100644 --- a/zsyscall_windows.go +++ b/zsyscall_windows.go @@ -19,6 +19,7 @@ const ( var ( errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) + errERROR_EINVAL error = syscall.EINVAL ) // errnoErr returns common boxed Errno values, to prevent @@ -26,7 +27,7 @@ var ( func errnoErr(e syscall.Errno) error { switch e { case 0: - return nil + return errERROR_EINVAL case errnoERROR_IO_PENDING: return errERROR_IO_PENDING } @@ -41,15 +42,15 @@ var ( modkernel32 = windows.NewLazySystemDLL("kernel32.dll") procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") - procGetCommState = modkernel32.NewProc("GetCommState") - procSetCommState = modkernel32.NewProc("SetCommState") - procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") + procCreateEventW = modkernel32.NewProc("CreateEventW") procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction") procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procResetEvent = modkernel32.NewProc("ResetEvent") + procGetCommState = modkernel32.NewProc("GetCommState") procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") procPurgeComm = modkernel32.NewProc("PurgeComm") + procResetEvent = modkernel32.NewProc("ResetEvent") + procSetCommState = modkernel32.NewProc("SetCommState") + procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") ) func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) { @@ -60,38 +61,19 @@ func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint3 return } -func getCommState(handle syscall.Handle, dcb *dcb) (err error) { - r1, _, e1 := syscall.Syscall(procGetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0) - if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } +func createEvent(eventAttributes *uint32, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { + var _p0 uint32 + if manualReset { + _p0 = 1 } - return -} - -func setCommState(handle syscall.Handle, dcb *dcb) (err error) { - r1, _, e1 := syscall.Syscall(procSetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0) - if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } + var _p1 uint32 + if initialState { + _p1 = 1 } - return -} - -func setCommTimeouts(handle syscall.Handle, timeouts *commTimeouts) (err error) { - r1, _, e1 := syscall.Syscall(procSetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0) - if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } + r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(eventAttributes)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) + handle = syscall.Handle(r0) + if handle == 0 { + err = errnoErr(e1) } return } @@ -108,27 +90,30 @@ func getCommModemStatus(handle syscall.Handle, bits *uint32) (res bool) { return } -func createEvent(eventAttributes *uint32, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { +func getCommState(handle syscall.Handle, dcb *dcb) (err error) { + r1, _, e1 := syscall.Syscall(procGetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + +func getOverlappedResult(handle syscall.Handle, overlapEvent *syscall.Overlapped, n *uint32, wait bool) (err error) { var _p0 uint32 - if manualReset { + if wait { _p0 = 1 - } else { - _p0 = 0 } - var _p1 uint32 - if initialState { - _p1 = 1 - } else { - _p1 = 0 + r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapEvent)), uintptr(unsafe.Pointer(n)), uintptr(_p0), 0, 0) + if r1 == 0 { + err = errnoErr(e1) } - r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(eventAttributes)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) - handle = syscall.Handle(r0) - if handle == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } + return +} + +func purgeComm(handle syscall.Handle, flags uint32) (err error) { + r1, _, e1 := syscall.Syscall(procPurgeComm.Addr(), 2, uintptr(handle), uintptr(flags), 0) + if r1 == 0 { + err = errnoErr(e1) } return } @@ -136,41 +121,23 @@ func createEvent(eventAttributes *uint32, manualReset bool, initialState bool, n func resetEvent(handle syscall.Handle) (err error) { r1, _, e1 := syscall.Syscall(procResetEvent.Addr(), 1, uintptr(handle), 0, 0) if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } + err = errnoErr(e1) } return } -func getOverlappedResult(handle syscall.Handle, overlapEvent *syscall.Overlapped, n *uint32, wait bool) (err error) { - var _p0 uint32 - if wait { - _p0 = 1 - } else { - _p0 = 0 - } - r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapEvent)), uintptr(unsafe.Pointer(n)), uintptr(_p0), 0, 0) +func setCommState(handle syscall.Handle, dcb *dcb) (err error) { + r1, _, e1 := syscall.Syscall(procSetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0) if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } + err = errnoErr(e1) } return } -func purgeComm(handle syscall.Handle, flags uint32) (err error) { - r1, _, e1 := syscall.Syscall(procPurgeComm.Addr(), 2, uintptr(handle), uintptr(flags), 0) +func setCommTimeouts(handle syscall.Handle, timeouts *commTimeouts) (err error) { + r1, _, e1 := syscall.Syscall(procSetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0) if r1 == 0 { - if e1 != 0 { - err = errnoErr(e1) - } else { - err = syscall.EINVAL - } + err = errnoErr(e1) } return } From 893b2eb08269314af23244584a2871af81f0898b Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 4 Sep 2022 17:32:15 +0200 Subject: [PATCH 137/181] Added Break implementation for windows --- serial_windows.go | 12 +++++++++++- syscall_windows.go | 4 ++++ zsyscall_windows.go | 18 ++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/serial_windows.go b/serial_windows.go index e84ea08..e393d4e 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -382,7 +382,17 @@ func (port *windowsPort) SetReadTimeout(timeout time.Duration) error { } func (port *windowsPort) Break(d time.Duration) error { - return &PortError{code: FunctionNotImplemented} + if err := setCommBreak(port.handle); err != nil { + return &PortError{causedBy: err} + } + + time.Sleep(d) + + if err := clearCommBreak(port.handle); err != nil { + return &PortError{causedBy: err} + } + + return nil } func createOverlappedEvent() (*syscall.Overlapped, error) { diff --git a/syscall_windows.go b/syscall_windows.go index baa4deb..3b86776 100644 --- a/syscall_windows.go +++ b/syscall_windows.go @@ -25,3 +25,7 @@ package serial //sys getOverlappedResult(handle syscall.Handle, overlapEvent *syscall.Overlapped, n *uint32, wait bool) (err error) = GetOverlappedResult //sys purgeComm(handle syscall.Handle, flags uint32) (err error) = PurgeComm + +//sys setCommBreak(handle syscall.Handle) (err error) = SetCommBreak + +//sys clearCommBreak(handle syscall.Handle) (err error) = ClearCommBreak diff --git a/zsyscall_windows.go b/zsyscall_windows.go index e848c9f..a2411a6 100644 --- a/zsyscall_windows.go +++ b/zsyscall_windows.go @@ -42,6 +42,7 @@ var ( modkernel32 = windows.NewLazySystemDLL("kernel32.dll") procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") + procClearCommBreak = modkernel32.NewProc("ClearCommBreak") procCreateEventW = modkernel32.NewProc("CreateEventW") procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction") procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus") @@ -49,6 +50,7 @@ var ( procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") procPurgeComm = modkernel32.NewProc("PurgeComm") procResetEvent = modkernel32.NewProc("ResetEvent") + procSetCommBreak = modkernel32.NewProc("SetCommBreak") procSetCommState = modkernel32.NewProc("SetCommState") procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") ) @@ -61,6 +63,14 @@ func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint3 return } +func clearCommBreak(handle syscall.Handle) (err error) { + r1, _, e1 := syscall.Syscall(procClearCommBreak.Addr(), 1, uintptr(handle), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func createEvent(eventAttributes *uint32, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { var _p0 uint32 if manualReset { @@ -126,6 +136,14 @@ func resetEvent(handle syscall.Handle) (err error) { return } +func setCommBreak(handle syscall.Handle) (err error) { + r1, _, e1 := syscall.Syscall(procSetCommBreak.Addr(), 1, uintptr(handle), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func setCommState(handle syscall.Handle, dcb *dcb) (err error) { r1, _, e1 := syscall.Syscall(procSetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0) if r1 == 0 { From e6cff1a986e70bc0163658b5ab76729d283c2014 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 2 Jan 2023 14:44:56 +0100 Subject: [PATCH 138/181] windows: Avoid panic if USB Product name is empty --- enumerator/usb_windows.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/enumerator/usb_windows.go b/enumerator/usb_windows.go index b69e115..fd2a167 100644 --- a/enumerator/usb_windows.go +++ b/enumerator/usb_windows.go @@ -347,10 +347,12 @@ func retrievePortDetailsFromDevInfo(device *deviceInfo, details *PortDetails) er the result of spdrpFriendlyName is therefore unique and suitable as an alternative string to for a port choice */ n := uint32(0) setupDiGetDeviceRegistryProperty(device.set, &device.data, spdrpFriendlyName /* spdrpDeviceDesc */, nil, nil, 0, &n) - buff := make([]uint16, n*2) - buffP := (*byte)(unsafe.Pointer(&buff[0])) - if setupDiGetDeviceRegistryProperty(device.set, &device.data, spdrpFriendlyName /* spdrpDeviceDesc */, nil, buffP, n, &n) { - details.Product = syscall.UTF16ToString(buff[:]) + if n > 0 { + buff := make([]uint16, n*2) + buffP := (*byte)(unsafe.Pointer(&buff[0])) + if setupDiGetDeviceRegistryProperty(device.set, &device.data, spdrpFriendlyName /* spdrpDeviceDesc */, nil, buffP, n, &n) { + details.Product = syscall.UTF16ToString(buff[:]) + } } return nil From ff38fe2a780f46777bf5ed91729ec99d01e88918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Polomsk=C3=BD?= Date: Sat, 9 May 2020 08:34:28 +0200 Subject: [PATCH 139/181] add support for drain Tested on Mac OS, Linux and Windows with UART emulated on USB. I wrote general BSD code, because it should work, but I am not able to test it. On Mac OS I am quite sure that drain behaves as expected because it fixed an issue with buffer overflow (which OS does not report but fails). For Windows it seems that drain is actually part of `Write`, because `Write` was long and `Drain` was fast. But I have not found any mention in Win32 docs about buffering and asynchronous write, so I put there flushing code to be sure, and this code did not break anything. For Linux `Drain` is also taking more time than writing so it looks working too. --- serial.go | 3 +++ serial_bsd.go | 10 ++++++++++ serial_linux.go | 10 ++++++++++ serial_windows.go | 4 ++++ 4 files changed, 27 insertions(+) create mode 100644 serial_bsd.go diff --git a/serial.go b/serial.go index 2bb7f3e..4420100 100644 --- a/serial.go +++ b/serial.go @@ -26,6 +26,9 @@ type Port interface { // Returns the number of bytes written. Write(p []byte) (n int, err error) + // Wait until all data in the buffer are sent + Drain() error + // ResetInputBuffer Purges port read buffer ResetInputBuffer() error diff --git a/serial_bsd.go b/serial_bsd.go new file mode 100644 index 0000000..4db5b3d --- /dev/null +++ b/serial_bsd.go @@ -0,0 +1,10 @@ +//go:build darwin || dragonfly || freebsd || netbsd || openbsd +// +build darwin dragonfly freebsd netbsd openbsd + +package serial + +import "golang.org/x/sys/unix" + +func (port *unixPort) Drain() error { + return unix.IoctlSetInt(port.handle, unix.TIOCDRAIN, 0) +} diff --git a/serial_linux.go b/serial_linux.go index 116474d..0ac0f39 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -85,3 +85,13 @@ func setTermSettingsBaudrate(speed int, settings *unix.Termios) (error, bool) { settings.Ospeed = toTermiosSpeedType(baudrate) return nil, false } + +func (port *unixPort) Drain() error { + // simulate drain with change settings with TCSETSW + settings, err := port.getTermSettings() + if err != nil { + return err + } + + return unix.IoctlSetTermios(port.handle, unix.TCSETSW, settings) +} diff --git a/serial_windows.go b/serial_windows.go index e393d4e..5c4a576 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -119,6 +119,10 @@ func (port *windowsPort) Write(p []byte) (int, error) { return int(writed), err } +func (port *windowsPort) Drain() (err error) { + return syscall.FlushFileBuffers(port.handle) +} + const ( purgeRxAbort uint32 = 0x0002 purgeRxClear = 0x0008 From 10b7b3e7afcfd35a5e8de8f5f2376c9bbe26a503 Mon Sep 17 00:00:00 2001 From: Ancient Xu Date: Tue, 8 Aug 2023 18:17:02 +0800 Subject: [PATCH 140/181] feat: add ttyHS support for linux Signed-off-by: Ancient Xu --- serial_linux.go | 2 +- serial_unix.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/serial_linux.go b/serial_linux.go index 116474d..7ab05e7 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -9,7 +9,7 @@ package serial import "golang.org/x/sys/unix" const devFolder = "/dev" -const regexFilter = "(ttyS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO|ttymxc)[0-9]{1,3}" +const regexFilter = "(ttyS|ttyHS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO|ttymxc)[0-9]{1,3}" // termios manipulation functions diff --git a/serial_unix.go b/serial_unix.go index 0e83018..c19513c 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -313,8 +313,8 @@ func nativeGetPortsList() ([]string, error) { portName := devFolder + "/" + f.Name() - // Check if serial port is real or is a placeholder serial port "ttySxx" - if strings.HasPrefix(f.Name(), "ttyS") { + // Check if serial port is real or is a placeholder serial port "ttySxx" or "ttyHSxx" + if strings.HasPrefix(f.Name(), "ttyS") || strings.HasPrefix(f.Name(), "ttyHS") { port, err := nativeOpen(portName, &Mode{}) if err != nil { continue From bafe3137499d5f80f2948a7d900378194bf90a2a Mon Sep 17 00:00:00 2001 From: Andre Renaud Date: Fri, 24 Mar 2023 16:09:44 +1300 Subject: [PATCH 141/181] Rework for Linux & BSD differences --- serial_bsd.go | 1 - serial_linux.go | 8 +------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/serial_bsd.go b/serial_bsd.go index 4db5b3d..4e1f226 100644 --- a/serial_bsd.go +++ b/serial_bsd.go @@ -1,5 +1,4 @@ //go:build darwin || dragonfly || freebsd || netbsd || openbsd -// +build darwin dragonfly freebsd netbsd openbsd package serial diff --git a/serial_linux.go b/serial_linux.go index 0ac0f39..c8a7651 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -87,11 +87,5 @@ func setTermSettingsBaudrate(speed int, settings *unix.Termios) (error, bool) { } func (port *unixPort) Drain() error { - // simulate drain with change settings with TCSETSW - settings, err := port.getTermSettings() - if err != nil { - return err - } - - return unix.IoctlSetTermios(port.handle, unix.TCSETSW, settings) + return unix.IoctlSetInt(port.handle, unix.TCSBRK, 1) } From cce21700ebb37329e433df2a9c056d0bc512d0af Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 9 Aug 2023 18:29:02 +0200 Subject: [PATCH 142/181] Added informative comment --- serial_linux.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/serial_linux.go b/serial_linux.go index c8a7651..adc1149 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -87,5 +87,8 @@ func setTermSettingsBaudrate(speed int, settings *unix.Termios) (error, bool) { } func (port *unixPort) Drain() error { + // It's not super well documented, but this is the same as calling tcdrain: + // - https://git.musl-libc.org/cgit/musl/tree/src/termios/tcdrain.c + // - https://elixir.bootlin.com/linux/v6.2.8/source/drivers/tty/tty_io.c#L2673 return unix.IoctlSetInt(port.handle, unix.TCSBRK, 1) } From d2a59a651d77bc80c4acb155773278350292de71 Mon Sep 17 00:00:00 2001 From: Daniel Lublin Date: Thu, 9 Feb 2023 08:22:51 +0100 Subject: [PATCH 143/181] Make Open return a nil interface on failure Closes #154 Signed-off-by: Daniel Lublin --- serial.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/serial.go b/serial.go index 4420100..be0a738 100644 --- a/serial.go +++ b/serial.go @@ -80,7 +80,13 @@ type ModemOutputBits struct { // Open opens the serial port using the specified modes func Open(portName string, mode *Mode) (Port, error) { - return nativeOpen(portName, mode) + port, err := nativeOpen(portName, mode) + if err != nil { + // Return a nil interface, for which var==nil is true (instead of + // a nil pointer to a struct that satisfies the interface). + return nil, err + } + return port, err } // GetPortsList retrieve the list of available serial ports From 75c59a94185524f418524c40af7671e83d64d308 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 9 Aug 2023 18:48:09 +0200 Subject: [PATCH 144/181] Removed obsolete code --- enumerator/usb_ole_windows.go | 113 ---------------------------------- 1 file changed, 113 deletions(-) delete mode 100644 enumerator/usb_ole_windows.go diff --git a/enumerator/usb_ole_windows.go b/enumerator/usb_ole_windows.go deleted file mode 100644 index c02356a..0000000 --- a/enumerator/usb_ole_windows.go +++ /dev/null @@ -1,113 +0,0 @@ -// -// Copyright 2014-2021 Lars Knudsen, Cristian Maglie. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -// +build ignore - -package enumerator - -import ( - "log" - "regexp" - - ole "github.com/go-ole/go-ole" - "github.com/go-ole/go-ole/oleutil" -) - -func init() { - err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED) - if err != nil { - log.Fatal("Init error: ", err) - } -} - -func nativeGetDetailedPortsList() ([]*PortDetails, error) { - unknown, err := oleutil.CreateObject("WbemScripting.SWbemLocator") - if err != nil { - return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} - } - defer unknown.Release() - - wmi, err := unknown.QueryInterface(ole.IID_IDispatch) - if err != nil { - return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} - } - defer wmi.Release() - - serviceRaw, err := wmi.CallMethod("ConnectServer") - if err != nil { - return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} - } - service := serviceRaw.ToIDispatch() - defer service.Release() - - query := "SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0 and Name like '%(COM%'" - queryResult, err := oleutil.CallMethod(service, "ExecQuery", query) - if err != nil { - return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} - } - result := queryResult.ToIDispatch() - defer result.Release() - - countVar, err := result.GetProperty("Count") - if err != nil { - return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} - } - count := int(countVar.Val) - - res := []*PortDetails{} - - // Retrieve all items - for i := 0; i < count; i++ { - itemRaw, err := result.CallMethod("ItemIndex", i) - if err != nil { - return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} - } - item := itemRaw.ToIDispatch() - defer item.Release() - - detail := &PortDetails{} - if err := getPortDetails(item, detail); err != nil { - return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} - } - // SerialPort{Path: path, VendorId: VID, ProductId: PID, DisplayName: displayName.ToString()} - res = append(res, detail) - } - - return res, nil -} - -func getPortDetails(item *ole.IDispatch, res *PortDetails) error { - // Find port name - itemName, err := item.GetProperty("Name") - if err != nil { - return err - } - re := regexp.MustCompile("\\((COM[0-9]+)\\)").FindAllStringSubmatch(itemName.ToString(), 1) - if re == nil || len(re[0]) < 2 { - // Discard items that are not serial ports - return nil - } - res.Name = re[0][1] - - //itemPnPDeviceID, err := item.GetProperty("PnPDeviceID") - //if err != nil { - // return err - //} - //PnPDeviceID := itemPnPDeviceID.ToString() - - itemDeviceID, err := item.GetProperty("DeviceID") - if err != nil { - return err - } - parseDeviceID(itemDeviceID.ToString(), res) - - itemManufacturer, err := item.GetProperty("Product") - if err != nil { - return err - } - res.Manufacturer = itemManufacturer.ToString() - return nil -} From 93097bf4956879a3284ed23f64004207742a2d3e Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 9 Aug 2023 18:54:02 +0200 Subject: [PATCH 145/181] Removed obsolete +build directives in favor of modern go:build --- serial_darwin_64.go | 5 ++--- serial_resetbuf_linux_bsd.go | 2 +- serial_specialbaudrate_linux.go | 5 ++--- serial_unix.go | 1 - unixutils/pipe.go | 2 +- unixutils/select.go | 2 +- 6 files changed, 7 insertions(+), 10 deletions(-) diff --git a/serial_darwin_64.go b/serial_darwin_64.go index 7f72e66..2318a6d 100644 --- a/serial_darwin_64.go +++ b/serial_darwin_64.go @@ -1,12 +1,11 @@ -// +build darwin -// +build amd64 arm64 - // // Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +//go:build darwin && (amd64 || arm64) + package serial import "golang.org/x/sys/unix" diff --git a/serial_resetbuf_linux_bsd.go b/serial_resetbuf_linux_bsd.go index c6e1a4a..8895ce1 100644 --- a/serial_resetbuf_linux_bsd.go +++ b/serial_resetbuf_linux_bsd.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -// +build linux freebsd openbsd +//go:build linux || freebsd || openbsd package serial diff --git a/serial_specialbaudrate_linux.go b/serial_specialbaudrate_linux.go index d6baffb..3a105eb 100644 --- a/serial_specialbaudrate_linux.go +++ b/serial_specialbaudrate_linux.go @@ -1,12 +1,11 @@ -// +build linux -// +build !ppc64le - // // Copyright 2014-2021 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // +//go:build linux && !ppc64le + package serial import "golang.org/x/sys/unix" diff --git a/serial_unix.go b/serial_unix.go index c19513c..6f58d98 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -5,7 +5,6 @@ // //go:build linux || darwin || freebsd || openbsd -// +build linux darwin freebsd openbsd package serial diff --git a/unixutils/pipe.go b/unixutils/pipe.go index f187ce9..311c801 100644 --- a/unixutils/pipe.go +++ b/unixutils/pipe.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -// +build linux darwin freebsd openbsd +//go:build linux || darwin || freebsd || openbsd package unixutils diff --git a/unixutils/select.go b/unixutils/select.go index e945ec1..12705d8 100644 --- a/unixutils/select.go +++ b/unixutils/select.go @@ -4,7 +4,7 @@ // license that can be found in the LICENSE file. // -// +build linux darwin freebsd openbsd +//go:build linux || darwin || freebsd || openbsd package unixutils From 386b387977a1fafaa3ef7737fb495bd74d15d047 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 10 Aug 2023 01:07:25 +0200 Subject: [PATCH 146/181] Updated license --- doc.go | 2 +- enumerator/doc.go | 2 +- enumerator/enumerator.go | 2 +- enumerator/example_getdetailedportlist_test.go | 2 +- enumerator/usb_darwin.go | 2 +- enumerator/usb_freebsd.go | 2 +- enumerator/usb_linux.go | 2 +- enumerator/usb_openbsd.go | 2 +- enumerator/usb_windows.go | 2 +- enumerator/usb_windows_test.go | 2 +- example_getportlist_test.go | 2 +- example_modem_bits_test.go | 2 +- example_serialport_test.go | 2 +- example_test.go | 2 +- portlist/portlist.go | 2 +- serial.go | 2 +- serial_bsd.go | 6 ++++++ serial_darwin.go | 2 +- serial_darwin_386.go | 2 +- serial_darwin_64.go | 2 +- serial_freebsd.go | 2 +- serial_linux.go | 2 +- serial_linux_test.go | 4 ++-- serial_openbsd.go | 2 +- serial_resetbuf_linux_bsd.go | 2 +- serial_specialbaudrate_linux.go | 2 +- serial_specialbaudrate_linux_ppc64le.go | 2 +- serial_unix.go | 2 +- serial_windows.go | 2 +- syscall_windows.go | 2 +- unixutils/pipe.go | 2 +- unixutils/select.go | 2 +- 32 files changed, 38 insertions(+), 32 deletions(-) diff --git a/doc.go b/doc.go index 9705eb0..6e230ff 100644 --- a/doc.go +++ b/doc.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/doc.go b/enumerator/doc.go index 818180d..a3f887d 100644 --- a/enumerator/doc.go +++ b/enumerator/doc.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/enumerator.go b/enumerator/enumerator.go index 6f0f32d..420f451 100644 --- a/enumerator/enumerator.go +++ b/enumerator/enumerator.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/example_getdetailedportlist_test.go b/enumerator/example_getdetailedportlist_test.go index 0d3bae4..d2796fb 100644 --- a/enumerator/example_getdetailedportlist_test.go +++ b/enumerator/example_getdetailedportlist_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_darwin.go b/enumerator/usb_darwin.go index 99dba0e..1b1cbd1 100644 --- a/enumerator/usb_darwin.go +++ b/enumerator/usb_darwin.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_freebsd.go b/enumerator/usb_freebsd.go index 739e222..db5d96c 100644 --- a/enumerator/usb_freebsd.go +++ b/enumerator/usb_freebsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_linux.go b/enumerator/usb_linux.go index bad2323..f756367 100644 --- a/enumerator/usb_linux.go +++ b/enumerator/usb_linux.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_openbsd.go b/enumerator/usb_openbsd.go index 739e222..db5d96c 100644 --- a/enumerator/usb_openbsd.go +++ b/enumerator/usb_openbsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_windows.go b/enumerator/usb_windows.go index fd2a167..7883e45 100644 --- a/enumerator/usb_windows.go +++ b/enumerator/usb_windows.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_windows_test.go b/enumerator/usb_windows_test.go index df5223a..3160ef3 100644 --- a/enumerator/usb_windows_test.go +++ b/enumerator/usb_windows_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/example_getportlist_test.go b/example_getportlist_test.go index fe3bf61..beb4773 100644 --- a/example_getportlist_test.go +++ b/example_getportlist_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/example_modem_bits_test.go b/example_modem_bits_test.go index 298271e..34b212f 100644 --- a/example_modem_bits_test.go +++ b/example_modem_bits_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/example_serialport_test.go b/example_serialport_test.go index 544cd42..12f68b5 100644 --- a/example_serialport_test.go +++ b/example_serialport_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/example_test.go b/example_test.go index 63866bb..27430d0 100644 --- a/example_test.go +++ b/example_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/portlist/portlist.go b/portlist/portlist.go index a198b81..8c36e5c 100644 --- a/portlist/portlist.go +++ b/portlist/portlist.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial.go b/serial.go index be0a738..abfd7f9 100644 --- a/serial.go +++ b/serial.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_bsd.go b/serial_bsd.go index 4e1f226..fb7c849 100644 --- a/serial_bsd.go +++ b/serial_bsd.go @@ -1,3 +1,9 @@ +// +// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + //go:build darwin || dragonfly || freebsd || netbsd || openbsd package serial diff --git a/serial_darwin.go b/serial_darwin.go index 6c448a7..2817041 100644 --- a/serial_darwin.go +++ b/serial_darwin.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_darwin_386.go b/serial_darwin_386.go index 850f6b7..3594a6c 100644 --- a/serial_darwin_386.go +++ b/serial_darwin_386.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_darwin_64.go b/serial_darwin_64.go index 2318a6d..d1ae7c4 100644 --- a/serial_darwin_64.go +++ b/serial_darwin_64.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_freebsd.go b/serial_freebsd.go index 1ccaf33..aceb08a 100644 --- a/serial_freebsd.go +++ b/serial_freebsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_linux.go b/serial_linux.go index 32049a2..fe114d9 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_linux_test.go b/serial_linux_test.go index 2eda7f3..1a7e1a3 100644 --- a/serial_linux_test.go +++ b/serial_linux_test.go @@ -1,11 +1,11 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // + // Testing code idea and fix thanks to @angri // https://github.com/bugst/go-serial/pull/42 -// package serial diff --git a/serial_openbsd.go b/serial_openbsd.go index bf38bba..644dc92 100644 --- a/serial_openbsd.go +++ b/serial_openbsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_resetbuf_linux_bsd.go b/serial_resetbuf_linux_bsd.go index 8895ce1..105669d 100644 --- a/serial_resetbuf_linux_bsd.go +++ b/serial_resetbuf_linux_bsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_specialbaudrate_linux.go b/serial_specialbaudrate_linux.go index 3a105eb..ff48974 100644 --- a/serial_specialbaudrate_linux.go +++ b/serial_specialbaudrate_linux.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_specialbaudrate_linux_ppc64le.go b/serial_specialbaudrate_linux_ppc64le.go index 4db8cc7..cd80b86 100644 --- a/serial_specialbaudrate_linux_ppc64le.go +++ b/serial_specialbaudrate_linux_ppc64le.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_unix.go b/serial_unix.go index 6f58d98..f6ec896 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_windows.go b/serial_windows.go index 5c4a576..287856a 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/syscall_windows.go b/syscall_windows.go index 3b86776..c814528 100644 --- a/syscall_windows.go +++ b/syscall_windows.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/unixutils/pipe.go b/unixutils/pipe.go index 311c801..748de34 100644 --- a/unixutils/pipe.go +++ b/unixutils/pipe.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/unixutils/select.go b/unixutils/select.go index 12705d8..9f0e214 100644 --- a/unixutils/select.go +++ b/unixutils/select.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2021 Cristian Maglie. All rights reserved. +// Copyright 2014-2023 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // From 74e194ea45d11f342c453fcf88d6b4d2f18e083e Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 10 Aug 2023 01:09:42 +0200 Subject: [PATCH 147/181] Updated license --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 50ee44b..e34cbff 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ -Copyright (c) 2014-2021, Cristian Maglie. +Copyright (c) 2014-2023, Cristian Maglie. All rights reserved. Redistribution and use in source and binary forms, with or without From 80a3721f8b044b23f09b6cdcbada741d60d60b59 Mon Sep 17 00:00:00 2001 From: deadprogram Date: Mon, 17 Jul 2023 11:20:37 +0200 Subject: [PATCH 148/181] darwin: use Go type wrappers to avoid declaring Go methods on C types. This is needed starting with Go 1.21 due to stricter enforcement of rules about adding methods to existing types, now including C types. See https://github.com/golang/go/issues/60725 for further details. Signed-off-by: deadprogram --- enumerator/usb_darwin.go | 120 +++++++++++++++++++++++++-------------- 1 file changed, 76 insertions(+), 44 deletions(-) diff --git a/enumerator/usb_darwin.go b/enumerator/usb_darwin.go index 1b1cbd1..2d791d0 100644 --- a/enumerator/usb_darwin.go +++ b/enumerator/usb_darwin.go @@ -18,6 +18,26 @@ import ( "unsafe" ) +type io_iterator_t struct { + ioiterator C.io_iterator_t +} + +type io_object_t struct { + ioobject C.io_object_t +} + +type io_registry_entry_t struct { + ioregistryentry C.io_registry_entry_t +} + +type cfStringRef struct { + cfs C.CFStringRef +} + +type cfTypeRef struct { + cft C.CFTypeRef +} + func nativeGetDetailedPortsList() ([]*PortDetails, error) { var ports []*PortDetails @@ -28,7 +48,7 @@ func nativeGetDetailedPortsList() ([]*PortDetails, error) { for _, service := range services { defer service.Release() - port, err := extractPortInfo(C.io_registry_entry_t(service)) + port, err := extractPortInfo(&io_registry_entry_t{ioregistryentry: service.ioobject}) if err != nil { return nil, &PortEnumerationError{causedBy: err} } @@ -37,7 +57,7 @@ func nativeGetDetailedPortsList() ([]*PortDetails, error) { return ports, nil } -func extractPortInfo(service C.io_registry_entry_t) (*PortDetails, error) { +func extractPortInfo(service *io_registry_entry_t) (*PortDetails, error) { port := &PortDetails{} // If called too early the port may still not be ready or fully enumerated // so we retry 5 times before returning error. @@ -58,12 +78,18 @@ func extractPortInfo(service C.io_registry_entry_t) (*PortDetails, error) { "IOUSBDevice": true, "IOUSBHostDevice": true, } - usbDevice := service - var searchErr error - for !validUSBDeviceClass[usbDevice.GetClass()] { + + var ( + usbDevice = io_registry_entry_t{service.ioregistryentry} + usbDeviceObj = io_object_t{usbDevice.ioregistryentry} + searchErr error + ) + + for !validUSBDeviceClass[usbDeviceObj.GetClass()] { if usbDevice, searchErr = usbDevice.GetParent("IOService"); searchErr != nil { break } + usbDeviceObj = io_object_t{usbDevice.ioregistryentry} } if searchErr == nil { // It's an IOUSBDevice @@ -82,19 +108,19 @@ func extractPortInfo(service C.io_registry_entry_t) (*PortDetails, error) { return port, nil } -func getAllServices(serviceType string) ([]C.io_object_t, error) { +func getAllServices(serviceType string) ([]io_object_t, error) { i, err := getMatchingServices(serviceMatching(serviceType)) if err != nil { return nil, err } defer i.Release() - var services []C.io_object_t + var services []io_object_t tries := 0 for tries < 5 { // Extract all elements from iterator if service, ok := i.Next(); ok { - services = append(services, service) + services = append(services, *service) continue } // If iterator is still valid return the result @@ -105,7 +131,7 @@ func getAllServices(serviceType string) ([]C.io_object_t, error) { for _, s := range services { s.Release() } - services = []C.io_object_t{} + services = []io_object_t{} i.Reset() tries++ } @@ -121,84 +147,86 @@ func serviceMatching(serviceType string) C.CFMutableDictionaryRef { } // getMatchingServices look up registered IOService objects that match a matching dictionary. -func getMatchingServices(matcher C.CFMutableDictionaryRef) (C.io_iterator_t, error) { +func getMatchingServices(matcher C.CFMutableDictionaryRef) (io_iterator_t, error) { var i C.io_iterator_t err := C.IOServiceGetMatchingServices(C.kIOMasterPortDefault, C.CFDictionaryRef(matcher), &i) if err != C.KERN_SUCCESS { - return 0, fmt.Errorf("IOServiceGetMatchingServices failed (code %d)", err) + return io_iterator_t{}, fmt.Errorf("IOServiceGetMatchingServices failed (code %d)", err) } - return i, nil + return io_iterator_t{i}, nil } -// CFStringRef +// cfStringRef -func cfStringCreateWithString(s string) C.CFStringRef { +func CFStringCreateWithString(s string) cfStringRef { c := C.CString(s) defer C.free(unsafe.Pointer(c)) - return C.CFStringCreateWithCString( + val := C.CFStringCreateWithCString( C.kCFAllocatorDefault, c, C.kCFStringEncodingMacRoman) + return cfStringRef{val} } -func (ref C.CFStringRef) Release() { - C.CFRelease(C.CFTypeRef(ref)) +func (ref cfStringRef) Release() { + C.CFRelease(C.CFTypeRef(ref.cfs)) } // CFTypeRef -func (ref C.CFTypeRef) Release() { - C.CFRelease(ref) +func (ref cfTypeRef) Release() { + C.CFRelease(C.CFTypeRef(ref.cft)) } // io_registry_entry_t -func (me *C.io_registry_entry_t) GetParent(plane string) (C.io_registry_entry_t, error) { +func (me *io_registry_entry_t) GetParent(plane string) (io_registry_entry_t, error) { cPlane := C.CString(plane) defer C.free(unsafe.Pointer(cPlane)) var parent C.io_registry_entry_t - err := C.IORegistryEntryGetParentEntry(*me, cPlane, &parent) + err := C.IORegistryEntryGetParentEntry(me.ioregistryentry, cPlane, &parent) if err != 0 { - return 0, errors.New("No parent device available") + return io_registry_entry_t{}, errors.New("No parent device available") } - return parent, nil + return io_registry_entry_t{parent}, nil } -func (me *C.io_registry_entry_t) CreateCFProperty(key string) (C.CFTypeRef, error) { - k := cfStringCreateWithString(key) +func (me *io_registry_entry_t) CreateCFProperty(key string) (cfTypeRef, error) { + k := CFStringCreateWithString(key) defer k.Release() - property := C.IORegistryEntryCreateCFProperty(*me, k, C.kCFAllocatorDefault, 0) + property := C.IORegistryEntryCreateCFProperty(me.ioregistryentry, k.cfs, C.kCFAllocatorDefault, 0) if property == 0 { - return 0, errors.New("Property not found: " + key) + return cfTypeRef{}, errors.New("Property not found: " + key) } - return property, nil + return cfTypeRef{property}, nil } -func (me *C.io_registry_entry_t) GetStringProperty(key string) (string, error) { +func (me *io_registry_entry_t) GetStringProperty(key string) (string, error) { property, err := me.CreateCFProperty(key) if err != nil { + fmt.Println(err) return "", err } defer property.Release() - if ptr := C.CFStringGetCStringPtr(C.CFStringRef(property), 0); ptr != nil { + if ptr := C.CFStringGetCStringPtr(C.CFStringRef(property.cft), 0); ptr != nil { return C.GoString(ptr), nil } // in certain circumstances CFStringGetCStringPtr may return NULL // and we must retrieve the string by copy buff := make([]C.char, 1024) - if C.CFStringGetCString(C.CFStringRef(property), &buff[0], 1024, 0) != C.true { + if C.CFStringGetCString(C.CFStringRef(property.cft), &buff[0], 1024, 0) != C.true { return "", fmt.Errorf("Property '%s' can't be converted", key) } return C.GoString(&buff[0]), nil } -func (me *C.io_registry_entry_t) GetIntProperty(key string, intType C.CFNumberType) (int, error) { +func (me *io_registry_entry_t) GetIntProperty(key string, intType C.CFNumberType) (int, error) { property, err := me.CreateCFProperty(key) if err != nil { return 0, err } defer property.Release() var res int - if C.CFNumberGetValue((C.CFNumberRef)(property), intType, unsafe.Pointer(&res)) != C.true { + if C.CFNumberGetValue((C.CFNumberRef)(property.cft), intType, unsafe.Pointer(&res)) != C.true { return res, fmt.Errorf("Property '%s' can't be converted or has been truncated", key) } return res, nil @@ -211,27 +239,31 @@ func (me *C.io_registry_entry_t) GetIntProperty(key string, intType C.CFNumberTy // structure they are iterating over. This function checks the iterator // is still valid and should be called when Next returns zero. // An invalid iterator can be Reset and the iteration restarted. -func (me *C.io_iterator_t) IsValid() bool { - return C.IOIteratorIsValid(*me) == C.true +func (me *io_iterator_t) IsValid() bool { + return C.IOIteratorIsValid(me.ioiterator) == C.true +} + +func (me *io_iterator_t) Reset() { + C.IOIteratorReset(me.ioiterator) } -func (me *C.io_iterator_t) Reset() { - C.IOIteratorReset(*me) +func (me *io_iterator_t) Next() (*io_object_t, bool) { + res := C.IOIteratorNext(me.ioiterator) + return &io_object_t{res}, res != 0 } -func (me *C.io_iterator_t) Next() (C.io_object_t, bool) { - res := C.IOIteratorNext(*me) - return res, res != 0 +func (me *io_iterator_t) Release() { + C.IOObjectRelease(me.ioiterator) } // io_object_t -func (me *C.io_object_t) Release() { - C.IOObjectRelease(*me) +func (me *io_object_t) Release() { + C.IOObjectRelease(me.ioobject) } -func (me *C.io_object_t) GetClass() string { +func (me *io_object_t) GetClass() string { class := make([]C.char, 1024) - C.IOObjectGetClass(*me, &class[0]) + C.IOObjectGetClass(me.ioobject, &class[0]) return C.GoString(&class[0]) } From c1ded5fc9846782d3f5387f43a85fbd77023e2f7 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 7 Aug 2023 13:26:49 +0200 Subject: [PATCH 149/181] darwin: moved type definitions near methods definition --- enumerator/usb_darwin.go | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/enumerator/usb_darwin.go b/enumerator/usb_darwin.go index 2d791d0..1295f04 100644 --- a/enumerator/usb_darwin.go +++ b/enumerator/usb_darwin.go @@ -18,26 +18,6 @@ import ( "unsafe" ) -type io_iterator_t struct { - ioiterator C.io_iterator_t -} - -type io_object_t struct { - ioobject C.io_object_t -} - -type io_registry_entry_t struct { - ioregistryentry C.io_registry_entry_t -} - -type cfStringRef struct { - cfs C.CFStringRef -} - -type cfTypeRef struct { - cft C.CFTypeRef -} - func nativeGetDetailedPortsList() ([]*PortDetails, error) { var ports []*PortDetails @@ -158,6 +138,10 @@ func getMatchingServices(matcher C.CFMutableDictionaryRef) (io_iterator_t, error // cfStringRef +type cfStringRef struct { + cfs C.CFStringRef +} + func CFStringCreateWithString(s string) cfStringRef { c := C.CString(s) defer C.free(unsafe.Pointer(c)) @@ -172,12 +156,20 @@ func (ref cfStringRef) Release() { // CFTypeRef +type cfTypeRef struct { + cft C.CFTypeRef +} + func (ref cfTypeRef) Release() { C.CFRelease(C.CFTypeRef(ref.cft)) } // io_registry_entry_t +type io_registry_entry_t struct { + ioregistryentry C.io_registry_entry_t +} + func (me *io_registry_entry_t) GetParent(plane string) (io_registry_entry_t, error) { cPlane := C.CString(plane) defer C.free(unsafe.Pointer(cPlane)) @@ -234,6 +226,10 @@ func (me *io_registry_entry_t) GetIntProperty(key string, intType C.CFNumberType // io_iterator_t +type io_iterator_t struct { + ioiterator C.io_iterator_t +} + // IsValid checks if an iterator is still valid. // Some iterators will be made invalid if changes are made to the // structure they are iterating over. This function checks the iterator @@ -258,6 +254,10 @@ func (me *io_iterator_t) Release() { // io_object_t +type io_object_t struct { + ioobject C.io_object_t +} + func (me *io_object_t) Release() { C.IOObjectRelease(me.ioobject) } From c9a9ed456d9037fa8b6a5c9705731391272e9c31 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 7 Aug 2023 13:33:41 +0200 Subject: [PATCH 150/181] darwin: wrap cgo-types directly (without using a struct) This makes easier the boxing/unboxing of the wrapped types. --- enumerator/usb_darwin.go | 101 ++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 54 deletions(-) diff --git a/enumerator/usb_darwin.go b/enumerator/usb_darwin.go index 1295f04..0918f1e 100644 --- a/enumerator/usb_darwin.go +++ b/enumerator/usb_darwin.go @@ -28,7 +28,7 @@ func nativeGetDetailedPortsList() ([]*PortDetails, error) { for _, service := range services { defer service.Release() - port, err := extractPortInfo(&io_registry_entry_t{ioregistryentry: service.ioobject}) + port, err := extractPortInfo(io_registry_entry_t(service)) if err != nil { return nil, &PortEnumerationError{causedBy: err} } @@ -37,7 +37,7 @@ func nativeGetDetailedPortsList() ([]*PortDetails, error) { return ports, nil } -func extractPortInfo(service *io_registry_entry_t) (*PortDetails, error) { +func extractPortInfo(service io_registry_entry_t) (*PortDetails, error) { port := &PortDetails{} // If called too early the port may still not be ready or fully enumerated // so we retry 5 times before returning error. @@ -58,18 +58,12 @@ func extractPortInfo(service *io_registry_entry_t) (*PortDetails, error) { "IOUSBDevice": true, "IOUSBHostDevice": true, } - - var ( - usbDevice = io_registry_entry_t{service.ioregistryentry} - usbDeviceObj = io_object_t{usbDevice.ioregistryentry} - searchErr error - ) - - for !validUSBDeviceClass[usbDeviceObj.GetClass()] { + usbDevice := service + var searchErr error + for !validUSBDeviceClass[usbDevice.GetClass()] { if usbDevice, searchErr = usbDevice.GetParent("IOService"); searchErr != nil { break } - usbDeviceObj = io_object_t{usbDevice.ioregistryentry} } if searchErr == nil { // It's an IOUSBDevice @@ -100,7 +94,7 @@ func getAllServices(serviceType string) ([]io_object_t, error) { for tries < 5 { // Extract all elements from iterator if service, ok := i.Next(); ok { - services = append(services, *service) + services = append(services, service) continue } // If iterator is still valid return the result @@ -131,64 +125,57 @@ func getMatchingServices(matcher C.CFMutableDictionaryRef) (io_iterator_t, error var i C.io_iterator_t err := C.IOServiceGetMatchingServices(C.kIOMasterPortDefault, C.CFDictionaryRef(matcher), &i) if err != C.KERN_SUCCESS { - return io_iterator_t{}, fmt.Errorf("IOServiceGetMatchingServices failed (code %d)", err) + return 0, fmt.Errorf("IOServiceGetMatchingServices failed (code %d)", err) } - return io_iterator_t{i}, nil + return io_iterator_t(i), nil } -// cfStringRef +// CFStringRef -type cfStringRef struct { - cfs C.CFStringRef -} +type cfStringRef C.CFStringRef -func CFStringCreateWithString(s string) cfStringRef { +func cfStringCreateWithString(s string) cfStringRef { c := C.CString(s) defer C.free(unsafe.Pointer(c)) - val := C.CFStringCreateWithCString( - C.kCFAllocatorDefault, c, C.kCFStringEncodingMacRoman) - return cfStringRef{val} + return cfStringRef(C.CFStringCreateWithCString( + C.kCFAllocatorDefault, c, C.kCFStringEncodingMacRoman)) } func (ref cfStringRef) Release() { - C.CFRelease(C.CFTypeRef(ref.cfs)) + C.CFRelease(C.CFTypeRef(ref)) } // CFTypeRef -type cfTypeRef struct { - cft C.CFTypeRef -} +type cfTypeRef C.CFTypeRef func (ref cfTypeRef) Release() { - C.CFRelease(C.CFTypeRef(ref.cft)) + C.CFRelease(C.CFTypeRef(ref)) } // io_registry_entry_t -type io_registry_entry_t struct { - ioregistryentry C.io_registry_entry_t -} +type io_registry_entry_t C.io_registry_entry_t func (me *io_registry_entry_t) GetParent(plane string) (io_registry_entry_t, error) { cPlane := C.CString(plane) defer C.free(unsafe.Pointer(cPlane)) var parent C.io_registry_entry_t - err := C.IORegistryEntryGetParentEntry(me.ioregistryentry, cPlane, &parent) + err := C.IORegistryEntryGetParentEntry(C.io_registry_entry_t(*me), cPlane, &parent) if err != 0 { - return io_registry_entry_t{}, errors.New("No parent device available") + return 0, errors.New("No parent device available") } - return io_registry_entry_t{parent}, nil + return io_registry_entry_t(parent), nil } func (me *io_registry_entry_t) CreateCFProperty(key string) (cfTypeRef, error) { - k := CFStringCreateWithString(key) + k := cfStringCreateWithString(key) defer k.Release() - property := C.IORegistryEntryCreateCFProperty(me.ioregistryentry, k.cfs, C.kCFAllocatorDefault, 0) + property := C.IORegistryEntryCreateCFProperty(C.io_registry_entry_t(*me), C.CFStringRef(k), C.kCFAllocatorDefault, 0) if property == 0 { - return cfTypeRef{}, errors.New("Property not found: " + key) + return 0, errors.New("Property not found: " + key) } - return cfTypeRef{property}, nil + return cfTypeRef(property), nil } func (me *io_registry_entry_t) GetStringProperty(key string) (string, error) { @@ -199,13 +186,13 @@ func (me *io_registry_entry_t) GetStringProperty(key string) (string, error) { } defer property.Release() - if ptr := C.CFStringGetCStringPtr(C.CFStringRef(property.cft), 0); ptr != nil { + if ptr := C.CFStringGetCStringPtr(C.CFStringRef(property), 0); ptr != nil { return C.GoString(ptr), nil } // in certain circumstances CFStringGetCStringPtr may return NULL // and we must retrieve the string by copy buff := make([]C.char, 1024) - if C.CFStringGetCString(C.CFStringRef(property.cft), &buff[0], 1024, 0) != C.true { + if C.CFStringGetCString(C.CFStringRef(property), &buff[0], 1024, 0) != C.true { return "", fmt.Errorf("Property '%s' can't be converted", key) } return C.GoString(&buff[0]), nil @@ -218,52 +205,58 @@ func (me *io_registry_entry_t) GetIntProperty(key string, intType C.CFNumberType } defer property.Release() var res int - if C.CFNumberGetValue((C.CFNumberRef)(property.cft), intType, unsafe.Pointer(&res)) != C.true { + if C.CFNumberGetValue((C.CFNumberRef)(property), intType, unsafe.Pointer(&res)) != C.true { return res, fmt.Errorf("Property '%s' can't be converted or has been truncated", key) } return res, nil } -// io_iterator_t +func (me *io_registry_entry_t) Release() { + C.IOObjectRelease(C.io_object_t(*me)) +} -type io_iterator_t struct { - ioiterator C.io_iterator_t +func (me *io_registry_entry_t) GetClass() string { + class := make([]C.char, 1024) + C.IOObjectGetClass(C.io_object_t(*me), &class[0]) + return C.GoString(&class[0]) } +// io_iterator_t + +type io_iterator_t C.io_iterator_t + // IsValid checks if an iterator is still valid. // Some iterators will be made invalid if changes are made to the // structure they are iterating over. This function checks the iterator // is still valid and should be called when Next returns zero. // An invalid iterator can be Reset and the iteration restarted. func (me *io_iterator_t) IsValid() bool { - return C.IOIteratorIsValid(me.ioiterator) == C.true + return C.IOIteratorIsValid(C.io_iterator_t(*me)) == C.true } func (me *io_iterator_t) Reset() { - C.IOIteratorReset(me.ioiterator) + C.IOIteratorReset(C.io_iterator_t(*me)) } -func (me *io_iterator_t) Next() (*io_object_t, bool) { - res := C.IOIteratorNext(me.ioiterator) - return &io_object_t{res}, res != 0 +func (me *io_iterator_t) Next() (io_object_t, bool) { + res := C.IOIteratorNext(C.io_iterator_t(*me)) + return io_object_t(res), res != 0 } func (me *io_iterator_t) Release() { - C.IOObjectRelease(me.ioiterator) + C.IOObjectRelease(C.io_object_t(*me)) } // io_object_t -type io_object_t struct { - ioobject C.io_object_t -} +type io_object_t C.io_object_t func (me *io_object_t) Release() { - C.IOObjectRelease(me.ioobject) + C.IOObjectRelease(C.io_object_t(*me)) } func (me *io_object_t) GetClass() string { class := make([]C.char, 1024) - C.IOObjectGetClass(me.ioobject, &class[0]) + C.IOObjectGetClass(C.io_object_t(*me), &class[0]) return C.GoString(&class[0]) } From 1e13b2f6101a1bbb929f7d045416194d5e1eea55 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 7 Aug 2023 13:34:41 +0200 Subject: [PATCH 151/181] removed leftover print --- enumerator/usb_darwin.go | 1 - 1 file changed, 1 deletion(-) diff --git a/enumerator/usb_darwin.go b/enumerator/usb_darwin.go index 0918f1e..2e653ec 100644 --- a/enumerator/usb_darwin.go +++ b/enumerator/usb_darwin.go @@ -181,7 +181,6 @@ func (me *io_registry_entry_t) CreateCFProperty(key string) (cfTypeRef, error) { func (me *io_registry_entry_t) GetStringProperty(key string) (string, error) { property, err := me.CreateCFProperty(key) if err != nil { - fmt.Println(err) return "", err } defer property.Release() From f3f01227e94235d352f14c6e318ff4e11ad6abb9 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 14 Sep 2023 15:17:30 +0200 Subject: [PATCH 152/181] Fix some rare IOServiceGetMatchingServices failure on MacOSX We had reports of users always getting: IOServiceGetMatchingServices failed, data changed while iterating The issue seems related to the function getMatchingServices that returns an always-invalid iterator even if there are no actual services. This is a workaround for this issue. --- enumerator/usb_darwin.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/enumerator/usb_darwin.go b/enumerator/usb_darwin.go index 2e653ec..d9adb1b 100644 --- a/enumerator/usb_darwin.go +++ b/enumerator/usb_darwin.go @@ -97,8 +97,8 @@ func getAllServices(serviceType string) ([]io_object_t, error) { services = append(services, service) continue } - // If iterator is still valid return the result - if i.IsValid() { + // If the list of services is empty or the iterator is still valid return the result + if len(services) == 0 || i.IsValid() { return services, nil } // Otherwise empty the result and retry From 0f730e491f1ffa921f4d83ed57f0d828a9fbe9f4 Mon Sep 17 00:00:00 2001 From: James Stanley Date: Sun, 11 Feb 2024 21:08:52 +0000 Subject: [PATCH 153/181] unix: nativeOpen: always close the port on errors --- serial_unix.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/serial_unix.go b/serial_unix.go index f6ec896..db6158d 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -249,6 +249,7 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { if mode.InitialStatusBits != nil { status, err := port.getModemBitsStatus() if err != nil { + port.Close() return nil, &PortError{code: InvalidSerialPort, causedBy: err} } if mode.InitialStatusBits.DTR { @@ -262,6 +263,7 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { status &^= unix.TIOCM_RTS } if err := port.setModemBitsStatus(status); err != nil { + port.Close() return nil, &PortError{code: InvalidSerialPort, causedBy: err} } } From c414e4f55c7954d5fb45f6479f2e3d858ece9231 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 16 Feb 2024 13:32:43 +0100 Subject: [PATCH 154/181] windows: Avoid double-set of communication parameters on Open --- serial_windows.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/serial_windows.go b/serial_windows.go index 287856a..35bb0c9 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -254,6 +254,15 @@ func (port *windowsPort) SetMode(mode *Mode) error { port.Close() return &PortError{code: InvalidSerialPort} } + port.setModeParams(mode, ¶ms) + if setCommState(port.handle, ¶ms) != nil { + port.Close() + return &PortError{code: InvalidSerialPort} + } + return nil +} + +func (port *windowsPort) setModeParams(mode *Mode, params *dcb) { if mode.BaudRate == 0 { params.BaudRate = 9600 // Default to 9600 } else { @@ -266,11 +275,6 @@ func (port *windowsPort) SetMode(mode *Mode) error { } params.StopBits = stopBitsMap[mode.StopBits] params.Parity = parityMap[mode.Parity] - if setCommState(port.handle, ¶ms) != nil { - port.Close() - return &PortError{code: InvalidSerialPort} - } - return nil } func (port *windowsPort) SetDTR(dtr bool) error { @@ -432,16 +436,12 @@ func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { } // Set port parameters - if port.SetMode(mode) != nil { - port.Close() - return nil, &PortError{code: InvalidSerialPort} - } - params := &dcb{} if getCommState(port.handle, params) != nil { port.Close() return nil, &PortError{code: InvalidSerialPort} } + port.setModeParams(mode, params) params.Flags &= dcbDTRControlDisableMask params.Flags &= dcbRTSControlDisbaleMask if mode.InitialStatusBits == nil { From 9aae282f09e92dadad494f174b9b347362c5e9e9 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Fri, 16 Feb 2024 14:17:53 +0100 Subject: [PATCH 155/181] Improved error messages --- serial_unix.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/serial_unix.go b/serial_unix.go index db6158d..e025d2d 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -9,6 +9,7 @@ package serial import ( + "fmt" "io/ioutil" "regexp" "strings" @@ -232,7 +233,7 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { settings, err := port.getTermSettings() if err != nil { port.Close() - return nil, &PortError{code: InvalidSerialPort} + return nil, &PortError{code: InvalidSerialPort, causedBy: fmt.Errorf("error getting term settings: %w", err)} } // Set raw mode @@ -243,14 +244,14 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { if port.setTermSettings(settings) != nil { port.Close() - return nil, &PortError{code: InvalidSerialPort} + return nil, &PortError{code: InvalidSerialPort, causedBy: fmt.Errorf("error setting term settings: %w", err)} } if mode.InitialStatusBits != nil { status, err := port.getModemBitsStatus() if err != nil { port.Close() - return nil, &PortError{code: InvalidSerialPort, causedBy: err} + return nil, &PortError{code: InvalidSerialPort, causedBy: fmt.Errorf("error getting modem bits status: %w", err)} } if mode.InitialStatusBits.DTR { status |= unix.TIOCM_DTR @@ -264,15 +265,15 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { } if err := port.setModemBitsStatus(status); err != nil { port.Close() - return nil, &PortError{code: InvalidSerialPort, causedBy: err} + return nil, &PortError{code: InvalidSerialPort, causedBy: fmt.Errorf("error setting modem bits status: %w", err)} } } // MacOSX require that this operation is the last one otherwise an // 'Invalid serial port' error is returned... don't know why... - if port.SetMode(mode) != nil { + if err := port.SetMode(mode); err != nil { port.Close() - return nil, &PortError{code: InvalidSerialPort} + return nil, &PortError{code: InvalidSerialPort, causedBy: fmt.Errorf("error configuring port: %w", err)} } unix.SetNonblock(h, false) @@ -283,7 +284,7 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { pipe := &unixutils.Pipe{} if err := pipe.Open(); err != nil { port.Close() - return nil, &PortError{code: InvalidSerialPort, causedBy: err} + return nil, &PortError{code: InvalidSerialPort, causedBy: fmt.Errorf("error opening signaling pipe: %w", err)} } port.closeSignal = pipe From 7f490f208a51997efee4c8d6091857a0c572b5fc Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Sun, 18 Feb 2024 19:33:23 +0100 Subject: [PATCH 156/181] Added issues templates --- .github/ISSUE_TEMPLATE/bug-report.yml | 77 ++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature-request.yml | 29 ++++++++ 2 files changed, 106 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug-report.yml create mode 100644 .github/ISSUE_TEMPLATE/feature-request.yml diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 0000000..e3130ae --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,77 @@ +# Source: https://github.com/arduino/tooling-project-assets/blob/main/issue-templates/forms/platform-dependent/bug-report.yml +# See: https://docs.github.com/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms + +name: Bug report +description: Report a problem with the code or documentation in this repository. +labels: + - bug +body: + - type: textarea + id: description + attributes: + label: Describe the problem + validations: + required: true + - type: textarea + id: reproduce + attributes: + label: To reproduce + description: | + Provide the specific set of steps we can follow to reproduce the + problem in particular the exact golang source code you used. + validations: + required: true + - type: checkboxes + id: checklist-reproduce + attributes: + label: | + Please double-check that you have reported each of the following + before submitting the issue. + options: + - label: I've provided the FULL source code that causes the problem + required: true + - label: I've provided all the actions required to reproduce the problem + required: true + - type: textarea + id: expected + attributes: + label: Expected behavior + description: | + What would you expect to happen after following those instructions? + validations: + required: true + - type: input + id: os + attributes: + label: Operating system and version + description: | + Which operating system(s) version are you using on your computer? + validations: + required: true + - type: input + id: boards + attributes: + label: Arduino boards or embedded devices used + description: | + Which Arduino boards or embedded devices are you using (if applicable)? + - type: textarea + id: additional + attributes: + label: Additional context + description: | + Add here any additional information that you think might be relevant to + the problem. + validations: + required: false + - type: checkboxes + id: checklist + attributes: + label: Issue checklist + description: | + Please double-check that you have done each of the following things before + submitting the issue. + options: + - label: I searched for previous requests in [the issue tracker](https://github.com/bugst/go-serial/issues) + required: true + - label: My request contains all necessary details + required: true diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 0000000..d56393d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,29 @@ +# Source: https://github.com/arduino/tooling-project-assets/blob/main/issue-templates/forms/platform-dependent/feature-request.yml +# See: https://docs.github.com/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-issue-forms + +name: Feature request +description: Suggest an enhancement to this project. +labels: + - "type: enhancement" +body: + - type: textarea + id: description + attributes: + label: Describe the new feature or change suggestion + validations: + required: true + - type: textarea + id: additional + attributes: + label: Additional context + description: Add any additional information about the feature request here. + - type: checkboxes + id: checklist + attributes: + label: Issue checklist + description: Please double-check that you have done each of the following things before submitting the issue. + options: + - label: I searched for previous requests in [the issue tracker](https://github.com/bugst/go-serial/issues) + required: true + - label: My request contains all necessary details + required: true From 2aa105e32e2b1368eb0b861072af3b25dcf43cb8 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 20 Feb 2024 09:51:06 +0100 Subject: [PATCH 157/181] Improved issue template message --- .github/ISSUE_TEMPLATE/bug-report.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index e3130ae..4fe6566 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -51,9 +51,10 @@ body: - type: input id: boards attributes: - label: Arduino boards or embedded devices used + label: Please describe your hardware setup description: | - Which Arduino boards or embedded devices are you using (if applicable)? + Arduino boards, USB dongles, hubs or embedded devices you are using and how they + are connected together. - type: textarea id: additional attributes: From 674fbae95abf6b5974bf47a602ed81ff4384d54d Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 20 Feb 2024 09:52:27 +0100 Subject: [PATCH 158/181] Upgraded dependencies --- go.mod | 8 ++++---- go.sum | 20 ++++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index c3abe90..61d0d6d 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,12 @@ go 1.17 require ( github.com/creack/goselect v0.1.2 - github.com/stretchr/testify v1.7.0 - golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 + github.com/stretchr/testify v1.8.4 + golang.org/x/sys v0.17.0 ) require ( - github.com/davecgh/go-spew v1.1.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index dac8e08..43d4dac 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,21 @@ github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0= github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 92703ecb02e5efc412e181c4df73afbfbf8ebeae Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 20 Feb 2024 09:55:49 +0100 Subject: [PATCH 159/181] Improved issue template message --- .github/ISSUE_TEMPLATE/bug-report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index 4fe6566..b544840 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -48,7 +48,7 @@ body: Which operating system(s) version are you using on your computer? validations: required: true - - type: input + - type: textarea id: boards attributes: label: Please describe your hardware setup From bcd8695df44541e722ae4576f7be12ffd3320071 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 20 Feb 2024 15:13:12 +0100 Subject: [PATCH 160/181] Removed deprecated package io/ioutil --- serial_unix.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serial_unix.go b/serial_unix.go index e025d2d..9328656 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -10,7 +10,7 @@ package serial import ( "fmt" - "io/ioutil" + "os" "regexp" "strings" "sync" @@ -292,7 +292,7 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { } func nativeGetPortsList() ([]string, error) { - files, err := ioutil.ReadDir(devFolder) + files, err := os.ReadDir(devFolder) if err != nil { return nil, err } From 14e5ea68ce7e08e3fc27ff237e66d41ed3e502ae Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 20 Feb 2024 15:24:08 +0100 Subject: [PATCH 161/181] Precompile port-filter regexp --- serial_darwin.go | 9 +++++++-- serial_freebsd.go | 9 +++++++-- serial_linux.go | 9 +++++++-- serial_openbsd.go | 9 +++++++-- serial_unix.go | 7 +------ 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/serial_darwin.go b/serial_darwin.go index 2817041..3baa489 100644 --- a/serial_darwin.go +++ b/serial_darwin.go @@ -6,10 +6,15 @@ package serial -import "golang.org/x/sys/unix" +import ( + "regexp" + + "golang.org/x/sys/unix" +) const devFolder = "/dev" -const regexFilter = "^(cu|tty)\\..*" + +var osPortFiler = regexp.MustCompile("^(cu|tty)\\..*") const ioctlTcgetattr = unix.TIOCGETA const ioctlTcsetattr = unix.TIOCSETA diff --git a/serial_freebsd.go b/serial_freebsd.go index aceb08a..be0fa01 100644 --- a/serial_freebsd.go +++ b/serial_freebsd.go @@ -6,10 +6,15 @@ package serial -import "golang.org/x/sys/unix" +import ( + "regexp" + + "golang.org/x/sys/unix" +) const devFolder = "/dev" -const regexFilter = "^(cu|tty)\\..*" + +var osPortFiler = regexp.MustCompile("^(cu|tty)\\..*") // termios manipulation functions diff --git a/serial_linux.go b/serial_linux.go index fe114d9..93482cd 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -6,10 +6,15 @@ package serial -import "golang.org/x/sys/unix" +import ( + "regexp" + + "golang.org/x/sys/unix" +) const devFolder = "/dev" -const regexFilter = "(ttyS|ttyHS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO|ttymxc)[0-9]{1,3}" + +var osPortFilter = regexp.MustCompile("(ttyS|ttyHS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO|ttymxc)[0-9]{1,3}") // termios manipulation functions diff --git a/serial_openbsd.go b/serial_openbsd.go index 644dc92..e56a760 100644 --- a/serial_openbsd.go +++ b/serial_openbsd.go @@ -6,10 +6,15 @@ package serial -import "golang.org/x/sys/unix" +import ( + "regexp" + + "golang.org/x/sys/unix" +) const devFolder = "/dev" -const regexFilter = "^(cu|tty)\\..*" + +var osPortFiler = regexp.MustCompile("^(cu|tty)\\..*") // termios manipulation functions diff --git a/serial_unix.go b/serial_unix.go index 9328656..dffdcb4 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -11,7 +11,6 @@ package serial import ( "fmt" "os" - "regexp" "strings" "sync" "sync/atomic" @@ -298,10 +297,6 @@ func nativeGetPortsList() ([]string, error) { } ports := make([]string, 0, len(files)) - regex, err := regexp.Compile(regexFilter) - if err != nil { - return nil, err - } for _, f := range files { // Skip folders if f.IsDir() { @@ -309,7 +304,7 @@ func nativeGetPortsList() ([]string, error) { } // Keep only devices with the correct name - if !regex.MatchString(f.Name()) { + if !osPortFilter.MatchString(f.Name()) { continue } From 572f392ca94f20292ea48ab1e3884a325efa4487 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 20 Feb 2024 15:27:44 +0100 Subject: [PATCH 162/181] Updated license year --- LICENSE | 2 +- doc.go | 2 +- enumerator/doc.go | 2 +- enumerator/enumerator.go | 2 +- enumerator/example_getdetailedportlist_test.go | 2 +- enumerator/usb_darwin.go | 2 +- enumerator/usb_freebsd.go | 2 +- enumerator/usb_linux.go | 2 +- enumerator/usb_openbsd.go | 2 +- enumerator/usb_windows.go | 2 +- enumerator/usb_windows_test.go | 2 +- example_getportlist_test.go | 2 +- example_modem_bits_test.go | 2 +- example_serialport_test.go | 2 +- example_test.go | 2 +- portlist/portlist.go | 2 +- serial.go | 2 +- serial_bsd.go | 2 +- serial_darwin.go | 2 +- serial_darwin_386.go | 2 +- serial_darwin_64.go | 2 +- serial_freebsd.go | 2 +- serial_linux.go | 2 +- serial_linux_test.go | 2 +- serial_openbsd.go | 2 +- serial_resetbuf_linux_bsd.go | 2 +- serial_specialbaudrate_linux.go | 2 +- serial_specialbaudrate_linux_ppc64le.go | 2 +- serial_unix.go | 2 +- serial_windows.go | 2 +- syscall_windows.go | 2 +- unixutils/pipe.go | 2 +- unixutils/select.go | 2 +- 33 files changed, 33 insertions(+), 33 deletions(-) diff --git a/LICENSE b/LICENSE index e34cbff..5aca55c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ -Copyright (c) 2014-2023, Cristian Maglie. +Copyright (c) 2014-2024, Cristian Maglie. All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/doc.go b/doc.go index 6e230ff..360f0cc 100644 --- a/doc.go +++ b/doc.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/doc.go b/enumerator/doc.go index a3f887d..5d27f00 100644 --- a/enumerator/doc.go +++ b/enumerator/doc.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/enumerator.go b/enumerator/enumerator.go index 420f451..2bd5043 100644 --- a/enumerator/enumerator.go +++ b/enumerator/enumerator.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/example_getdetailedportlist_test.go b/enumerator/example_getdetailedportlist_test.go index d2796fb..6d3c2db 100644 --- a/enumerator/example_getdetailedportlist_test.go +++ b/enumerator/example_getdetailedportlist_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_darwin.go b/enumerator/usb_darwin.go index d9adb1b..1f51c47 100644 --- a/enumerator/usb_darwin.go +++ b/enumerator/usb_darwin.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_freebsd.go b/enumerator/usb_freebsd.go index db5d96c..553c5a0 100644 --- a/enumerator/usb_freebsd.go +++ b/enumerator/usb_freebsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_linux.go b/enumerator/usb_linux.go index f756367..2e3737e 100644 --- a/enumerator/usb_linux.go +++ b/enumerator/usb_linux.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_openbsd.go b/enumerator/usb_openbsd.go index db5d96c..553c5a0 100644 --- a/enumerator/usb_openbsd.go +++ b/enumerator/usb_openbsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_windows.go b/enumerator/usb_windows.go index 7883e45..c8018f9 100644 --- a/enumerator/usb_windows.go +++ b/enumerator/usb_windows.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/enumerator/usb_windows_test.go b/enumerator/usb_windows_test.go index 3160ef3..3d39e0a 100644 --- a/enumerator/usb_windows_test.go +++ b/enumerator/usb_windows_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/example_getportlist_test.go b/example_getportlist_test.go index beb4773..a1756ac 100644 --- a/example_getportlist_test.go +++ b/example_getportlist_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/example_modem_bits_test.go b/example_modem_bits_test.go index 34b212f..cc6969d 100644 --- a/example_modem_bits_test.go +++ b/example_modem_bits_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/example_serialport_test.go b/example_serialport_test.go index 12f68b5..b56fd7f 100644 --- a/example_serialport_test.go +++ b/example_serialport_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/example_test.go b/example_test.go index 27430d0..7ad5eef 100644 --- a/example_test.go +++ b/example_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/portlist/portlist.go b/portlist/portlist.go index 8c36e5c..845a6c9 100644 --- a/portlist/portlist.go +++ b/portlist/portlist.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial.go b/serial.go index abfd7f9..8e3ce89 100644 --- a/serial.go +++ b/serial.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_bsd.go b/serial_bsd.go index fb7c849..0368d18 100644 --- a/serial_bsd.go +++ b/serial_bsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_darwin.go b/serial_darwin.go index 3baa489..2398ea5 100644 --- a/serial_darwin.go +++ b/serial_darwin.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_darwin_386.go b/serial_darwin_386.go index 3594a6c..f3842b5 100644 --- a/serial_darwin_386.go +++ b/serial_darwin_386.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_darwin_64.go b/serial_darwin_64.go index d1ae7c4..c0ed76c 100644 --- a/serial_darwin_64.go +++ b/serial_darwin_64.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_freebsd.go b/serial_freebsd.go index be0fa01..591dc4b 100644 --- a/serial_freebsd.go +++ b/serial_freebsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_linux.go b/serial_linux.go index 93482cd..a1218bb 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_linux_test.go b/serial_linux_test.go index 1a7e1a3..4a76fbe 100644 --- a/serial_linux_test.go +++ b/serial_linux_test.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_openbsd.go b/serial_openbsd.go index e56a760..3b396c1 100644 --- a/serial_openbsd.go +++ b/serial_openbsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_resetbuf_linux_bsd.go b/serial_resetbuf_linux_bsd.go index 105669d..9e24604 100644 --- a/serial_resetbuf_linux_bsd.go +++ b/serial_resetbuf_linux_bsd.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_specialbaudrate_linux.go b/serial_specialbaudrate_linux.go index ff48974..4e55fdb 100644 --- a/serial_specialbaudrate_linux.go +++ b/serial_specialbaudrate_linux.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_specialbaudrate_linux_ppc64le.go b/serial_specialbaudrate_linux_ppc64le.go index cd80b86..b31d6c5 100644 --- a/serial_specialbaudrate_linux_ppc64le.go +++ b/serial_specialbaudrate_linux_ppc64le.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_unix.go b/serial_unix.go index dffdcb4..54e55a8 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/serial_windows.go b/serial_windows.go index 35bb0c9..e350f86 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/syscall_windows.go b/syscall_windows.go index c814528..6a95dcf 100644 --- a/syscall_windows.go +++ b/syscall_windows.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/unixutils/pipe.go b/unixutils/pipe.go index 748de34..c11e0d1 100644 --- a/unixutils/pipe.go +++ b/unixutils/pipe.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // diff --git a/unixutils/select.go b/unixutils/select.go index 9f0e214..42cacc3 100644 --- a/unixutils/select.go +++ b/unixutils/select.go @@ -1,5 +1,5 @@ // -// Copyright 2014-2023 Cristian Maglie. All rights reserved. +// Copyright 2014-2024 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // From 259bdeb6c725b44fa27b6f927d9a3c5a868d09fd Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 20 Feb 2024 15:57:58 +0100 Subject: [PATCH 163/181] Fixed typo --- serial_darwin.go | 2 +- serial_freebsd.go | 2 +- serial_openbsd.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/serial_darwin.go b/serial_darwin.go index 2398ea5..a87458e 100644 --- a/serial_darwin.go +++ b/serial_darwin.go @@ -14,7 +14,7 @@ import ( const devFolder = "/dev" -var osPortFiler = regexp.MustCompile("^(cu|tty)\\..*") +var osPortFilter = regexp.MustCompile("^(cu|tty)\\..*") const ioctlTcgetattr = unix.TIOCGETA const ioctlTcsetattr = unix.TIOCSETA diff --git a/serial_freebsd.go b/serial_freebsd.go index 591dc4b..775b7a6 100644 --- a/serial_freebsd.go +++ b/serial_freebsd.go @@ -14,7 +14,7 @@ import ( const devFolder = "/dev" -var osPortFiler = regexp.MustCompile("^(cu|tty)\\..*") +var osPortFilter = regexp.MustCompile("^(cu|tty)\\..*") // termios manipulation functions diff --git a/serial_openbsd.go b/serial_openbsd.go index 3b396c1..ad5efd0 100644 --- a/serial_openbsd.go +++ b/serial_openbsd.go @@ -14,7 +14,7 @@ import ( const devFolder = "/dev" -var osPortFiler = regexp.MustCompile("^(cu|tty)\\..*") +var osPortFilter = regexp.MustCompile("^(cu|tty)\\..*") // termios manipulation functions From 42bc112d18a52424bbf6546a4e0af53f5158bdcc Mon Sep 17 00:00:00 2001 From: Christian Stewart Date: Tue, 20 Feb 2024 15:46:44 -0800 Subject: [PATCH 164/181] fix: add shims for GOARCH=wasm with GOOS=js and GOOS=wasip1 Fixes build errors: GOOS=js GOARCH=wasm go build GOOS=wasip1 GOARCH=wasm go build Signed-off-by: Christian Stewart --- enumerator/usb_wasm.go | 11 +++++++++++ enumerator_wasm.go | 15 +++++++++++++++ serial_wasm.go | 15 +++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 enumerator/usb_wasm.go create mode 100644 enumerator_wasm.go create mode 100644 serial_wasm.go diff --git a/enumerator/usb_wasm.go b/enumerator/usb_wasm.go new file mode 100644 index 0000000..3f3b22f --- /dev/null +++ b/enumerator/usb_wasm.go @@ -0,0 +1,11 @@ +// +// Copyright 2014-2024 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +package enumerator + +func nativeGetDetailedPortsList() ([]*PortDetails, error) { + return nil, &PortEnumerationError{} +} diff --git a/enumerator_wasm.go b/enumerator_wasm.go new file mode 100644 index 0000000..349d272 --- /dev/null +++ b/enumerator_wasm.go @@ -0,0 +1,15 @@ +// +// Copyright 2014-2024 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +package serial + +import ( + "errors" +) + +func nativeGetPortsList() ([]string, error) { + return nil, errors.New("nativeGetPortsList is not supported on wasm") +} diff --git a/serial_wasm.go b/serial_wasm.go new file mode 100644 index 0000000..b2369f1 --- /dev/null +++ b/serial_wasm.go @@ -0,0 +1,15 @@ +// +// Copyright 2014-2024 Cristian Maglie. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// + +package serial + +import ( + "errors" +) + +func nativeOpen(portName string, mode *Mode) (Port, error) { + return nil, errors.New("nativeOpen is not supported on wasm") +} From bac809c5a1a385fa544b29a7fcabe9c980c5a7fd Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Mon, 18 Mar 2024 22:25:55 +0100 Subject: [PATCH 165/181] Fixed minor documentation typos --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b05796c..c32fa18 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,17 @@ # go.bug.st/serial -A cross-platform serial library for go-lang. +A cross-platform serial port library for Go. ## Documentation and examples -See the godoc here: https://godoc.org/go.bug.st/serial +See the package documentation here: https://pkg.go.dev/go.bug.st/serial ## go.mod transition -This library now support `go.mod` with the import `go.bug.st/serial`. +This library supports `go.mod` with the import `go.bug.st/serial`. -If you came from the pre-`go.mod` era please update your import paths from `go.bug.st/serial.v1` to `go.bug.st/serial` to receive new updates. Anyway, the latest `v1` release should still be avaiable using the old import. +If you came from the pre-`go.mod` era please update your import paths from `go.bug.st/serial.v1` to `go.bug.st/serial` to receive updates. The latest `v1` release is still available using the old import path. ## Credits @@ -20,7 +20,7 @@ If you came from the pre-`go.mod` era please update your import paths from `go.b ## License -The software is release under a [BSD 3-clause license] +This software is released under the [BSD 3-clause license]. [contributors]: https://github.com/bugst/go-serial/graphs/contributors [BSD 3-clause license]: https://github.com/bugst/go-serial/blob/master/LICENSE From 671075c6acc7965eca6d30a0ba4020683d0e6667 Mon Sep 17 00:00:00 2001 From: Andreas Deininger Date: Mon, 1 Apr 2024 16:04:53 +0200 Subject: [PATCH 166/181] Go-format source code --- portlist/portlist.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/portlist/portlist.go b/portlist/portlist.go index 845a6c9..5d58409 100644 --- a/portlist/portlist.go +++ b/portlist/portlist.go @@ -12,7 +12,7 @@ // Port: /dev/cu.usbmodemFD121 // USB ID 2341:8053 // USB serial FB7B6060504B5952302E314AFF08191A -// + package main import ( From 03c961bc8afed1fd4bd8043607d1441dee5b83e6 Mon Sep 17 00:00:00 2001 From: Andre Renaud Date: Tue, 28 May 2024 09:10:28 +1200 Subject: [PATCH 167/181] Workflows: Use CGO_ENABLED=1 for MacOS Also enable explicit ARM64 builds --- .github/workflows/test.yaml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 7a43621..01c4691 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -19,9 +19,18 @@ jobs: - uses: actions/setup-go@v1 with: go-version: "1.17" - - name: Build native + - name: Setup CGO Environment + run: | + if [ ${{ matrix.os }} == 'macOS-latest' ] ; then + echo "CGO_ENABLED=1" >> "$GITHUB_ENV" + fi + shell: bash + - name: Build AMD64 run: GOARCH=amd64 go build -v ./... shell: bash + - name: Build ARM64 + run: GOARCH=arm64 go build -v ./... + shell: bash - name: Install socat if: matrix.os == 'ubuntu-latest' run: sudo apt-get install socat From 1282f62c6e37fb2eed3520f9cf65bfd93dfb5a83 Mon Sep 17 00:00:00 2001 From: Andreas Deininger Date: Mon, 24 Jun 2024 17:24:35 +0200 Subject: [PATCH 168/181] Bump GitHub workflow actions to latest versions (#185) * Bump GitHub workflow actions to latest versions * Fixed typo --------- Co-authored-by: Cristian Maglie --- .github/workflows/test.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 01c4691..bfacab5 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -15,10 +15,10 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v1 - - uses: actions/setup-go@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 with: - go-version: "1.17" + go-version: "1.22" - name: Setup CGO Environment run: | if [ ${{ matrix.os }} == 'macOS-latest' ] ; then @@ -60,10 +60,10 @@ jobs: runs-on: "ubuntu-latest" steps: - - uses: actions/checkout@v1 - - uses: actions/setup-go@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 with: - go-version: "1.17" + go-version: "1.22" - name: Cross-build run: | set ${{ matrix.go-os-pairs }} From 0b7848559acc54148cec234c28eb7f88612673d6 Mon Sep 17 00:00:00 2001 From: Andreas Deininger Date: Mon, 24 Jun 2024 17:33:18 +0200 Subject: [PATCH 169/181] Fix typos (#183) Co-authored-by: Cristian Maglie --- enumerator/usb_windows.go | 2 +- serial.go | 2 +- serial_windows.go | 10 +++++----- unixutils/pipe.go | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/enumerator/usb_windows.go b/enumerator/usb_windows.go index c8018f9..2d3793b 100644 --- a/enumerator/usb_windows.go +++ b/enumerator/usb_windows.go @@ -98,7 +98,7 @@ const ( spdrpUpperFilters = 0x00000011 // UpperFilters = R/W spdrpLowerFilters = 0x00000012 // LowerFilters = R/W spdrpBusTypeGUID = 0x00000013 // BusTypeGUID = R - spdrpLegactBusType = 0x00000014 // LegacyBusType = R + spdrpLegacyBusType = 0x00000014 // LegacyBusType = R spdrpBusNumber = 0x00000015 // BusNumber = R spdrpEnumeratorName = 0x00000016 // Enumerator Name = R spdrpSecurity = 0x00000017 // Security = R/W, binary form diff --git a/serial.go b/serial.go index 8e3ce89..a2f7333 100644 --- a/serial.go +++ b/serial.go @@ -147,7 +147,7 @@ const ( PortNotFound // InvalidSerialPort the requested port is not a serial port InvalidSerialPort - // PermissionDenied the user doesn't have enough priviledges + // PermissionDenied the user doesn't have enough privileges PermissionDenied // InvalidSpeed the requested speed is not valid or not supported InvalidSpeed diff --git a/serial_windows.go b/serial_windows.go index e350f86..b2c9009 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -152,7 +152,7 @@ const ( dcbInX = 0x00000200 dcbErrorChar = 0x00000400 dcbNull = 0x00000800 - dcbRTSControlDisbaleMask = ^uint32(0x00003000) + dcbRTSControlDisableMask = ^uint32(0x00003000) dcbRTSControlEnable = 0x00001000 dcbRTSControlHandshake = 0x00002000 dcbRTSControlToggle = 0x00003000 @@ -282,8 +282,8 @@ func (port *windowsPort) SetDTR(dtr bool) error { // observed behaviour was that DTR is set from false -> true // when setting RTS from true -> false // 1) Connect -> RTS = true (low) DTR = true (low) OKAY - // 2) SetDTR(false) -> RTS = true (low) DTR = false (heigh) OKAY - // 3) SetRTS(false) -> RTS = false (heigh) DTR = true (low) ERROR: DTR toggled + // 2) SetDTR(false) -> RTS = true (low) DTR = false (high) OKAY + // 3) SetRTS(false) -> RTS = false (high) DTR = true (low) ERROR: DTR toggled // // In addition this way the CommState Flags are not updated /* @@ -343,7 +343,7 @@ func (port *windowsPort) SetRTS(rts bool) error { if err := getCommState(port.handle, params); err != nil { return &PortError{causedBy: err} } - params.Flags &= dcbRTSControlDisbaleMask + params.Flags &= dcbRTSControlDisableMask if rts { params.Flags |= dcbRTSControlEnable } @@ -443,7 +443,7 @@ func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { } port.setModeParams(mode, params) params.Flags &= dcbDTRControlDisableMask - params.Flags &= dcbRTSControlDisbaleMask + params.Flags &= dcbRTSControlDisableMask if mode.InitialStatusBits == nil { params.Flags |= dcbDTRControlEnable params.Flags |= dcbRTSControlEnable diff --git a/unixutils/pipe.go b/unixutils/pipe.go index c11e0d1..f54c916 100644 --- a/unixutils/pipe.go +++ b/unixutils/pipe.go @@ -40,7 +40,7 @@ func (p *Pipe) ReadFD() int { return p.rd } -// WriteFD returns the flie handle for the write side of the pipe. +// WriteFD returns the file handle for the write side of the pipe. func (p *Pipe) WriteFD() int { if !p.opened { return -1 @@ -48,7 +48,7 @@ func (p *Pipe) WriteFD() int { return p.wr } -// Write to the pipe the content of data. Returns the numbre of bytes written. +// Write to the pipe the content of data. Returns the number of bytes written. func (p *Pipe) Write(data []byte) (int, error) { if !p.opened { return 0, fmt.Errorf("Pipe not opened") From 45e996e1b06fb0d93f64a09d13f27d0859b19000 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Tue, 9 Apr 2024 12:51:09 +0200 Subject: [PATCH 170/181] Use Windows serial comm functions from golang.org/x/sys/windows --- go.mod | 2 +- go.sum | 4 +- serial_windows.go | 261 ++++++++++++++------------------------------ syscall_windows.go | 31 ------ zsyscall_windows.go | 161 --------------------------- 5 files changed, 84 insertions(+), 375 deletions(-) delete mode 100644 syscall_windows.go delete mode 100644 zsyscall_windows.go diff --git a/go.mod b/go.mod index 61d0d6d..e89755e 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/creack/goselect v0.1.2 github.com/stretchr/testify v1.8.4 - golang.org/x/sys v0.17.0 + golang.org/x/sys v0.19.0 ) require ( diff --git a/go.sum b/go.sum index 43d4dac..4cef3dc 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/serial_windows.go b/serial_windows.go index b2c9009..a7f9a12 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -19,46 +19,29 @@ package serial import ( "sync" - "syscall" "time" + + "golang.org/x/sys/windows" + "golang.org/x/sys/windows/registry" ) type windowsPort struct { mu sync.Mutex - handle syscall.Handle + handle windows.Handle } func nativeGetPortsList() ([]string, error) { - subKey, err := syscall.UTF16PtrFromString("HARDWARE\\DEVICEMAP\\SERIALCOMM\\") + key, err := registry.OpenKey(windows.HKEY_LOCAL_MACHINE, `HARDWARE\DEVICEMAP\SERIALCOMM\`, windows.KEY_READ) if err != nil { - return nil, &PortError{code: ErrorEnumeratingPorts} - } - - var h syscall.Handle - if err := syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE, subKey, 0, syscall.KEY_READ, &h); err != nil { - if errno, isErrno := err.(syscall.Errno); isErrno && errno == syscall.ERROR_FILE_NOT_FOUND { - return []string{}, nil - } - return nil, &PortError{code: ErrorEnumeratingPorts} + return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} } - defer syscall.RegCloseKey(h) + defer key.Close() - var valuesCount uint32 - if syscall.RegQueryInfoKey(h, nil, nil, nil, nil, nil, nil, &valuesCount, nil, nil, nil, nil) != nil { - return nil, &PortError{code: ErrorEnumeratingPorts} + list, err := key.ReadValueNames(0) + if err != nil { + return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} } - list := make([]string, valuesCount) - for i := range list { - var data [1024]uint16 - dataSize := uint32(len(data)) - var name [1024]uint16 - nameSize := uint32(len(name)) - if regEnumValue(h, uint32(i), &name[0], &nameSize, nil, nil, &data[0], &dataSize) != nil { - return nil, &PortError{code: ErrorEnumeratingPorts} - } - list[i] = syscall.UTF16ToString(data[:]) - } return list, nil } @@ -71,7 +54,7 @@ func (port *windowsPort) Close() error { if port.handle == 0 { return nil } - return syscall.CloseHandle(port.handle) + return windows.CloseHandle(port.handle) } func (port *windowsPort) Read(p []byte) (int, error) { @@ -80,16 +63,16 @@ func (port *windowsPort) Read(p []byte) (int, error) { if err != nil { return 0, err } - defer syscall.CloseHandle(ev.HEvent) + defer windows.CloseHandle(ev.HEvent) - err = syscall.ReadFile(port.handle, p, &readed, ev) - if err == syscall.ERROR_IO_PENDING { - err = getOverlappedResult(port.handle, ev, &readed, true) + err = windows.ReadFile(port.handle, p, &readed, ev) + if err == windows.ERROR_IO_PENDING { + err = windows.GetOverlappedResult(port.handle, ev, &readed, true) } switch err { case nil: // operation completed successfully - case syscall.ERROR_OPERATION_ABORTED: + case windows.ERROR_OPERATION_ABORTED: // port may have been closed return int(readed), &PortError{code: PortClosed, causedBy: err} default: @@ -110,32 +93,25 @@ func (port *windowsPort) Write(p []byte) (int, error) { if err != nil { return 0, err } - defer syscall.CloseHandle(ev.HEvent) - err = syscall.WriteFile(port.handle, p, &writed, ev) - if err == syscall.ERROR_IO_PENDING { + defer windows.CloseHandle(ev.HEvent) + err = windows.WriteFile(port.handle, p, &writed, ev) + if err == windows.ERROR_IO_PENDING { // wait for write to complete - err = getOverlappedResult(port.handle, ev, &writed, true) + err = windows.GetOverlappedResult(port.handle, ev, &writed, true) } return int(writed), err } func (port *windowsPort) Drain() (err error) { - return syscall.FlushFileBuffers(port.handle) + return windows.FlushFileBuffers(port.handle) } -const ( - purgeRxAbort uint32 = 0x0002 - purgeRxClear = 0x0008 - purgeTxAbort = 0x0001 - purgeTxClear = 0x0004 -) - func (port *windowsPort) ResetInputBuffer() error { - return purgeComm(port.handle, purgeRxClear|purgeRxAbort) + return windows.PurgeComm(port.handle, windows.PURGE_RXCLEAR|windows.PURGE_RXABORT) } func (port *windowsPort) ResetOutputBuffer() error { - return purgeComm(port.handle, purgeTxClear|purgeTxAbort) + return windows.PurgeComm(port.handle, windows.PURGE_TXCLEAR|windows.PURGE_TXABORT) } const ( @@ -159,112 +135,37 @@ const ( dcbAbortOnError = 0x00004000 ) -type dcb struct { - DCBlength uint32 - BaudRate uint32 - - // Flags field is a bitfield - // fBinary :1 - // fParity :1 - // fOutxCtsFlow :1 - // fOutxDsrFlow :1 - // fDtrControl :2 - // fDsrSensitivity :1 - // fTXContinueOnXoff :1 - // fOutX :1 - // fInX :1 - // fErrorChar :1 - // fNull :1 - // fRtsControl :2 - // fAbortOnError :1 - // fDummy2 :17 - Flags uint32 - - wReserved uint16 - XonLim uint16 - XoffLim uint16 - ByteSize byte - Parity byte - StopBits byte - XonChar byte - XoffChar byte - ErrorChar byte - EOFChar byte - EvtChar byte - wReserved1 uint16 -} - -type commTimeouts struct { - ReadIntervalTimeout uint32 - ReadTotalTimeoutMultiplier uint32 - ReadTotalTimeoutConstant uint32 - WriteTotalTimeoutMultiplier uint32 - WriteTotalTimeoutConstant uint32 -} - -const ( - noParity = 0 - oddParity = 1 - evenParity = 2 - markParity = 3 - spaceParity = 4 -) - var parityMap = map[Parity]byte{ - NoParity: noParity, - OddParity: oddParity, - EvenParity: evenParity, - MarkParity: markParity, - SpaceParity: spaceParity, + NoParity: windows.NOPARITY, + OddParity: windows.ODDPARITY, + EvenParity: windows.EVENPARITY, + MarkParity: windows.MARKPARITY, + SpaceParity: windows.SPACEPARITY, } -const ( - oneStopBit = 0 - one5StopBits = 1 - twoStopBits = 2 -) - var stopBitsMap = map[StopBits]byte{ - OneStopBit: oneStopBit, - OnePointFiveStopBits: one5StopBits, - TwoStopBits: twoStopBits, + OneStopBit: windows.ONESTOPBIT, + OnePointFiveStopBits: windows.ONE5STOPBITS, + TwoStopBits: windows.TWOSTOPBITS, } -const ( - commFunctionSetXOFF = 1 - commFunctionSetXON = 2 - commFunctionSetRTS = 3 - commFunctionClrRTS = 4 - commFunctionSetDTR = 5 - commFunctionClrDTR = 6 - commFunctionSetBreak = 8 - commFunctionClrBreak = 9 -) - -const ( - msCTSOn = 0x0010 - msDSROn = 0x0020 - msRingOn = 0x0040 - msRLSDOn = 0x0080 -) - func (port *windowsPort) SetMode(mode *Mode) error { - params := dcb{} - if getCommState(port.handle, ¶ms) != nil { + params := windows.DCB{} + if windows.GetCommState(port.handle, ¶ms) != nil { port.Close() return &PortError{code: InvalidSerialPort} } port.setModeParams(mode, ¶ms) - if setCommState(port.handle, ¶ms) != nil { + if windows.SetCommState(port.handle, ¶ms) != nil { port.Close() return &PortError{code: InvalidSerialPort} } return nil } -func (port *windowsPort) setModeParams(mode *Mode, params *dcb) { +func (port *windowsPort) setModeParams(mode *Mode, params *windows.DCB) { if mode.BaudRate == 0 { - params.BaudRate = 9600 // Default to 9600 + params.BaudRate = windows.CBR_9600 // Default to 9600 } else { params.BaudRate = uint32(mode.BaudRate) } @@ -278,22 +179,22 @@ func (port *windowsPort) setModeParams(mode *Mode, params *dcb) { } func (port *windowsPort) SetDTR(dtr bool) error { - // Like for RTS there are problems with the escapeCommFunction + // Like for RTS there are problems with the windows.EscapeCommFunction // observed behaviour was that DTR is set from false -> true // when setting RTS from true -> false // 1) Connect -> RTS = true (low) DTR = true (low) OKAY - // 2) SetDTR(false) -> RTS = true (low) DTR = false (high) OKAY - // 3) SetRTS(false) -> RTS = false (high) DTR = true (low) ERROR: DTR toggled + // 2) SetDTR(false) -> RTS = true (low) DTR = false (heigh) OKAY + // 3) SetRTS(false) -> RTS = false (heigh) DTR = true (low) ERROR: DTR toggled // // In addition this way the CommState Flags are not updated /* - var res bool + var err error if dtr { - res = escapeCommFunction(port.handle, commFunctionSetDTR) + err = windows.EscapeCommFunction(port.handle, windows.SETDTR) } else { - res = escapeCommFunction(port.handle, commFunctionClrDTR) + err = windows.EscapeCommFunction(port.handle, windows.CLTDTR) } - if !res { + if err != nil { return &PortError{} } return nil @@ -301,15 +202,15 @@ func (port *windowsPort) SetDTR(dtr bool) error { // The following seems a more reliable way to do it - params := &dcb{} - if err := getCommState(port.handle, params); err != nil { + params := &windows.DCB{} + if err := windows.GetCommState(port.handle, params); err != nil { return &PortError{causedBy: err} } params.Flags &= dcbDTRControlDisableMask if dtr { - params.Flags |= dcbDTRControlEnable + params.Flags |= windows.DTR_CONTROL_ENABLE } - if err := setCommState(port.handle, params); err != nil { + if err := windows.SetCommState(port.handle, params); err != nil { return &PortError{causedBy: err} } @@ -325,13 +226,13 @@ func (port *windowsPort) SetRTS(rts bool) error { // In addition this way the CommState Flags are not updated /* - var res bool + var err error if rts { - res = escapeCommFunction(port.handle, commFunctionSetRTS) + err = windows.EscapeCommFunction(port.handle, windows.SETRTS) } else { - res = escapeCommFunction(port.handle, commFunctionClrRTS) + err = windows.EscapeCommFunction(port.handle, windows.CLRRTS) } - if !res { + if err != nil { return &PortError{} } return nil @@ -339,15 +240,15 @@ func (port *windowsPort) SetRTS(rts bool) error { // The following seems a more reliable way to do it - params := &dcb{} - if err := getCommState(port.handle, params); err != nil { + params := &windows.DCB{} + if err := windows.GetCommState(port.handle, params); err != nil { return &PortError{causedBy: err} } params.Flags &= dcbRTSControlDisableMask if rts { - params.Flags |= dcbRTSControlEnable + params.Flags |= windows.RTS_CONTROL_ENABLE } - if err := setCommState(port.handle, params); err != nil { + if err := windows.SetCommState(port.handle, params); err != nil { return &PortError{causedBy: err} } return nil @@ -355,19 +256,19 @@ func (port *windowsPort) SetRTS(rts bool) error { func (port *windowsPort) GetModemStatusBits() (*ModemStatusBits, error) { var bits uint32 - if !getCommModemStatus(port.handle, &bits) { + if err := windows.GetCommModemStatus(port.handle, &bits); err != nil { return nil, &PortError{} } return &ModemStatusBits{ - CTS: (bits & msCTSOn) != 0, - DCD: (bits & msRLSDOn) != 0, - DSR: (bits & msDSROn) != 0, - RI: (bits & msRingOn) != 0, + CTS: (bits & windows.EV_CTS) != 0, + DCD: (bits & windows.EV_RLSD) != 0, + DSR: (bits & windows.EV_DSR) != 0, + RI: (bits & windows.EV_RING) != 0, }, nil } func (port *windowsPort) SetReadTimeout(timeout time.Duration) error { - commTimeouts := &commTimeouts{ + commTimeouts := &windows.CommTimeouts{ ReadIntervalTimeout: 0xFFFFFFFF, ReadTotalTimeoutMultiplier: 0xFFFFFFFF, ReadTotalTimeoutConstant: 0xFFFFFFFE, @@ -382,7 +283,7 @@ func (port *windowsPort) SetReadTimeout(timeout time.Duration) error { commTimeouts.ReadTotalTimeoutConstant = uint32(ms) } - if err := setCommTimeouts(port.handle, commTimeouts); err != nil { + if err := windows.SetCommTimeouts(port.handle, commTimeouts); err != nil { return &PortError{code: InvalidTimeoutValue, causedBy: err} } @@ -390,42 +291,42 @@ func (port *windowsPort) SetReadTimeout(timeout time.Duration) error { } func (port *windowsPort) Break(d time.Duration) error { - if err := setCommBreak(port.handle); err != nil { + if err := windows.SetCommBreak(port.handle); err != nil { return &PortError{causedBy: err} } time.Sleep(d) - if err := clearCommBreak(port.handle); err != nil { + if err := windows.ClearCommBreak(port.handle); err != nil { return &PortError{causedBy: err} } return nil } -func createOverlappedEvent() (*syscall.Overlapped, error) { - h, err := createEvent(nil, true, false, nil) - return &syscall.Overlapped{HEvent: h}, err +func createOverlappedEvent() (*windows.Overlapped, error) { + h, err := windows.CreateEvent(nil, 1, 0, nil) + return &windows.Overlapped{HEvent: h}, err } func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { portName = "\\\\.\\" + portName - path, err := syscall.UTF16PtrFromString(portName) + path, err := windows.UTF16PtrFromString(portName) if err != nil { return nil, err } - handle, err := syscall.CreateFile( + handle, err := windows.CreateFile( path, - syscall.GENERIC_READ|syscall.GENERIC_WRITE, + windows.GENERIC_READ|windows.GENERIC_WRITE, 0, nil, - syscall.OPEN_EXISTING, - syscall.FILE_FLAG_OVERLAPPED, + windows.OPEN_EXISTING, + windows.FILE_FLAG_OVERLAPPED, 0) if err != nil { switch err { - case syscall.ERROR_ACCESS_DENIED: + case windows.ERROR_ACCESS_DENIED: return nil, &PortError{code: PortBusy} - case syscall.ERROR_FILE_NOT_FOUND: + case windows.ERROR_FILE_NOT_FOUND: return nil, &PortError{code: PortNotFound} } return nil, err @@ -436,8 +337,8 @@ func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { } // Set port parameters - params := &dcb{} - if getCommState(port.handle, params) != nil { + params := &windows.DCB{} + if windows.GetCommState(port.handle, params) != nil { port.Close() return nil, &PortError{code: InvalidSerialPort} } @@ -445,14 +346,14 @@ func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { params.Flags &= dcbDTRControlDisableMask params.Flags &= dcbRTSControlDisableMask if mode.InitialStatusBits == nil { - params.Flags |= dcbDTRControlEnable - params.Flags |= dcbRTSControlEnable + params.Flags |= windows.DTR_CONTROL_ENABLE + params.Flags |= windows.RTS_CONTROL_ENABLE } else { if mode.InitialStatusBits.DTR { - params.Flags |= dcbDTRControlEnable + params.Flags |= windows.DTR_CONTROL_ENABLE } if mode.InitialStatusBits.RTS { - params.Flags |= dcbRTSControlEnable + params.Flags |= windows.RTS_CONTROL_ENABLE } } params.Flags &^= dcbOutXCTSFlow @@ -468,7 +369,7 @@ func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { params.XoffLim = 512 params.XonChar = 17 // DC1 params.XoffChar = 19 // C3 - if setCommState(port.handle, params) != nil { + if windows.SetCommState(port.handle, params) != nil { port.Close() return nil, &PortError{code: InvalidSerialPort} } diff --git a/syscall_windows.go b/syscall_windows.go deleted file mode 100644 index 6a95dcf..0000000 --- a/syscall_windows.go +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright 2014-2024 Cristian Maglie. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// - -package serial - -//sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) = advapi32.RegEnumValueW - -//sys getCommState(handle syscall.Handle, dcb *dcb) (err error) = GetCommState - -//sys setCommState(handle syscall.Handle, dcb *dcb) (err error) = SetCommState - -//sys setCommTimeouts(handle syscall.Handle, timeouts *commTimeouts) (err error) = SetCommTimeouts - -//sys escapeCommFunction(handle syscall.Handle, function uint32) (res bool) = EscapeCommFunction - -//sys getCommModemStatus(handle syscall.Handle, bits *uint32) (res bool) = GetCommModemStatus - -//sys createEvent(eventAttributes *uint32, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) = CreateEventW - -//sys resetEvent(handle syscall.Handle) (err error) = ResetEvent - -//sys getOverlappedResult(handle syscall.Handle, overlapEvent *syscall.Overlapped, n *uint32, wait bool) (err error) = GetOverlappedResult - -//sys purgeComm(handle syscall.Handle, flags uint32) (err error) = PurgeComm - -//sys setCommBreak(handle syscall.Handle) (err error) = SetCommBreak - -//sys clearCommBreak(handle syscall.Handle) (err error) = ClearCommBreak diff --git a/zsyscall_windows.go b/zsyscall_windows.go deleted file mode 100644 index a2411a6..0000000 --- a/zsyscall_windows.go +++ /dev/null @@ -1,161 +0,0 @@ -// Code generated by 'go generate'; DO NOT EDIT. - -package serial - -import ( - "syscall" - "unsafe" - - "golang.org/x/sys/windows" -) - -var _ unsafe.Pointer - -// Do the interface allocations only once for common -// Errno values. -const ( - errnoERROR_IO_PENDING = 997 -) - -var ( - errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) - errERROR_EINVAL error = syscall.EINVAL -) - -// errnoErr returns common boxed Errno values, to prevent -// allocations at runtime. -func errnoErr(e syscall.Errno) error { - switch e { - case 0: - return errERROR_EINVAL - case errnoERROR_IO_PENDING: - return errERROR_IO_PENDING - } - // TODO: add more here, after collecting data on the common - // error values see on Windows. (perhaps when running - // all.bat?) - return e -} - -var ( - modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") - modkernel32 = windows.NewLazySystemDLL("kernel32.dll") - - procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") - procClearCommBreak = modkernel32.NewProc("ClearCommBreak") - procCreateEventW = modkernel32.NewProc("CreateEventW") - procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction") - procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus") - procGetCommState = modkernel32.NewProc("GetCommState") - procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") - procPurgeComm = modkernel32.NewProc("PurgeComm") - procResetEvent = modkernel32.NewProc("ResetEvent") - procSetCommBreak = modkernel32.NewProc("SetCommBreak") - procSetCommState = modkernel32.NewProc("SetCommState") - procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") -) - -func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) { - r0, _, _ := syscall.Syscall9(procRegEnumValueW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(class)), uintptr(unsafe.Pointer(value)), uintptr(unsafe.Pointer(valueLen)), 0) - if r0 != 0 { - regerrno = syscall.Errno(r0) - } - return -} - -func clearCommBreak(handle syscall.Handle) (err error) { - r1, _, e1 := syscall.Syscall(procClearCommBreak.Addr(), 1, uintptr(handle), 0, 0) - if r1 == 0 { - err = errnoErr(e1) - } - return -} - -func createEvent(eventAttributes *uint32, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { - var _p0 uint32 - if manualReset { - _p0 = 1 - } - var _p1 uint32 - if initialState { - _p1 = 1 - } - r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(eventAttributes)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) - handle = syscall.Handle(r0) - if handle == 0 { - err = errnoErr(e1) - } - return -} - -func escapeCommFunction(handle syscall.Handle, function uint32) (res bool) { - r0, _, _ := syscall.Syscall(procEscapeCommFunction.Addr(), 2, uintptr(handle), uintptr(function), 0) - res = r0 != 0 - return -} - -func getCommModemStatus(handle syscall.Handle, bits *uint32) (res bool) { - r0, _, _ := syscall.Syscall(procGetCommModemStatus.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(bits)), 0) - res = r0 != 0 - return -} - -func getCommState(handle syscall.Handle, dcb *dcb) (err error) { - r1, _, e1 := syscall.Syscall(procGetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0) - if r1 == 0 { - err = errnoErr(e1) - } - return -} - -func getOverlappedResult(handle syscall.Handle, overlapEvent *syscall.Overlapped, n *uint32, wait bool) (err error) { - var _p0 uint32 - if wait { - _p0 = 1 - } - r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapEvent)), uintptr(unsafe.Pointer(n)), uintptr(_p0), 0, 0) - if r1 == 0 { - err = errnoErr(e1) - } - return -} - -func purgeComm(handle syscall.Handle, flags uint32) (err error) { - r1, _, e1 := syscall.Syscall(procPurgeComm.Addr(), 2, uintptr(handle), uintptr(flags), 0) - if r1 == 0 { - err = errnoErr(e1) - } - return -} - -func resetEvent(handle syscall.Handle) (err error) { - r1, _, e1 := syscall.Syscall(procResetEvent.Addr(), 1, uintptr(handle), 0, 0) - if r1 == 0 { - err = errnoErr(e1) - } - return -} - -func setCommBreak(handle syscall.Handle) (err error) { - r1, _, e1 := syscall.Syscall(procSetCommBreak.Addr(), 1, uintptr(handle), 0, 0) - if r1 == 0 { - err = errnoErr(e1) - } - return -} - -func setCommState(handle syscall.Handle, dcb *dcb) (err error) { - r1, _, e1 := syscall.Syscall(procSetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0) - if r1 == 0 { - err = errnoErr(e1) - } - return -} - -func setCommTimeouts(handle syscall.Handle, timeouts *commTimeouts) (err error) { - r1, _, e1 := syscall.Syscall(procSetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0) - if r1 == 0 { - err = errnoErr(e1) - } - return -} From 1c72447e64a43ef0d6bfd1136f04b812096a1f56 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Tue, 25 Jun 2024 10:48:18 +0200 Subject: [PATCH 171/181] Fix typos Co-authored-by: Cristian Maglie --- serial_windows.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serial_windows.go b/serial_windows.go index a7f9a12..4751c91 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -183,8 +183,8 @@ func (port *windowsPort) SetDTR(dtr bool) error { // observed behaviour was that DTR is set from false -> true // when setting RTS from true -> false // 1) Connect -> RTS = true (low) DTR = true (low) OKAY - // 2) SetDTR(false) -> RTS = true (low) DTR = false (heigh) OKAY - // 3) SetRTS(false) -> RTS = false (heigh) DTR = true (low) ERROR: DTR toggled + // 2) SetDTR(false) -> RTS = true (low) DTR = false (high) OKAY + // 3) SetRTS(false) -> RTS = false (high) DTR = true (low) ERROR: DTR toggled // // In addition this way the CommState Flags are not updated /* From 56ac2d4e76afa2d2e789f33da60395ae184eccb1 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Tue, 25 Jun 2024 11:02:05 +0200 Subject: [PATCH 172/181] Restore check for no detected serial ports on Windows --- serial_windows.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/serial_windows.go b/serial_windows.go index 4751c91..5853e51 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -18,7 +18,9 @@ package serial */ import ( + "errors" "sync" + "syscall" "time" "golang.org/x/sys/windows" @@ -32,7 +34,12 @@ type windowsPort struct { func nativeGetPortsList() ([]string, error) { key, err := registry.OpenKey(windows.HKEY_LOCAL_MACHINE, `HARDWARE\DEVICEMAP\SERIALCOMM\`, windows.KEY_READ) - if err != nil { + switch { + case errors.Is(err, syscall.ERROR_FILE_NOT_FOUND): + // On machines with no serial ports the registry key does not exist. + // Return this as no serial ports instead of an error. + return nil, nil + case err != nil: return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} } defer key.Close() From f5a4685ea0112d5230d45141c606bb49cefc75a5 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Tue, 25 Jun 2024 23:52:38 +0200 Subject: [PATCH 173/181] Fixed examples for docs --- example_modem_bits_test.go | 2 +- example_serialport_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example_modem_bits_test.go b/example_modem_bits_test.go index cc6969d..eaad39a 100644 --- a/example_modem_bits_test.go +++ b/example_modem_bits_test.go @@ -14,7 +14,7 @@ import ( "go.bug.st/serial" ) -func ExampleGetSetModemBits() { +func ExamplePort_GetModemStatusBits() { // Open the first serial port detected at 9600bps N81 mode := &serial.Mode{ BaudRate: 9600, diff --git a/example_serialport_test.go b/example_serialport_test.go index b56fd7f..937b8b9 100644 --- a/example_serialport_test.go +++ b/example_serialport_test.go @@ -13,7 +13,7 @@ import ( "go.bug.st/serial" ) -func ExampleSerialPort_SetMode() { +func ExamplePort_SetMode() { port, err := serial.Open("/dev/ttyACM0", &serial.Mode{}) if err != nil { log.Fatal(err) From b7483e31a79c068ccdafa1bd5a39ec284e74c968 Mon Sep 17 00:00:00 2001 From: Dirk007 Date: Sat, 7 Sep 2024 13:26:01 +0200 Subject: [PATCH 174/181] Report the actual error instead of nil --- serial_unix.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serial_unix.go b/serial_unix.go index 54e55a8..e6913c2 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -241,7 +241,7 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { // Explicitly disable RTS/CTS flow control setTermSettingsCtsRts(false, settings) - if port.setTermSettings(settings) != nil { + if err = port.setTermSettings(settings); err != nil { port.Close() return nil, &PortError{code: InvalidSerialPort, causedBy: fmt.Errorf("error setting term settings: %w", err)} } From fb4b111d503ec814416fa6b4514c8460b2d12b77 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 12 Mar 2025 20:50:56 +0100 Subject: [PATCH 175/181] Hack for CH340 support --- serial_windows.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/serial_windows.go b/serial_windows.go index 5853e51..9ea64b6 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -275,10 +275,19 @@ func (port *windowsPort) GetModemStatusBits() (*ModemStatusBits, error) { } func (port *windowsPort) SetReadTimeout(timeout time.Duration) error { + // This is a brutal hack to make the CH340 chipset work properly. + // Normally this value should be 0xFFFFFFFE but, after a lot of + // tinkering, I discovered that any value with the highest + // bit set will make the CH340 driver behave like the timeout is 0, + // in the best cases leading to a spinning loop... + // (could this be a wrong signed vs unsigned conversion in the driver?) + // https://github.com/arduino/serial-monitor/issues/112 + const MaxReadTotalTimeoutConstant = 0x7FFFFFFE + commTimeouts := &windows.CommTimeouts{ ReadIntervalTimeout: 0xFFFFFFFF, ReadTotalTimeoutMultiplier: 0xFFFFFFFF, - ReadTotalTimeoutConstant: 0xFFFFFFFE, + ReadTotalTimeoutConstant: MaxReadTotalTimeoutConstant, WriteTotalTimeoutConstant: 0, WriteTotalTimeoutMultiplier: 0, } @@ -287,6 +296,11 @@ func (port *windowsPort) SetReadTimeout(timeout time.Duration) error { if ms > 0xFFFFFFFE || ms < 0 { return &PortError{code: InvalidTimeoutValue} } + + if ms > MaxReadTotalTimeoutConstant { + ms = MaxReadTotalTimeoutConstant + } + commTimeouts.ReadTotalTimeoutConstant = uint32(ms) } From 1ff9b6fa9a7c0c0dd0f0dc5c25e2902914704a19 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 12 Mar 2025 20:52:40 +0100 Subject: [PATCH 176/181] If the timeout is set to NoTimeout, make Read wait forever. Otherwise the maximul allowed timeout is 0x7FFFFFFF milliseconds, equivalent to about 24.85 days. This patch will make a transparent repeated read in case this long timeout should ever happen. --- serial_windows.go | 53 ++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/serial_windows.go b/serial_windows.go index 9ea64b6..37b3b39 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -28,8 +28,9 @@ import ( ) type windowsPort struct { - mu sync.Mutex - handle windows.Handle + mu sync.Mutex + handle windows.Handle + hasTimeout bool } func nativeGetPortsList() ([]string, error) { @@ -72,26 +73,33 @@ func (port *windowsPort) Read(p []byte) (int, error) { } defer windows.CloseHandle(ev.HEvent) - err = windows.ReadFile(port.handle, p, &readed, ev) - if err == windows.ERROR_IO_PENDING { - err = windows.GetOverlappedResult(port.handle, ev, &readed, true) - } - switch err { - case nil: - // operation completed successfully - case windows.ERROR_OPERATION_ABORTED: - // port may have been closed - return int(readed), &PortError{code: PortClosed, causedBy: err} - default: - // error happened - return int(readed), err - } - if readed > 0 { - return int(readed), nil - } + for { + err = windows.ReadFile(port.handle, p, &readed, ev) + if err == windows.ERROR_IO_PENDING { + err = windows.GetOverlappedResult(port.handle, ev, &readed, true) + } + switch err { + case nil: + // operation completed successfully + case windows.ERROR_OPERATION_ABORTED: + // port may have been closed + return int(readed), &PortError{code: PortClosed, causedBy: err} + default: + // error happened + return int(readed), err + } + if readed > 0 { + return int(readed), nil + } - // Timeout - return 0, nil + // Timeout + port.mu.Lock() + hasTimeout := port.hasTimeout + port.mu.Unlock() + if hasTimeout { + return 0, nil + } + } } func (port *windowsPort) Write(p []byte) (int, error) { @@ -304,9 +312,12 @@ func (port *windowsPort) SetReadTimeout(timeout time.Duration) error { commTimeouts.ReadTotalTimeoutConstant = uint32(ms) } + port.mu.Lock() + defer port.mu.Unlock() if err := windows.SetCommTimeouts(port.handle, commTimeouts); err != nil { return &PortError{code: InvalidTimeoutValue, causedBy: err} } + port.hasTimeout = (timeout != NoTimeout) return nil } From 5069d66aa266bdc5aa7cc5d9fed52c4584675d4a Mon Sep 17 00:00:00 2001 From: Stepan Date: Fri, 28 Mar 2025 16:28:13 +0900 Subject: [PATCH 177/181] Windows: fixed port enumeration --- serial_windows.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/serial_windows.go b/serial_windows.go index 37b3b39..445430d 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -45,12 +45,22 @@ func nativeGetPortsList() ([]string, error) { } defer key.Close() - list, err := key.ReadValueNames(0) + names, err := key.ReadValueNames(0) if err != nil { return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} } - return list, nil + var values []string + for _, n := range names { + v, _, err := key.GetStringValue(n) + if err != nil || v == "" { + continue + } + + values = append(values, v) + } + + return values, nil } func (port *windowsPort) Close() error { From bcb04087016ea55190c7895173b02ae204b3928a Mon Sep 17 00:00:00 2001 From: Stepan Date: Fri, 28 Mar 2025 16:42:18 +0900 Subject: [PATCH 178/181] Windows: added a check for \\.\ prefix --- serial_windows.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/serial_windows.go b/serial_windows.go index 445430d..7aa0123 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -19,6 +19,7 @@ package serial import ( "errors" + "strings" "sync" "syscall" "time" @@ -352,7 +353,9 @@ func createOverlappedEvent() (*windows.Overlapped, error) { } func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { - portName = "\\\\.\\" + portName + if !strings.HasPrefix(portName, `\\.\`) { + portName = `\\.\` + portName + } path, err := windows.UTF16PtrFromString(portName) if err != nil { return nil, err From 9710c814f629cf9836385479554d79822a1e1ec2 Mon Sep 17 00:00:00 2001 From: WarningImHack3r <43064022+WarningImHack3r@users.noreply.github.com> Date: Wed, 7 May 2025 20:44:25 +0200 Subject: [PATCH 179/181] fix: incorrect masks for modem status bits on Windows --- serial_windows.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/serial_windows.go b/serial_windows.go index 7aa0123..54d2a89 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -280,16 +280,24 @@ func (port *windowsPort) SetRTS(rts bool) error { return nil } +// GetCommModemStatus constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getcommmodemstatus. +const ( + msCTSOn = 0x0010 + msDSROn = 0x0020 + msRingOn = 0x0040 + msRLSDOn = 0x0080 +) + func (port *windowsPort) GetModemStatusBits() (*ModemStatusBits, error) { var bits uint32 if err := windows.GetCommModemStatus(port.handle, &bits); err != nil { return nil, &PortError{} } return &ModemStatusBits{ - CTS: (bits & windows.EV_CTS) != 0, - DCD: (bits & windows.EV_RLSD) != 0, - DSR: (bits & windows.EV_DSR) != 0, - RI: (bits & windows.EV_RING) != 0, + CTS: (bits & msCTSOn) != 0, + DCD: (bits & msRLSDOn) != 0, + DSR: (bits & msDSROn) != 0, + RI: (bits & msRingOn) != 0, }, nil } @@ -366,7 +374,8 @@ func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { 0, nil, windows.OPEN_EXISTING, windows.FILE_FLAG_OVERLAPPED, - 0) + 0, + ) if err != nil { switch err { case windows.ERROR_ACCESS_DENIED: From 20a47944cfa974f7ff695f355056c22e62171c95 Mon Sep 17 00:00:00 2001 From: WarningImHack3r <43064022+WarningImHack3r@users.noreply.github.com> Date: Thu, 8 May 2025 13:17:50 +0200 Subject: [PATCH 180/181] refactor: rename variables to match docs --- serial_windows.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/serial_windows.go b/serial_windows.go index 54d2a89..5c5a2a4 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -282,10 +282,10 @@ func (port *windowsPort) SetRTS(rts bool) error { // GetCommModemStatus constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getcommmodemstatus. const ( - msCTSOn = 0x0010 - msDSROn = 0x0020 - msRingOn = 0x0040 - msRLSDOn = 0x0080 + MS_CTS_ON = 0x0010 + MS_DSR_ON = 0x0020 + MS_RING_ON = 0x0040 + MS_RLSD_ON = 0x0080 ) func (port *windowsPort) GetModemStatusBits() (*ModemStatusBits, error) { @@ -294,10 +294,10 @@ func (port *windowsPort) GetModemStatusBits() (*ModemStatusBits, error) { return nil, &PortError{} } return &ModemStatusBits{ - CTS: (bits & msCTSOn) != 0, - DCD: (bits & msRLSDOn) != 0, - DSR: (bits & msDSROn) != 0, - RI: (bits & msRingOn) != 0, + CTS: (bits & MS_CTS_ON) != 0, + DCD: (bits & MS_RLSD_ON) != 0, + DSR: (bits & MS_DSR_ON) != 0, + RI: (bits & MS_RING_ON) != 0, }, nil } From 8f447ebc07eb5bec7d0654c8a44cc6f5ed05d48f Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 19 May 2025 15:31:36 +0200 Subject: [PATCH 181/181] Make win32 internal constants private. --- serial_windows.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/serial_windows.go b/serial_windows.go index 5c5a2a4..d2800f8 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -280,15 +280,14 @@ func (port *windowsPort) SetRTS(rts bool) error { return nil } -// GetCommModemStatus constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getcommmodemstatus. -const ( - MS_CTS_ON = 0x0010 - MS_DSR_ON = 0x0020 - MS_RING_ON = 0x0040 - MS_RLSD_ON = 0x0080 -) - func (port *windowsPort) GetModemStatusBits() (*ModemStatusBits, error) { + // GetCommModemStatus constants. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getcommmodemstatus. + const ( + MS_CTS_ON = 0x0010 + MS_DSR_ON = 0x0020 + MS_RING_ON = 0x0040 + MS_RLSD_ON = 0x0080 + ) var bits uint32 if err := windows.GetCommModemStatus(port.handle, &bits); err != nil { return nil, &PortError{}