summaryrefslogtreecommitdiff
path: root/software/vendor/go.bug.st
diff options
context:
space:
mode:
authorxengineering <me@xengineering.eu>2024-05-30 16:00:32 +0200
committerxengineering <me@xengineering.eu>2024-05-30 16:00:32 +0200
commitb8ef4d11fe0d00ce0884ccf982675845b20c3ce9 (patch)
tree3beb57ff849ed74569ce325225bc819791c25a6a /software/vendor/go.bug.st
parenteab833271eeaa8d54991c11eccec9445f662a191 (diff)
downloadiot-core-b8ef4d11fe0d00ce0884ccf982675845b20c3ce9.tar
iot-core-b8ef4d11fe0d00ce0884ccf982675845b20c3ce9.tar.zst
iot-core-b8ef4d11fe0d00ce0884ccf982675845b20c3ce9.zip
software: Implement serial port detection
Diffstat (limited to 'software/vendor/go.bug.st')
-rw-r--r--software/vendor/go.bug.st/serial/LICENSE33
-rw-r--r--software/vendor/go.bug.st/serial/README.md27
-rw-r--r--software/vendor/go.bug.st/serial/doc.go113
-rw-r--r--software/vendor/go.bug.st/serial/enumerator/doc.go15
-rw-r--r--software/vendor/go.bug.st/serial/enumerator/enumerator.go46
-rw-r--r--software/vendor/go.bug.st/serial/enumerator/syscall_windows.go167
-rw-r--r--software/vendor/go.bug.st/serial/enumerator/usb_darwin.go261
-rw-r--r--software/vendor/go.bug.st/serial/enumerator/usb_freebsd.go12
-rw-r--r--software/vendor/go.bug.st/serial/enumerator/usb_linux.go109
-rw-r--r--software/vendor/go.bug.st/serial/enumerator/usb_openbsd.go12
-rw-r--r--software/vendor/go.bug.st/serial/enumerator/usb_windows.go359
-rw-r--r--software/vendor/go.bug.st/serial/serial.go213
-rw-r--r--software/vendor/go.bug.st/serial/serial_bsd.go15
-rw-r--r--software/vendor/go.bug.st/serial/serial_darwin.go41
-rw-r--r--software/vendor/go.bug.st/serial/serial_darwin_386.go53
-rw-r--r--software/vendor/go.bug.st/serial/serial_darwin_64.go55
-rw-r--r--software/vendor/go.bug.st/serial/serial_freebsd.go87
-rw-r--r--software/vendor/go.bug.st/serial/serial_linux.go94
-rw-r--r--software/vendor/go.bug.st/serial/serial_openbsd.go87
-rw-r--r--software/vendor/go.bug.st/serial/serial_resetbuf_linux_bsd.go19
-rw-r--r--software/vendor/go.bug.st/serial/serial_specialbaudrate_linux.go23
-rw-r--r--software/vendor/go.bug.st/serial/serial_specialbaudrate_linux_ppc64le.go12
-rw-r--r--software/vendor/go.bug.st/serial/serial_unix.go472
-rw-r--r--software/vendor/go.bug.st/serial/serial_windows.go481
-rw-r--r--software/vendor/go.bug.st/serial/syscall_windows.go31
-rw-r--r--software/vendor/go.bug.st/serial/unixutils/pipe.go82
-rw-r--r--software/vendor/go.bug.st/serial/unixutils/select.go101
-rw-r--r--software/vendor/go.bug.st/serial/zsyscall_windows.go161
28 files changed, 3181 insertions, 0 deletions
diff --git a/software/vendor/go.bug.st/serial/LICENSE b/software/vendor/go.bug.st/serial/LICENSE
new file mode 100644
index 0000000..e34cbff
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/LICENSE
@@ -0,0 +1,33 @@
+
+Copyright (c) 2014-2023, Cristian Maglie.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/software/vendor/go.bug.st/serial/README.md b/software/vendor/go.bug.st/serial/README.md
new file mode 100644
index 0000000..b05796c
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/README.md
@@ -0,0 +1,27 @@
+[![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
+
+A cross-platform serial library for go-lang.
+
+## Documentation and examples
+
+See the godoc here: https://godoc.org/go.bug.st/serial
+
+## go.mod transition
+
+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.
+
+## Credits
+
+:sparkles: Thanks to all awesome [contributors]! :sparkles:
+
+## 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
+
diff --git a/software/vendor/go.bug.st/serial/doc.go b/software/vendor/go.bug.st/serial/doc.go
new file mode 100644
index 0000000..6e230ff
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/doc.go
@@ -0,0 +1,113 @@
+//
+// 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.
+//
+
+/*
+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:
+
+ import "go.bug.st/serial"
+
+It is possible to get the list of available serial ports with the
+GetPortsList function:
+
+ ports, err := serial.GetPortsList()
+ if err != nil {
+ log.Fatal(err)
+ }
+ if len(ports) == 0 {
+ log.Fatal("No serial ports found!")
+ }
+ for _, port := range ports {
+ fmt.Printf("Found port: %v\n", port)
+ }
+
+The serial port can be opened with the Open function:
+
+ mode := &serial.Mode{
+ BaudRate: 115200,
+ }
+ port, err := serial.Open("/dev/ttyUSB0", mode)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+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:
+
+ mode := &serial.Mode{
+ BaudRate: 57600,
+ Parity: serial.EvenParity,
+ DataBits: 7,
+ StopBits: serial.OneStopBit,
+ }
+
+The configuration can be changed at any time with the SetMode function:
+
+ err := port.SetMode(mode)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+The port object implements the io.ReadWriteCloser interface, so we can use
+the usual Read, Write and Close functions to send and receive data from the
+serial port:
+
+ n, err := port.Write([]byte("10,20,30\n\r"))
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("Sent %v bytes\n", n)
+
+ buff := make([]byte, 100)
+ for {
+ 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 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:
+
+ import "go.bug.st/serial/enumerator"
+
+ ports, err := enumerator.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)
+ }
+ }
+
+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
diff --git a/software/vendor/go.bug.st/serial/enumerator/doc.go b/software/vendor/go.bug.st/serial/enumerator/doc.go
new file mode 100644
index 0000000..a3f887d
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/enumerator/doc.go
@@ -0,0 +1,15 @@
+//
+// 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.
+//
+
+/*
+Package enumerator is a golang cross-platform library for USB serial port discovery.
+
+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 darwin/* targets.
+*/
+package enumerator
diff --git a/software/vendor/go.bug.st/serial/enumerator/enumerator.go b/software/vendor/go.bug.st/serial/enumerator/enumerator.go
new file mode 100644
index 0000000..420f451
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/enumerator/enumerator.go
@@ -0,0 +1,46 @@
+//
+// 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.
+//
+
+package enumerator
+
+//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.
+type PortDetails struct {
+ Name string
+ IsUSB bool
+ VID string
+ PID string
+ SerialNumber string
+
+ // Manufacturer 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.
+// 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/software/vendor/go.bug.st/serial/enumerator/syscall_windows.go b/software/vendor/go.bug.st/serial/enumerator/syscall_windows.go
new file mode 100644
index 0000000..2c6b379
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/enumerator/syscall_windows.go
@@ -0,0 +1,167 @@
+// Code generated by 'go generate'; DO NOT EDIT.
+
+package enumerator
+
+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)
+)
+
+// 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")
+ modcfgmgr32 = windows.NewLazySystemDLL("cfgmgr32.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")
+ 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) {
+ 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 = errnoErr(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 = errnoErr(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 = errnoErr(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 = errnoErr(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 = errnoErr(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 = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
+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
+}
+
+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/software/vendor/go.bug.st/serial/enumerator/usb_darwin.go b/software/vendor/go.bug.st/serial/enumerator/usb_darwin.go
new file mode 100644
index 0000000..d9adb1b
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/enumerator/usb_darwin.go
@@ -0,0 +1,261 @@
+//
+// 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.
+//
+
+package enumerator
+
+// #cgo LDFLAGS: -framework CoreFoundation -framework IOKit
+// #include <IOKit/IOKitLib.h>
+// #include <CoreFoundation/CoreFoundation.h>
+// #include <stdlib.h>
+import "C"
+import (
+ "errors"
+ "fmt"
+ "time"
+ "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(io_registry_entry_t(service))
+ if err != nil {
+ return nil, &PortEnumerationError{causedBy: err}
+ }
+ ports = append(ports, port)
+ }
+ return ports, nil
+}
+
+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.
+ 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{
+ "IOUSBDevice": true,
+ "IOUSBHostDevice": true,
+ }
+ usbDevice := service
+ var searchErr error
+ for !validUSBDeviceClass[usbDevice.GetClass()] {
+ if usbDevice, searchErr = usbDevice.GetParent("IOService"); searchErr != nil {
+ break
+ }
+ }
+ if searchErr == 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) ([]io_object_t, error) {
+ i, err := getMatchingServices(serviceMatching(serviceType))
+ if err != nil {
+ return nil, err
+ }
+ defer i.Release()
+
+ 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)
+ continue
+ }
+ // 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
+ for _, s := range services {
+ s.Release()
+ }
+ services = []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) (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(i), nil
+}
+
+// CFStringRef
+
+type cfStringRef C.CFStringRef
+
+func cfStringCreateWithString(s string) cfStringRef {
+ c := C.CString(s)
+ defer C.free(unsafe.Pointer(c))
+ return cfStringRef(C.CFStringCreateWithCString(
+ C.kCFAllocatorDefault, c, C.kCFStringEncodingMacRoman))
+}
+
+func (ref cfStringRef) Release() {
+ C.CFRelease(C.CFTypeRef(ref))
+}
+
+// CFTypeRef
+
+type cfTypeRef C.CFTypeRef
+
+func (ref cfTypeRef) Release() {
+ C.CFRelease(C.CFTypeRef(ref))
+}
+
+// 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(C.io_registry_entry_t(*me), cPlane, &parent)
+ if err != 0 {
+ return 0, errors.New("No parent device available")
+ }
+ return io_registry_entry_t(parent), nil
+}
+
+func (me *io_registry_entry_t) CreateCFProperty(key string) (cfTypeRef, error) {
+ k := cfStringCreateWithString(key)
+ defer k.Release()
+ property := C.IORegistryEntryCreateCFProperty(C.io_registry_entry_t(*me), C.CFStringRef(k), C.kCFAllocatorDefault, 0)
+ if property == 0 {
+ return 0, errors.New("Property not found: " + key)
+ }
+ return cfTypeRef(property), nil
+}
+
+func (me *io_registry_entry_t) GetStringProperty(key string) (string, error) {
+ property, err := me.CreateCFProperty(key)
+ if err != nil {
+ return "", err
+ }
+ defer property.Release()
+
+ 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), &buff[0], 1024, 0) != C.true {
+ return "", fmt.Errorf("Property '%s' can't be converted", key)
+ }
+ return C.GoString(&buff[0]), nil
+}
+
+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 {
+ return res, fmt.Errorf("Property '%s' can't be converted or has been truncated", key)
+ }
+ return res, nil
+}
+
+func (me *io_registry_entry_t) Release() {
+ C.IOObjectRelease(C.io_object_t(*me))
+}
+
+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(C.io_iterator_t(*me)) == C.true
+}
+
+func (me *io_iterator_t) Reset() {
+ C.IOIteratorReset(C.io_iterator_t(*me))
+}
+
+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(C.io_object_t(*me))
+}
+
+// io_object_t
+
+type io_object_t C.io_object_t
+
+func (me *io_object_t) Release() {
+ C.IOObjectRelease(C.io_object_t(*me))
+}
+
+func (me *io_object_t) GetClass() string {
+ class := make([]C.char, 1024)
+ C.IOObjectGetClass(C.io_object_t(*me), &class[0])
+ return C.GoString(&class[0])
+}
diff --git a/software/vendor/go.bug.st/serial/enumerator/usb_freebsd.go b/software/vendor/go.bug.st/serial/enumerator/usb_freebsd.go
new file mode 100644
index 0000000..db5d96c
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/enumerator/usb_freebsd.go
@@ -0,0 +1,12 @@
+//
+// 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.
+//
+
+package enumerator
+
+func nativeGetDetailedPortsList() ([]*PortDetails, error) {
+ // TODO
+ return nil, &PortEnumerationError{}
+}
diff --git a/software/vendor/go.bug.st/serial/enumerator/usb_linux.go b/software/vendor/go.bug.st/serial/enumerator/usb_linux.go
new file mode 100644
index 0000000..f756367
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/enumerator/usb_linux.go
@@ -0,0 +1,109 @@
+//
+// 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.
+//
+
+package enumerator
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "go.bug.st/serial"
+)
+
+func nativeGetDetailedPortsList() ([]*PortDetails, error) {
+ // Retrieve the port list
+ ports, err := serial.GetPortsList()
+ if err != nil {
+ return nil, &PortEnumerationError{causedBy: err}
+ }
+
+ var res []*PortDetails
+ for _, port := range ports {
+ details, err := nativeGetPortDetails(port)
+ if err != nil {
+ return nil, &PortEnumerationError{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
+}
diff --git a/software/vendor/go.bug.st/serial/enumerator/usb_openbsd.go b/software/vendor/go.bug.st/serial/enumerator/usb_openbsd.go
new file mode 100644
index 0000000..db5d96c
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/enumerator/usb_openbsd.go
@@ -0,0 +1,12 @@
+//
+// 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.
+//
+
+package enumerator
+
+func nativeGetDetailedPortsList() ([]*PortDetails, error) {
+ // TODO
+ return nil, &PortEnumerationError{}
+}
diff --git a/software/vendor/go.bug.st/serial/enumerator/usb_windows.go b/software/vendor/go.bug.st/serial/enumerator/usb_windows.go
new file mode 100644
index 0000000..7883e45
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/enumerator/usb_windows.go
@@ -0,0 +1,359 @@
+//
+// 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.
+//
+
+package enumerator
+
+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
+}
+
+// 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
+//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, 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)
+//
+// 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)
+}
+
+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 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
+}
+
+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) {
+ guids, err := classGuidsFromName("Ports")
+ if err != nil {
+ return nil, &PortEnumerationError{causedBy: err}
+ }
+
+ var res []*PortDetails
+ for _, g := range guids {
+ devsSet, err := g.getDevicesSet()
+ if err != nil {
+ return nil, &PortEnumerationError{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, &PortEnumerationError{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)
+
+ // 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 */
+ n := uint32(0)
+ setupDiGetDeviceRegistryProperty(device.set, &device.data, spdrpFriendlyName /* spdrpDeviceDesc */, nil, nil, 0, &n)
+ 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
+}
diff --git a/software/vendor/go.bug.st/serial/serial.go b/software/vendor/go.bug.st/serial/serial.go
new file mode 100644
index 0000000..abfd7f9
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/serial.go
@@ -0,0 +1,213 @@
+//
+// 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.
+//
+
+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
+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)
+
+ // Wait until all data in the buffer are sent
+ Drain() 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
+
+ // 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)
+
+ // 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
+
+ // Break sends a break for a determined time
+ Break(time.Duration) error
+}
+
+// NoTimeout should be used as a parameter to SetReadTimeout to disable timeout.
+var NoTimeout time.Duration = -1
+
+// 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
+ DSR bool // DataSetReady status
+ RI bool // RingIndicator status
+ 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.
+// 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
+}
+
+// Open opens the serial port using the specified modes
+func Open(portName string, mode *Mode) (Port, error) {
+ 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
+func GetPortsList() ([]string, error) {
+ return nativeGetPortsList()
+}
+
+// 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)
+ InitialStatusBits *ModemOutputBits // Initial output modem bits status (if nil defaults to DTR=true and RTS=true)
+}
+
+// Parity describes a serial port parity setting
+type Parity int
+
+const (
+ // 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 (
+ // OneStopBit sets 1 stop bit (default)
+ OneStopBit StopBits = iota
+ // OnePointFiveStopBits sets 1.5 stop bits
+ OnePointFiveStopBits
+ // TwoStopBits sets 2 stop bits
+ TwoStopBits
+)
+
+// PortError is a platform independent error type for serial ports
+type PortError struct {
+ code PortErrorCode
+ causedBy error
+}
+
+// PortErrorCode is a code to easily identify the type of error
+type PortErrorCode int
+
+const (
+ // 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
+ // 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
+ // 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
+ PortClosed
+ // FunctionNotImplemented the requested function is not implemented
+ FunctionNotImplemented
+)
+
+// EncodedErrorString returns a string explaining the error code
+func (e PortError) EncodedErrorString() string {
+ switch e.code {
+ case PortBusy:
+ return "Serial port busy"
+ case PortNotFound:
+ return "Serial port not found"
+ case InvalidSerialPort:
+ return "Invalid serial port"
+ case PermissionDenied:
+ return "Permission denied"
+ case InvalidSpeed:
+ return "Port speed invalid or not supported"
+ case InvalidDataBits:
+ 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 InvalidTimeoutValue:
+ return "Timeout value invalid or not supported"
+ case ErrorEnumeratingPorts:
+ return "Could not enumerate serial ports"
+ case PortClosed:
+ return "Port has been closed"
+ case FunctionNotImplemented:
+ return "Function not implemented"
+ 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.EncodedErrorString()
+}
+
+// Code returns an identifier for the kind of error occurred
+func (e PortError) Code() PortErrorCode {
+ return e.code
+}
diff --git a/software/vendor/go.bug.st/serial/serial_bsd.go b/software/vendor/go.bug.st/serial/serial_bsd.go
new file mode 100644
index 0000000..fb7c849
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/serial_bsd.go
@@ -0,0 +1,15 @@
+//
+// 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
+
+import "golang.org/x/sys/unix"
+
+func (port *unixPort) Drain() error {
+ return unix.IoctlSetInt(port.handle, unix.TIOCDRAIN, 0)
+}
diff --git a/software/vendor/go.bug.st/serial/serial_darwin.go b/software/vendor/go.bug.st/serial/serial_darwin.go
new file mode 100644
index 0000000..2817041
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/serial_darwin.go
@@ -0,0 +1,41 @@
+//
+// 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.
+//
+
+package serial
+
+import "golang.org/x/sys/unix"
+
+const devFolder = "/dev"
+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]
+ 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))
+}
+
+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/software/vendor/go.bug.st/serial/serial_darwin_386.go b/software/vendor/go.bug.st/serial/serial_darwin_386.go
new file mode 100644
index 0000000..3594a6c
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/serial_darwin_386.go
@@ -0,0 +1,53 @@
+//
+// 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.
+//
+
+package serial
+
+import "golang.org/x/sys/unix"
+
+// 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,
+}
+
+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 | tcCRTS_IFLOW)
+
+func toTermiosSpeedType(speed uint32) uint32 {
+ return speed
+}
diff --git a/software/vendor/go.bug.st/serial/serial_darwin_64.go b/software/vendor/go.bug.st/serial/serial_darwin_64.go
new file mode 100644
index 0000000..d1ae7c4
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/serial_darwin_64.go
@@ -0,0 +1,55 @@
+//
+// 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 && (amd64 || arm64)
+
+package serial
+
+import "golang.org/x/sys/unix"
+
+// termios manipulation functions
+
+var baudrateMap = map[int]uint64{
+ 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: 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
+const tcIUCLC uint64 = 0
+
+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/software/vendor/go.bug.st/serial/serial_freebsd.go b/software/vendor/go.bug.st/serial/serial_freebsd.go
new file mode 100644
index 0000000..aceb08a
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/serial_freebsd.go
@@ -0,0 +1,87 @@
+//
+// 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.
+//
+
+package serial
+
+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
+const ioctlTioccbrk = unix.TIOCCBRK
+const ioctlTiocsbrk = unix.TIOCSBRK
+
+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/software/vendor/go.bug.st/serial/serial_linux.go b/software/vendor/go.bug.st/serial/serial_linux.go
new file mode 100644
index 0000000..fe114d9
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/serial_linux.go
@@ -0,0 +1,94 @@
+//
+// 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.
+//
+
+package serial
+
+import "golang.org/x/sys/unix"
+
+const devFolder = "/dev"
+const regexFilter = "(ttyS|ttyHS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO|ttymxc)[0-9]{1,3}"
+
+// 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,
+ 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: unix.CS8, // Default to 8 bits
+ 5: unix.CS5,
+ 6: unix.CS6,
+ 7: unix.CS7,
+ 8: unix.CS8,
+}
+
+const tcCMSPAR = unix.CMSPAR
+const tcIUCLC = unix.IUCLC
+
+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
+}
+
+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) 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)
+}
diff --git a/software/vendor/go.bug.st/serial/serial_openbsd.go b/software/vendor/go.bug.st/serial/serial_openbsd.go
new file mode 100644
index 0000000..644dc92
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/serial_openbsd.go
@@ -0,0 +1,87 @@
+//
+// 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.
+//
+
+package serial
+
+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
+const ioctlTioccbrk = unix.TIOCCBRK
+const ioctlTiocsbrk = unix.TIOCSBRK
+
+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}
+}
diff --git a/software/vendor/go.bug.st/serial/serial_resetbuf_linux_bsd.go b/software/vendor/go.bug.st/serial/serial_resetbuf_linux_bsd.go
new file mode 100644
index 0000000..105669d
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/serial_resetbuf_linux_bsd.go
@@ -0,0 +1,19 @@
+//
+// 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 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/software/vendor/go.bug.st/serial/serial_specialbaudrate_linux.go b/software/vendor/go.bug.st/serial/serial_specialbaudrate_linux.go
new file mode 100644
index 0000000..ff48974
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/serial_specialbaudrate_linux.go
@@ -0,0 +1,23 @@
+//
+// 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 linux && !ppc64le
+
+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/software/vendor/go.bug.st/serial/serial_specialbaudrate_linux_ppc64le.go b/software/vendor/go.bug.st/serial/serial_specialbaudrate_linux_ppc64le.go
new file mode 100644
index 0000000..cd80b86
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/serial_specialbaudrate_linux_ppc64le.go
@@ -0,0 +1,12 @@
+//
+// 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.
+//
+
+package serial
+
+func (port *unixPort) setSpecialBaudrate(speed uint32) error {
+ // TODO: unimplemented
+ return &PortError{code: InvalidSpeed}
+}
diff --git a/software/vendor/go.bug.st/serial/serial_unix.go b/software/vendor/go.bug.st/serial/serial_unix.go
new file mode 100644
index 0000000..e025d2d
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/serial_unix.go
@@ -0,0 +1,472 @@
+//
+// 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 linux || darwin || freebsd || openbsd
+
+package serial
+
+import (
+ "fmt"
+ "io/ioutil"
+ "regexp"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "go.bug.st/serial/unixutils"
+ "golang.org/x/sys/unix"
+)
+
+type unixPort struct {
+ handle int
+
+ readTimeout time.Duration
+ closeLock sync.RWMutex
+ closeSignal *unixutils.Pipe
+ opened uint32
+}
+
+func (port *unixPort) Close() error {
+ if !atomic.CompareAndSwapUint32(&port.opened, 1, 0) {
+ return nil
+ }
+
+ // Close port
+ port.releaseExclusiveAccess()
+ if err := unix.Close(port.handle); err != nil {
+ return err
+ }
+
+ 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()
+
+ // Close signaling pipe
+ if err := port.closeSignal.Close(); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (port *unixPort) Read(p []byte) (int, error) {
+ port.closeLock.RLock()
+ defer port.closeLock.RUnlock()
+ if atomic.LoadUint32(&port.opened) != 1 {
+ 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 {
+ timeout := time.Duration(-1)
+ if port.readTimeout != NoTimeout {
+ 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 {
+ continue
+ }
+ if err != nil {
+ return 0, err
+ }
+ 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
+ }
+ // 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
+ }
+ return n, err
+ }
+}
+
+func (port *unixPort) Write(p []byte) (n int, err error) {
+ n, err = unix.Write(port.handle, p)
+ if n < 0 { // Do not return -1 unix errors
+ n = 0
+ }
+ 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 {
+ return err
+ }
+ if err := setTermSettingsParity(mode.Parity, settings); err != nil {
+ return err
+ }
+ if err := setTermSettingsDataBits(mode.DataBits, settings); err != nil {
+ return err
+ }
+ if err := setTermSettingsStopBits(mode.StopBits, settings); err != nil {
+ return err
+ }
+ 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 {
+ // 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
+ }
+ }
+ return nil
+}
+
+func (port *unixPort) SetDTR(dtr bool) error {
+ status, err := port.getModemBitsStatus()
+ if err != nil {
+ return err
+ }
+ if dtr {
+ status |= unix.TIOCM_DTR
+ } else {
+ status &^= unix.TIOCM_DTR
+ }
+ return port.setModemBitsStatus(status)
+}
+
+func (port *unixPort) SetRTS(rts bool) error {
+ status, err := port.getModemBitsStatus()
+ if err != nil {
+ return err
+ }
+ if rts {
+ status |= unix.TIOCM_RTS
+ } else {
+ status &^= unix.TIOCM_RTS
+ }
+ 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 {
+ return nil, err
+ }
+ return &ModemStatusBits{
+ 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 := unix.Open(portName, unix.O_RDWR|unix.O_NOCTTY|unix.O_NDELAY, 0)
+ if err != nil {
+ switch err {
+ case unix.EBUSY:
+ return nil, &PortError{code: PortBusy}
+ case unix.EACCES:
+ return nil, &PortError{code: PermissionDenied}
+ }
+ return nil, err
+ }
+ port := &unixPort{
+ handle: h,
+ opened: 1,
+ readTimeout: NoTimeout,
+ }
+
+ // Setup serial port
+ settings, err := port.getTermSettings()
+ if err != nil {
+ port.Close()
+ return nil, &PortError{code: InvalidSerialPort, causedBy: fmt.Errorf("error getting term settings: %w", err)}
+ }
+
+ // 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, 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: fmt.Errorf("error getting modem bits status: %w", 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 {
+ port.Close()
+ 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 err := port.SetMode(mode); err != nil {
+ port.Close()
+ return nil, &PortError{code: InvalidSerialPort, causedBy: fmt.Errorf("error configuring port: %w", err)}
+ }
+
+ unix.SetNonblock(h, false)
+
+ port.acquireExclusiveAccess()
+
+ // This pipe is used as a signal to cancel blocking Read
+ pipe := &unixutils.Pipe{}
+ if err := pipe.Open(); err != nil {
+ port.Close()
+ return nil, &PortError{code: InvalidSerialPort, causedBy: fmt.Errorf("error opening signaling pipe: %w", err)}
+ }
+ port.closeSignal = pipe
+
+ return port, nil
+}
+
+func nativeGetPortsList() ([]string, error) {
+ files, err := ioutil.ReadDir(devFolder)
+ if err != nil {
+ return nil, err
+ }
+
+ 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() {
+ continue
+ }
+
+ // Keep only devices with the correct name
+ if !regex.MatchString(f.Name()) {
+ continue
+ }
+
+ portName := devFolder + "/" + f.Name()
+
+ // 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
+ } else {
+ port.Close()
+ }
+ }
+
+ // Save serial port in the resulting list
+ ports = append(ports, portName)
+ }
+
+ return ports, nil
+}
+
+// termios manipulation functions
+
+func setTermSettingsParity(parity Parity, settings *unix.Termios) error {
+ switch parity {
+ case NoParity:
+ settings.Cflag &^= unix.PARENB
+ settings.Cflag &^= unix.PARODD
+ settings.Cflag &^= tcCMSPAR
+ settings.Iflag &^= unix.INPCK
+ case OddParity:
+ settings.Cflag |= unix.PARENB
+ settings.Cflag |= unix.PARODD
+ settings.Cflag &^= tcCMSPAR
+ settings.Iflag |= unix.INPCK
+ case EvenParity:
+ settings.Cflag |= unix.PARENB
+ settings.Cflag &^= unix.PARODD
+ settings.Cflag &^= tcCMSPAR
+ settings.Iflag |= unix.INPCK
+ case MarkParity:
+ if tcCMSPAR == 0 {
+ return &PortError{code: InvalidParity}
+ }
+ settings.Cflag |= unix.PARENB
+ settings.Cflag |= unix.PARODD
+ settings.Cflag |= tcCMSPAR
+ settings.Iflag |= unix.INPCK
+ case SpaceParity:
+ if tcCMSPAR == 0 {
+ return &PortError{code: InvalidParity}
+ }
+ settings.Cflag |= unix.PARENB
+ settings.Cflag &^= unix.PARODD
+ settings.Cflag |= tcCMSPAR
+ settings.Iflag |= unix.INPCK
+ default:
+ return &PortError{code: InvalidParity}
+ }
+ return nil
+}
+
+func setTermSettingsDataBits(bits int, settings *unix.Termios) error {
+ databits, ok := databitsMap[bits]
+ if !ok {
+ return &PortError{code: InvalidDataBits}
+ }
+ // Remove previous databits setting
+ settings.Cflag &^= unix.CSIZE
+ // Set requested databits
+ settings.Cflag |= databits
+ return nil
+}
+
+func setTermSettingsStopBits(bits StopBits, settings *unix.Termios) error {
+ switch bits {
+ case OneStopBit:
+ settings.Cflag &^= unix.CSTOPB
+ case OnePointFiveStopBits:
+ return &PortError{code: InvalidStopBits}
+ case TwoStopBits:
+ settings.Cflag |= unix.CSTOPB
+ default:
+ return &PortError{code: InvalidStopBits}
+ }
+ return nil
+}
+
+func setTermSettingsCtsRts(enable bool, settings *unix.Termios) {
+ if enable {
+ settings.Cflag |= tcCRTSCTS
+ } else {
+ settings.Cflag &^= tcCRTSCTS
+ }
+}
+
+func setRawMode(settings *unix.Termios) {
+ // Set local mode
+ settings.Cflag |= unix.CREAD
+ settings.Cflag |= unix.CLOCAL
+
+ // Set raw mode
+ 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 &^= unix.OPOST
+
+ // Block reads until at least one char is available (no timeout)
+ settings.Cc[unix.VMIN] = 1
+ settings.Cc[unix.VTIME] = 0
+}
+
+// native syscall wrapper functions
+
+func (port *unixPort) getTermSettings() (*unix.Termios, error) {
+ return unix.IoctlGetTermios(port.handle, ioctlTcgetattr)
+}
+
+func (port *unixPort) setTermSettings(settings *unix.Termios) error {
+ return unix.IoctlSetTermios(port.handle, ioctlTcsetattr, settings)
+}
+
+func (port *unixPort) getModemBitsStatus() (int, error) {
+ return unix.IoctlGetInt(port.handle, unix.TIOCMGET)
+}
+
+func (port *unixPort) setModemBitsStatus(status int) error {
+ return unix.IoctlSetPointerInt(port.handle, unix.TIOCMSET, status)
+}
+
+func (port *unixPort) acquireExclusiveAccess() error {
+ return unix.IoctlSetInt(port.handle, unix.TIOCEXCL, 0)
+}
+
+func (port *unixPort) releaseExclusiveAccess() error {
+ return unix.IoctlSetInt(port.handle, unix.TIOCNXCL, 0)
+}
diff --git a/software/vendor/go.bug.st/serial/serial_windows.go b/software/vendor/go.bug.st/serial/serial_windows.go
new file mode 100644
index 0000000..35bb0c9
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/serial_windows.go
@@ -0,0 +1,481 @@
+//
+// 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.
+//
+
+package serial
+
+/*
+
+// 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
+
+*/
+
+import (
+ "sync"
+ "syscall"
+ "time"
+)
+
+type windowsPort struct {
+ mu sync.Mutex
+ handle syscall.Handle
+}
+
+func nativeGetPortsList() ([]string, error) {
+ subKey, err := syscall.UTF16PtrFromString("HARDWARE\\DEVICEMAP\\SERIALCOMM\\")
+ 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}
+ }
+ 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, &PortError{code: ErrorEnumeratingPorts}
+ }
+
+ 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
+}
+
+func (port *windowsPort) Close() error {
+ port.mu.Lock()
+ defer func() {
+ port.handle = 0
+ port.mu.Unlock()
+ }()
+ if port.handle == 0 {
+ return nil
+ }
+ return syscall.CloseHandle(port.handle)
+}
+
+func (port *windowsPort) Read(p []byte) (int, error) {
+ var readed uint32
+ ev, err := createOverlappedEvent()
+ if err != nil {
+ return 0, err
+ }
+ defer syscall.CloseHandle(ev.HEvent)
+
+ 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) {
+ var writed uint32
+ 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
+}
+
+func (port *windowsPort) Drain() (err error) {
+ return syscall.FlushFileBuffers(port.handle)
+}
+
+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
+ 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 {
+ 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,
+}
+
+const (
+ oneStopBit = 0
+ one5StopBits = 1
+ twoStopBits = 2
+)
+
+var stopBitsMap = map[StopBits]byte{
+ OneStopBit: oneStopBit,
+ OnePointFiveStopBits: one5StopBits,
+ TwoStopBits: 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, &params) != nil {
+ port.Close()
+ return &PortError{code: InvalidSerialPort}
+ }
+ port.setModeParams(mode, &params)
+ if setCommState(port.handle, &params) != 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 {
+ params.BaudRate = uint32(mode.BaudRate)
+ }
+ if mode.DataBits == 0 {
+ params.ByteSize = 8 // Default to 8 bits
+ } else {
+ params.ByteSize = byte(mode.DataBits)
+ }
+ params.StopBits = stopBitsMap[mode.StopBits]
+ params.Parity = parityMap[mode.Parity]
+}
+
+func (port *windowsPort) SetDTR(dtr bool) error {
+ // 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 {
+ params.Flags |= dcbDTRControlEnable
+ }
+ if err := setCommState(port.handle, params); err != nil {
+ return &PortError{causedBy: err}
+ }
+
+ return nil
+}
+
+func (port *windowsPort) SetRTS(rts bool) error {
+ // 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.
+ //
+ // In addition this way the CommState Flags are not updated
+
+ /*
+ 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) {
+ 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 (port *windowsPort) SetReadTimeout(timeout time.Duration) error {
+ commTimeouts := &commTimeouts{
+ ReadIntervalTimeout: 0xFFFFFFFF,
+ ReadTotalTimeoutMultiplier: 0xFFFFFFFF,
+ ReadTotalTimeoutConstant: 0xFFFFFFFE,
+ WriteTotalTimeoutConstant: 0,
+ WriteTotalTimeoutMultiplier: 0,
+ }
+ 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}
+ }
+
+ return nil
+}
+
+func (port *windowsPort) Break(d time.Duration) error {
+ 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) {
+ 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)
+ if err != nil {
+ return nil, err
+ }
+ handle, err := syscall.CreateFile(
+ path,
+ syscall.GENERIC_READ|syscall.GENERIC_WRITE,
+ 0, nil,
+ syscall.OPEN_EXISTING,
+ syscall.FILE_FLAG_OVERLAPPED,
+ 0)
+ if err != nil {
+ switch err {
+ case syscall.ERROR_ACCESS_DENIED:
+ return nil, &PortError{code: PortBusy}
+ case syscall.ERROR_FILE_NOT_FOUND:
+ return nil, &PortError{code: PortNotFound}
+ }
+ return nil, err
+ }
+ // Create the serial port
+ port := &windowsPort{
+ handle: handle,
+ }
+
+ // Set port parameters
+ 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 {
+ 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
+ params.Flags &^= dcbDSRSensitivity
+ params.Flags |= dcbTXContinueOnXOFF
+ 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
+ params.XoffChar = 19 // C3
+ if setCommState(port.handle, params) != nil {
+ port.Close()
+ return nil, &PortError{code: InvalidSerialPort}
+ }
+
+ if port.SetReadTimeout(NoTimeout) != nil {
+ port.Close()
+ return nil, &PortError{code: InvalidSerialPort}
+ }
+ return port, nil
+}
diff --git a/software/vendor/go.bug.st/serial/syscall_windows.go b/software/vendor/go.bug.st/serial/syscall_windows.go
new file mode 100644
index 0000000..c814528
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/syscall_windows.go
@@ -0,0 +1,31 @@
+//
+// 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.
+//
+
+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/software/vendor/go.bug.st/serial/unixutils/pipe.go b/software/vendor/go.bug.st/serial/unixutils/pipe.go
new file mode 100644
index 0000000..748de34
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/unixutils/pipe.go
@@ -0,0 +1,82 @@
+//
+// 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 linux || darwin || freebsd || openbsd
+
+package unixutils
+
+import (
+ "fmt"
+ "syscall"
+)
+
+// 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
+}
diff --git a/software/vendor/go.bug.st/serial/unixutils/select.go b/software/vendor/go.bug.st/serial/unixutils/select.go
new file mode 100644
index 0000000..9f0e214
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/unixutils/select.go
@@ -0,0 +1,101 @@
+//
+// 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 linux || darwin || freebsd || openbsd
+
+package 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 = &copyOfRd
+ // Determine max fd.
+ max = rd.max
+ }
+ if wr != nil {
+ // fdsets are copied so the parameters are left untouched
+ copyOfWr := wr.set
+ res.writeable = &copyOfWr
+ // 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 = &copyOfEr
+ // 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
+}
diff --git a/software/vendor/go.bug.st/serial/zsyscall_windows.go b/software/vendor/go.bug.st/serial/zsyscall_windows.go
new file mode 100644
index 0000000..a2411a6
--- /dev/null
+++ b/software/vendor/go.bug.st/serial/zsyscall_windows.go
@@ -0,0 +1,161 @@
+// 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
+}