pax_global_header00006660000000000000000000000064140154417350014516gustar00rootroot0000000000000052 comment=1850ab4715b3097dd1bf2484a6b804fbd2935029 go-serial-2.3.0/000077500000000000000000000000001401544173500134025ustar00rootroot00000000000000go-serial-2.3.0/.editorconfig000066400000000000000000000003001401544173500160500ustar00rootroot00000000000000root = true [*] charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.go] indent_style = tab indent_size = tab [*.{yml, yaml}] indent_style = space indent_size = 2 go-serial-2.3.0/.github/000077500000000000000000000000001401544173500147425ustar00rootroot00000000000000go-serial-2.3.0/.github/workflows/000077500000000000000000000000001401544173500167775ustar00rootroot00000000000000go-serial-2.3.0/.github/workflows/go.yml000066400000000000000000000030421401544173500201260ustar00rootroot00000000000000name: Go on: push: branches: [ v2, dev ] pull_request: branches: [ v2, dev ] jobs: # Test tests: name: Run test strategy: matrix: os: [ 'ubuntu-latest', 'windows-latest', 'macos-latest' ] go: [ '1.14', '1.15', '1.16' ] runs-on: ${{ matrix.os }} steps: - name: Checkout uses: actions/checkout@v2 - name: Setup Go uses: actions/setup-go@v2 with: go-version: ${{ matrix.go }} - name: Get dependencies run: go mod download - name: Test run: go test -v -race ./... # Build build: name: Cross-Build strategy: matrix: os: [ 'ubuntu-latest', 'windows-latest', 'macos-latest' ] go: [ '1.14', '1.15', '1.16' ] goos: [ 'linux', 'windows', 'darwin', 'freebsd', 'openbsd' ] goarch: [ 'amd64' ] exclude: - os: ubuntu-latest goos: darwin - os: windows-latest goos: darwin include: - os: ubuntu-latest goos: linux goarch: arm - os: ubuntu-latest goos: openbsd goarch: arm runs-on: ${{ matrix.os }} env: GOOS: ${{ matrix.goos }} GOARCH: ${{ matrix.goarch }} steps: - name: Checkout uses: actions/checkout@v2 - name: Setup Go uses: actions/setup-go@v2 with: go-version: ${{ matrix.go }} - name: Get dependencies run: go mod download - name: Build run: go build -v . go-serial-2.3.0/.gitignore000066400000000000000000000003001401544173500153630ustar00rootroot00000000000000# Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out go-serial-2.3.0/CHANGELOG.md000066400000000000000000000012541401544173500152150ustar00rootroot00000000000000# Changelog ## v2.0.0 * New Go Module import path `github.com/albenik/go-serial/v2` * `serial.Port` interface discared in favor of `serial.Port` structure (similar to `os.File`) * `serial.Mode` discared and replaced with `serial.Option` * `serial.Open()` method changed to use `serila.Option`) * `port.SetMode(mode *Mode)` replaced with `port.Reconfigure(opts ...Option)` * `Disable HUPCL by default` (see https://github.com/albenik/go-serial/pull/7) * `WithHUPCL(bool)` option introduced * Minor bugfix & refactoring ## v1.x.x * Forked from https://github.com/bugst/go-serial * Minor but incompatible interface & logic changes implemented * Import path altered go-serial-2.3.0/LICENSE000066400000000000000000000027531401544173500144160ustar00rootroot00000000000000BSD 3-Clause License Copyright (c) 2018, Veniamin Albaev All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 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. * 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. go-serial-2.3.0/README.md000066400000000000000000000016341401544173500146650ustar00rootroot00000000000000# github.com/albenik/go-serial/v2 ![Go](https://github.com/albenik/go-serial/workflows/Go/badge.svg) ## MacOS Note Since version **v2.1.0** `GOOS=darwin` build requires `IOKit` as dependency and is only possible on Mac with cgo enabled. ## Package updated to v2 version A cross-platform serial library for Go. Forked from [github.com/bugst/go-serial](https://github.com/bugst/go-serial) and now developing independently. Many ideas are bein taken from [github.com/bugst/go-serial](https://github.com/bugst/go-serial) and [github.com/pyserial/pyserial](https://github.com/pyserial/pyserial). Any PR-s are welcome. ## INSTALL **Not work in GOPATH mode** ``` go get -u github.com/albenik/go-serial/v2 ``` **`CGO_ENABLED=1` required for MacOS build** ## Documentation and examples See the godoc here: https://godoc.org/github.com/albenik/go-serial ## License The software is release under a BSD 3-clause license go-serial-2.3.0/constants_darwin.go000066400000000000000000000004761401544173500173200ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package serial import "golang.org/x/sys/unix" const devFolder = "/dev" const regexFilter = "^(cu|tty)\\..*" const ioctlTcflsh = unix.TIOCFLUSH go-serial-2.3.0/constants_darwin_386.go000066400000000000000000000021171401544173500177120ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package serial import "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 = tcCCTS_OFLOW | tcCRTS_IFLOW func toTermiosSpeedType(speed uint32) uint32 { return speed } go-serial-2.3.0/constants_darwin_amd64.go000066400000000000000000000021171401544173500203050ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style license // that can be found in the LICENSE file. // package serial import "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 = tcCCTS_OFLOW | tcCRTS_IFLOW func toTermiosSpeedType(speed uint64) uint64 { return speed } go-serial-2.3.0/constants_freebsd.go000066400000000000000000000023301401544173500174350ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package serial import "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 ioctlTcflsh = unix.TIOCFLUSH func toTermiosSpeedType(speed uint32) uint32 { return speed } go-serial-2.3.0/constants_linux.go000066400000000000000000000025371401544173500171730ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package serial import "golang.org/x/sys/unix" const devFolder = "/dev" const regexFilter = "(ttyS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO)[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 ioctlTcflsh = unix.TCFLSH go-serial-2.3.0/constants_openbsd.go000066400000000000000000000023351401544173500174620ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package serial import "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 = tcCCTS_OFLOW const ioctlTcflsh = unix.TIOCFLUSH func toTermiosSpeedType(speed uint32) int32 { return int32(speed) } go-serial-2.3.0/doc.go000066400000000000000000000057361401544173500145110ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // /* Package go-serial is a cross-platform serial library for the go language. import github.com/albenik/go-serial/v2 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: port, err := serial.Open("/dev/ttyUSB0", serial.WithBaudrate(115200)) if err != nil { log.Fatal(err) } The Open function can accept options 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: serial.Open("/dev/ttyUSB0", serial.WithBaudrate(57600), serial.WithParity(serial.EvenParity), serial.WithDataBits(7), serial.WithStopBits(serial.OneStopBit), ) The configuration can be changed at any time with the Reconfigure() function: if err := port.Reconfigure( serial.WithBaudrate(57600), serial.WithParity(serial.EvenParity), ); 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 "github.com/albenik/go-serial/v2/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 go-serial-2.3.0/enumerator/000077500000000000000000000000001401544173500155635ustar00rootroot00000000000000go-serial-2.3.0/enumerator/doc.go000066400000000000000000000014661401544173500166660ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // /* Package enumerator is a golang cross-platform library for USB serial port discovery. WARNING: this library is still beta-testing code! please consider the library and the API as *unstable*. Beware that, even if at this point it's unlike to happen, the API may be subject to change until this notice is removed from the documentation. This library has been tested on Linux, Windows and Mac and uses specific OS services to enumerate USB PID/VID, in particular on MacOSX the use of cgo is required in order to access the IOKit Framework. This means that the library cannot be easily cross compiled for GOOS=darwing targets. */ package enumerator go-serial-2.3.0/enumerator/enumerator.go000066400000000000000000000023631401544173500202770ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package enumerator //go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_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 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 } go-serial-2.3.0/enumerator/example_getdetailedportlist_test.go000066400000000000000000000012521401544173500247400ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package enumerator_test import ( "fmt" "log" "github.com/albenik/go-serial/v2/enumerator" ) func ExampleGetDetailedPortsList() { 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) } } } go-serial-2.3.0/enumerator/syscall_windows.go000066400000000000000000000022351401544173500213400ustar00rootroot00000000000000package enumerator //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 hDevInfo, err error) = setupapi.SetupDiGetClassDevsW //sys setupDiDestroyDeviceInfoList(set hDevInfo) (err error) = setupapi.SetupDiDestroyDeviceInfoList //sys setupDiEnumDeviceInfo(set hDevInfo, index uint32, info *pspDevInfoData) (err error) = setupapi.SetupDiEnumDeviceInfo //sys setupDiGetDeviceInstanceId(set hDevInfo, devInfo *pspDevInfoData, devInstanceId unsafe.Pointer, devInstanceIdSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiGetDeviceInstanceIdW //sys setupDiOpenDevRegKey(set hDevInfo, devInfo *pspDevInfoData, scope dicsScope, hwProfile uint32, keyType uint32, samDesired regsam) (hkey syscall.Handle, err error) = setupapi.SetupDiOpenDevRegKey //sys setupDiGetDeviceRegistryProperty(set hDevInfo, devInfo *pspDevInfoData, property deviceProperty, propertyType *uint32, outValue *byte, outSize uint32, reqSize *uint32) (res bool) = setupapi.SetupDiGetDeviceRegistryPropertyW go-serial-2.3.0/enumerator/usb_darwin.go000066400000000000000000000142551401544173500202560ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // // +build go1.10,darwin package enumerator // #cgo LDFLAGS: -framework CoreFoundation -framework IOKit // #include // #include // #include import "C" import ( "errors" "fmt" "unsafe" ) func nativeGetDetailedPortsList() ([]*PortDetails, error) { var ports []*PortDetails services, err := getAllServices("IOSerialBSDClient") if err != nil { return nil, &PortEnumerationError{causedBy: err} } for _, s := range services { port, err := extractPortInfo(s) if err != nil { return nil, &PortEnumerationError{causedBy: err} } ports = append(ports, port) } return ports, nil } func extractPortInfo(s C.io_object_t) (*PortDetails, error) { defer s.Release() service := C.io_registry_entry_t(s) name, err := service.GetStringProperty("IOCalloutDevice") if err != nil { return nil, fmt.Errorf("error extracting port info from device: %s", err.Error()) } port := &PortDetails{} port.Name = name port.IsUSB = false usbDevice := service var searchErr error for usbDevice.GetClass() != "IOUSBDevice" { 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) ([]C.io_object_t, error) { i, err := getMatchingServices(serviceMatching(serviceType)) if err != nil { return nil, err } defer i.Release() var services []C.io_object_t tries := 0 for tries < 5 { // Extract all elements from iterator if service, ok := i.Next(); ok { services = append(services, service) continue } // If iterator is still valid return the result if i.IsValid() { return services, nil } // Otherwise empty the result and retry for _, s := range services { s.Release() } services = []C.io_object_t{} i.Reset() tries++ } // Give up if the iteration continues to fail... return nil, fmt.Errorf("IOServiceGetMatchingServices failed, data changed while iterating") } // serviceMatching create a matching dictionary that specifies an IOService class match. func serviceMatching(serviceType string) C.CFMutableDictionaryRef { t := C.CString(serviceType) defer C.free(unsafe.Pointer(t)) return C.IOServiceMatching(t) } // getMatchingServices look up registered IOService objects that match a matching dictionary. func getMatchingServices(matcher C.CFMutableDictionaryRef) (C.io_iterator_t, error) { var i C.io_iterator_t err := C.IOServiceGetMatchingServices(C.kIOMasterPortDefault, C.CFDictionaryRef(matcher), &i) if err != C.KERN_SUCCESS { return 0, fmt.Errorf("IOServiceGetMatchingServices failed (code %d)", err) } return i, nil } // CFStringRef func cfStringCreateWithString(s string) C.CFStringRef { c := C.CString(s) defer C.free(unsafe.Pointer(c)) return C.CFStringCreateWithCString( C.kCFAllocatorDefault, c, C.kCFStringEncodingMacRoman) } func (ref C.CFStringRef) Release() { C.CFRelease(C.CFTypeRef(ref)) } // CFTypeRef func (ref C.CFTypeRef) Release() { C.CFRelease(ref) } // io_registry_entry_t func (e *C.io_registry_entry_t) GetParent(plane string) (C.io_registry_entry_t, error) { cPlane := C.CString(plane) defer C.free(unsafe.Pointer(cPlane)) var parent C.io_registry_entry_t err := C.IORegistryEntryGetParentEntry(*e, cPlane, &parent) if err != 0 { return 0, errors.New("No parent device available") } return parent, nil } func (e *C.io_registry_entry_t) CreateCFProperty(key string) (C.CFTypeRef, error) { k := cfStringCreateWithString(key) defer k.Release() property := C.IORegistryEntryCreateCFProperty(*e, k, C.kCFAllocatorDefault, 0) if property == 0 { return 0, errors.New("Property not found: " + key) } return property, nil } func (e *C.io_registry_entry_t) GetStringProperty(key string) (string, error) { property, err := e.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 (e *C.io_registry_entry_t) GetIntProperty(key string, intType C.CFNumberType) (int, error) { property, err := e.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 } // 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 (i *C.io_iterator_t) IsValid() bool { return C.IOIteratorIsValid(*i) == C.true } func (i *C.io_iterator_t) Reset() { C.IOIteratorReset(*i) } func (i *C.io_iterator_t) Next() (C.io_object_t, bool) { res := C.IOIteratorNext(*i) return res, res != 0 } // io_object_t func (o *C.io_object_t) Release() { C.IOObjectRelease(*o) } func (o *C.io_object_t) GetClass() string { class := make([]C.char, 1024) C.IOObjectGetClass(*o, &class[0]) return C.GoString(&class[0]) } go-serial-2.3.0/enumerator/usb_freebsd.go000066400000000000000000000004541401544173500204000ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package enumerator func nativeGetDetailedPortsList() ([]*PortDetails, error) { // TODO return nil, &PortEnumerationError{} } go-serial-2.3.0/enumerator/usb_linux.go000066400000000000000000000053151401544173500201260ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package enumerator import ( "bufio" "fmt" "os" "path/filepath" "github.com/albenik/go-serial/v2" ) 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 } go-serial-2.3.0/enumerator/usb_ole_windows.go000066400000000000000000000055521401544173500213230ustar00rootroot00000000000000// // Copyright 2014-2017 Lars Knudsen, Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // // +build ignore package enumerator import ( "log" "regexp" ole "github.com/go-ole/go-ole" "github.com/go-ole/go-ole/oleutil" ) func init() { err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED) if err != nil { log.Fatal("Init error: ", err) } } func nativeGetDetailedPortsList() ([]*PortDetails, error) { unknown, err := oleutil.CreateObject("WbemScripting.SWbemLocator") if err != nil { return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} } defer unknown.Release() wmi, err := unknown.QueryInterface(ole.IID_IDispatch) if err != nil { return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} } defer wmi.Release() serviceRaw, err := wmi.CallMethod("ConnectServer") if err != nil { return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} } service := serviceRaw.ToIDispatch() defer service.Release() query := "SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0 and Name like '%(COM%'" queryResult, err := oleutil.CallMethod(service, "ExecQuery", query) if err != nil { return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} } result := queryResult.ToIDispatch() defer result.Release() countVar, err := result.GetProperty("Count") if err != nil { return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} } count := int(countVar.Val) res := []*PortDetails{} // Retrieve all items for i := 0; i < count; i++ { itemRaw, err := result.CallMethod("ItemIndex", i) if err != nil { return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} } item := itemRaw.ToIDispatch() defer item.Release() detail := &PortDetails{} if err := getPortDetails(item, detail); err != nil { return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err} } // SerialPort{Path: path, VendorId: VID, ProductId: PID, DisplayName: displayName.ToString()} res = append(res, detail) } return res, nil } func getPortDetails(item *ole.IDispatch, res *PortDetails) error { // Find port name itemName, err := item.GetProperty("Name") if err != nil { return err } re := regexp.MustCompile("\\((COM[0-9]+)\\)").FindAllStringSubmatch(itemName.ToString(), 1) if re == nil || len(re[0]) < 2 { // Discard items that are not serial ports return nil } res.Name = re[0][1] //itemPnPDeviceID, err := item.GetProperty("PnPDeviceID") //if err != nil { // return err //} //PnPDeviceID := itemPnPDeviceID.ToString() itemDeviceID, err := item.GetProperty("DeviceID") if err != nil { return err } parseDeviceID(itemDeviceID.ToString(), res) itemManufacturer, err := item.GetProperty("Product") if err != nil { return err } res.Manufacturer = itemManufacturer.ToString() return nil } go-serial-2.3.0/enumerator/usb_windows.go000066400000000000000000000240471401544173500204640ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package enumerator import ( "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 } // 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 hDevInfo syscall.Handle func (g *guid) getDevicesSet() (hDevInfo, error) { return setupDiGetClassDevs(g, nil, 0, digcfPresent) } func (set hDevInfo) destroy() { setupDiDestroyDeviceInfoList(set) } // https://msdn.microsoft.com/en-us/library/windows/hardware/ff552344(v=vs.85).aspx type pspDevInfoData struct { size uint32 guid guid devInst uint32 reserved uintptr } type deviceInfo struct { set hDevInfo data pspDevInfoData } func (set hDevInfo) 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) var friendlyName [1024]uint16 friendlyNameP := (*byte)(unsafe.Pointer(&friendlyName[0])) friendlyNameSize := uint32(unsafe.Sizeof(friendlyName) - 1) if setupDiGetDeviceRegistryProperty(device.set, &device.data, spdrpDeviceDesc /* spdrpFriendlyName */, nil, friendlyNameP, friendlyNameSize, nil) { details.Product = syscall.UTF16ToString(friendlyName[:]) } if setupDiGetDeviceRegistryProperty(device.set, &device.data, spdrpMFG, nil, friendlyNameP, friendlyNameSize, nil) { details.Manufacturer = syscall.UTF16ToString(friendlyName[:]) } return nil } go-serial-2.3.0/enumerator/usb_windows_test.go000066400000000000000000000031471401544173500215210ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package enumerator import ( "testing" "github.com/stretchr/testify/require" ) func parseAndReturnDeviceID(deviceID string) *PortDetails { res := &PortDetails{} parseDeviceID(deviceID, res) return res } func TestParseDeviceID(t *testing.T) { r := require.New(t) test := func(deviceId, vid, pid, serialNo string) { res := parseAndReturnDeviceID(deviceId) r.True(res.IsUSB) r.Equal(vid, res.VID) r.Equal(pid, res.PID) r.Equal(serialNo, res.SerialNumber) } test("FTDIBUS\\VID_0403+PID_6001+A6004CCFA\\0000", "0403", "6001", "A6004CCFA") test("USB\\VID_16C0&PID_0483\\12345", "16C0", "0483", "12345") test("USB\\VID_2341&PID_0000\\64936333936351400000", "2341", "0000", "64936333936351400000") test("USB\\VID_2341&PID_0000\\6493234373835191F1F1", "2341", "0000", "6493234373835191F1F1") test("USB\\VID_2341&PID_804E&MI_00\\6&279A3900&0&0000", "2341", "804E", "") test("USB\\VID_2341&PID_004E\\5&C3DC240&0&1", "2341", "004E", "") test("USB\\VID_03EB&PID_2111&MI_01\\6&21F3553F&0&0001", "03EB", "2111", "") // Atmel EDBG test("USB\\VID_2341&PID_804D&MI_00\\6&1026E213&0&0000", "2341", "804D", "") test("USB\\VID_2341&PID_004D\\5&C3DC240&0&1", "2341", "004D", "") test("USB\\VID_067B&PID_2303\\6&2C4CB384&0&3", "067B", "2303", "") // PL2303 } func TestParseDeviceIDWithInvalidStrings(t *testing.T) { r := require.New(t) res := parseAndReturnDeviceID("ABC") r.False(res.IsUSB) res2 := parseAndReturnDeviceID("USB") r.False(res2.IsUSB) } go-serial-2.3.0/enumerator/zsyscall_windows.go000066400000000000000000000107461401544173500215400ustar00rootroot00000000000000// 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") procSetupDiClassGuidsFromNameW = modsetupapi.NewProc("SetupDiClassGuidsFromNameW") procSetupDiGetClassDevsW = modsetupapi.NewProc("SetupDiGetClassDevsW") procSetupDiDestroyDeviceInfoList = modsetupapi.NewProc("SetupDiDestroyDeviceInfoList") procSetupDiEnumDeviceInfo = modsetupapi.NewProc("SetupDiEnumDeviceInfo") procSetupDiGetDeviceInstanceIdW = modsetupapi.NewProc("SetupDiGetDeviceInstanceIdW") procSetupDiOpenDevRegKey = modsetupapi.NewProc("SetupDiOpenDevRegKey") procSetupDiGetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiGetDeviceRegistryPropertyW") ) func setupDiClassGuidsFromNameInternal(class string, guid *guid, guidSize uint32, requiredSize *uint32) (err error) { var _p0 *uint16 _p0, err = syscall.UTF16PtrFromString(class) if err != nil { return } return _setupDiClassGuidsFromNameInternal(_p0, guid, guidSize, requiredSize) } func _setupDiClassGuidsFromNameInternal(class *uint16, guid *guid, guidSize uint32, requiredSize *uint32) (err error) { r1, _, e1 := syscall.Syscall6(procSetupDiClassGuidsFromNameW.Addr(), 4, uintptr(unsafe.Pointer(class)), uintptr(unsafe.Pointer(guid)), uintptr(guidSize), uintptr(unsafe.Pointer(requiredSize)), 0, 0) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func setupDiGetClassDevs(guid *guid, enumerator *string, hwndParent uintptr, flags uint32) (set hDevInfo, err error) { r0, _, e1 := syscall.Syscall6(procSetupDiGetClassDevsW.Addr(), 4, uintptr(unsafe.Pointer(guid)), uintptr(unsafe.Pointer(enumerator)), uintptr(hwndParent), uintptr(flags), 0, 0) set = hDevInfo(r0) if set == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func setupDiDestroyDeviceInfoList(set hDevInfo) (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 hDevInfo, index uint32, info *pspDevInfoData) (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 hDevInfo, devInfo *pspDevInfoData, 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 hDevInfo, devInfo *pspDevInfoData, 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 hDevInfo, devInfo *pspDevInfoData, property deviceProperty, propertyType *uint32, outValue *byte, outSize uint32, reqSize *uint32) (res bool) { r0, _, _ := syscall.Syscall9(procSetupDiGetDeviceRegistryPropertyW.Addr(), 7, uintptr(set), uintptr(unsafe.Pointer(devInfo)), uintptr(property), uintptr(unsafe.Pointer(propertyType)), uintptr(unsafe.Pointer(outValue)), uintptr(outSize), uintptr(unsafe.Pointer(reqSize)), 0, 0) res = r0 != 0 return } go-serial-2.3.0/errors.go000066400000000000000000000055641401544173500152570ustar00rootroot00000000000000package serial // 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 // Invalid timeout value passed 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 // Operating system function error OsError // Port write failed WriteFailed // Port read failed ReadFailed ) // PortError is a platform independent error type for serial ports type PortError struct { code PortErrorCode causedBy error } // 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" case OsError: return "operating system error" case WriteFailed: return "write failed" 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 } // Cause returns the cause for the error func (e PortError) Cause() error { return e.causedBy } func newOSError(err error) *PortError { return &PortError{code: OsError, causedBy: err} } go-serial-2.3.0/example_getportlist_test.go000066400000000000000000000007761401544173500210750ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package serial_test import ( "fmt" "log" "github.com/albenik/go-serial/v2" ) func ExampleGetPortsList() { ports, err := serial.GetPortsList() if err != nil { log.Fatal(err) } if len(ports) == 0 { fmt.Println("No serial ports found!") } else { for _, port := range ports { fmt.Printf("Found port: %v\n", port) } } } go-serial-2.3.0/example_modem_bits_test.go000066400000000000000000000025051401544173500206270ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package serial_test import ( "fmt" "log" "time" "github.com/albenik/go-serial/v2" ) func ExamplePort_GetModemStatusBits() { // Open the first serial port detected at 9600bps N81 port, err := serial.Open("/dev/ttyACM1", serial.WithBaudrate(9600), serial.WithDataBits(8), serial.WithParity(serial.NoParity), serial.WithStopBits(serial.OneStopBit), serial.WithReadTimeout(1000), serial.WithWriteTimeout(1000), ) if err != nil { log.Fatal(err) } defer port.Close() count := 0 for count < 25 { status, err := port.GetModemStatusBits() if err != nil { log.Fatal(err) } fmt.Printf("Status: %+v\n", status) time.Sleep(time.Second) count++ if count == 5 { err := port.SetDTR(false) if err != nil { log.Fatal(err) } fmt.Println("Set DTR OFF") } if count == 10 { err := port.SetDTR(true) if err != nil { log.Fatal(err) } fmt.Println("Set DTR ON") } if count == 15 { err := port.SetRTS(false) if err != nil { log.Fatal(err) } fmt.Println("Set RTS OFF") } if count == 20 { err := port.SetRTS(true) if err != nil { log.Fatal(err) } fmt.Println("Set RTS ON") } } } go-serial-2.3.0/example_serialport_test.go000066400000000000000000000012401401544173500206640ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package serial_test import ( "fmt" "log" "github.com/albenik/go-serial/v2" ) func ExamplePort_Reconfigure() { port, err := serial.Open("/dev/ttyACM0") if err != nil { log.Fatal(err) } if err := port.Reconfigure( serial.WithBaudrate(9600), serial.WithDataBits(8), serial.WithParity(serial.NoParity), serial.WithStopBits(serial.OneStopBit), serial.WithReadTimeout(1000), serial.WithWriteTimeout(1000), ); err != nil { log.Fatal(err) } fmt.Println("Port set to 9600 N81") } go-serial-2.3.0/example_test.go000066400000000000000000000027021401544173500164240ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package serial_test import ( "fmt" "log" "github.com/albenik/go-serial/v2" ) // This example prints the list of serial ports and use the first one // to send a string "10,20,30" and prints the response on the screen. func Example_sendAndReceive() { // Retrieve the port list ports, err := serial.GetPortsList() if err != nil { log.Fatal(err) } if len(ports) == 0 { log.Fatal("No serial ports found!") } // Print the list of detected ports for _, port := range ports { fmt.Printf("Found port: %v\n", port) } // Open the first serial port detected at 9600bps N81 port, err := serial.Open(ports[0], serial.WithBaudrate(9600), serial.WithDataBits(8), serial.WithParity(serial.NoParity), serial.WithStopBits(serial.OneStopBit), serial.WithReadTimeout(1000), serial.WithWriteTimeout(1000), serial.WithHUPCL(false), ) if err != nil { log.Fatal(err) } // Send the string "ABCDEF" to the serial port n, err := fmt.Fprint(port, "ABCDEF") if err != nil { log.Fatal(err) } fmt.Printf("Sent %v bytes\n", n) // Read and print the response buff := make([]byte, 100) for { // Reads up to 100 bytes n, err := port.Read(buff) if err != nil { log.Fatal(err) } if n == 0 { fmt.Println("\nEOF") break } fmt.Printf("%v", string(buff[:n])) } } go-serial-2.3.0/go.mod000066400000000000000000000006001401544173500145040ustar00rootroot00000000000000module github.com/albenik/go-serial/v2 go 1.14 require ( github.com/creack/goselect v0.1.2 github.com/kr/text v0.2.0 // indirect github.com/stretchr/testify v1.7.0 go.uber.org/multierr v1.6.0 golang.org/x/sys v0.0.0-20210223212115-eede4237b368 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) go-serial-2.3.0/go.sum000066400000000000000000000051551401544173500145430ustar00rootroot00000000000000github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0= github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= golang.org/x/sys v0.0.0-20210223212115-eede4237b368 h1:fDE3p0qf2V1co1vfj3/o87Ps8Hq6QTGNxJ5Xe7xSp80= golang.org/x/sys v0.0.0-20210223212115-eede4237b368/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= go-serial-2.3.0/options.go000066400000000000000000000014741401544173500154320ustar00rootroot00000000000000// // Copyright 2019 Veniamin Albaev . // 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 type Option func(p *Port) func WithBaudrate(o int) Option { return func(p *Port) { p.baudRate = o } } func WithDataBits(o int) Option { return func(p *Port) { p.dataBits = o } } func WithParity(o Parity) Option { return func(p *Port) { p.parity = o } } func WithStopBits(o StopBits) Option { return func(p *Port) { p.stopBits = o } } func WithReadTimeout(o int) Option { return func(p *Port) { p.setReadTimeoutValues(o) } } func WithWriteTimeout(o int) Option { return func(p *Port) { p.setWriteTimeoutValues(o) } } func WithHUPCL(o bool) Option { return func(p *Port) { p.hupcl = o } } go-serial-2.3.0/serial.go000066400000000000000000000044771401544173500152240ustar00rootroot00000000000000// // Copyright 2014-2018 Cristian Maglie. // Copyright 2019 Veniamin Albaev // 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 ( "os" ) //go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go syscall_windows.go 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 // OneStopBit sets 1 stop bit (default) OneStopBit StopBits = iota // OnePointFiveStopBits sets 1.5 stop bits OnePointFiveStopBits // TwoStopBits sets 2 stop bits TwoStopBits ) // StopBits describe a serial port stop bits setting type StopBits int // Parity describes a serial port parity setting type Parity int // ModemStatusBits contains all the modem status bits for a serial port (CTS, DSR, etc...). // It can be retrieved with the Port.GetModemStatusBits() method. type ModemStatusBits struct { CTS bool // ClearToSend status DSR bool // DataSetReady status RI bool // RingIndicator status DCD bool // DataCarrierDetect status } // Port is the interface for a serial Port type Port struct { name string opened bool 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) hupcl bool // Lower DTR line on close (hang up) internal *port // os specific (implementation like os.File) } func (p *Port) String() string { if p == nil { return "Error: port instance" } return p.name } func (p *Port) checkValid() error { if p == nil || p.internal == nil || !isHandleValid(p.internal.handle) { return &PortError{code: PortClosed, causedBy: os.ErrInvalid} } if !p.opened { return &PortError{code: PortClosed} } return nil } func newWithDefaults(n string, p *port) *Port { return &Port{ name: n, opened: true, baudRate: 9600, dataBits: 8, parity: NoParity, stopBits: OneStopBit, hupcl: false, internal: p, } } go-serial-2.3.0/serial_termsettings_darwin.go000066400000000000000000000020031401544173500213570ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // // +build darwin package serial // #include // #include import "C" import ( "unsafe" "golang.org/x/sys/unix" ) func (p *Port) retrieveTermSettings() (*settings, error) { var err error s := &settings{termios: new(unix.Termios), specificBaudrate: 0} s.termios, err = unix.IoctlGetTermios(p.internal.handle, unix.TIOCGETA) if err != nil { return nil, newOSError(err) } speed := C.cfgetispeed((*C.struct_termios)(unsafe.Pointer(s.termios))) s.specificBaudrate = int(speed) return s, nil } func (p *Port) applyTermSettings(s *settings) error { if err := unix.IoctlSetTermios(p.internal.handle, unix.TIOCSETA, s.termios); err != nil { return newOSError(err) } if err := unix.IoctlSetInt(p.internal.handle, C.IOSSIOSPEED, s.specificBaudrate); err != nil { return newOSError(err) } return nil } go-serial-2.3.0/serial_termsettings_linux.go000066400000000000000000000016751401544173500212500ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // // +build linux package serial import ( "golang.org/x/sys/unix" ) func (p *Port) retrieveTermSettings() (*settings, error) { var err error s := &settings{termios: new(unix.Termios)} if s.termios, err = unix.IoctlGetTermios(p.internal.handle, unix.TCGETS); err != nil { return nil, newOSError(err) } if s.termios.Cflag&unix.BOTHER == unix.BOTHER { if s.termios, err = unix.IoctlGetTermios(p.internal.handle, unix.TCGETS2); err != nil { return nil, newOSError(err) } } return s, nil } func (p *Port) applyTermSettings(s *settings) error { req := uint(unix.TCSETS) if s.termios.Cflag&unix.BOTHER == unix.BOTHER { req = unix.TCSETS2 } if err := unix.IoctlSetTermios(p.internal.handle, req, s.termios); err != nil { return newOSError(err) } return nil } go-serial-2.3.0/serial_termsettings_unix.go000066400000000000000000000012751401544173500210700ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // // +build freebsd openbsd package serial import ( "golang.org/x/sys/unix" ) func (p *Port) retrieveTermSettings() (*settings, error) { var err error s := &settings{termios: new(unix.Termios)} if s.termios, err = unix.IoctlGetTermios(p.internal.handle, unix.TIOCGETA); err != nil { return nil, newOSError(err) } return s, nil } func (p *Port) applyTermSettings(s *settings) error { if err := unix.IoctlSetTermios(p.internal.handle, unix.TIOCSETA, s.termios); err != nil { return newOSError(err) } return nil } go-serial-2.3.0/serial_test.go000066400000000000000000000010521401544173500162450ustar00rootroot00000000000000package serial_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/albenik/go-serial/v2" "os" ) func TestPort_Nil_SetDTR(t *testing.T) { var p *serial.Port err := p.SetDTR(false) if assert.Error(t, err) && assert.IsType(t, new(serial.PortError), err) { portErr := err.(*serial.PortError) assert.Equal(t, serial.PortClosed, portErr.Code()) assert.Equal(t, os.ErrInvalid, portErr.Cause()) } } func TestPort_Nil_String(t *testing.T) { var p *serial.Port assert.Equal(t, "Error: port instance", p.String()) } go-serial-2.3.0/serial_unix.go000066400000000000000000000242051401544173500162560ustar00rootroot00000000000000// // Copyright 2014-2018 Cristian Maglie. All rights reserved. // Copyright 2019 Veniamin Albaev // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // // +build linux darwin freebsd openbsd package serial import ( "io/ioutil" "regexp" "strings" "syscall" "time" "go.uber.org/multierr" "golang.org/x/sys/unix" "github.com/albenik/go-serial/v2/unixutils" ) const FIONREAD = 0x541B var zeroByte = []byte{0} type port struct { handle int firstByteTimeout bool readTimeout int writeTimeout int closePipeR int closePipeW int } func Open(name string, opts ...Option) (*Port, error) { h, err := unix.Open(name, 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 } // Accquire exclusive access if err = unix.IoctlSetInt(h, unix.TIOCEXCL, 0); err != nil { return nil, newOSError(multierr.Append(err, unix.Close(h))) } p := newWithDefaults(name, &port{ handle: h, firstByteTimeout: true, readTimeout: 0, writeTimeout: 0, }) // Setup serial port if err := p.Reconfigure(opts...); err != nil { return nil, p.closeAndReturnError(InvalidSerialPort, err) } if err = unix.SetNonblock(h, false); err != nil { return nil, p.closeAndReturnError(OsError, err) } fds := []int{0, 0} if err := syscall.Pipe(fds); err != nil { p.Close() return nil, p.closeAndReturnError(OsError, err) } p.internal.closePipeR = fds[0] p.internal.closePipeW = fds[1] return p, nil } func (p *Port) Reconfigure(opts ...Option) error { if err := p.checkValid(); err != nil { return err } for _, o := range opts { o(p) } return p.reconfigure() } func (p *Port) Close() error { // NOT thread safe if err := p.checkValid(); err != nil { return err } p.opened = false // Send close signal to all pending reads (if any) and close signaling pipe _, err := unix.Write(p.internal.closePipeW, zeroByte) err = multierr.Combine( err, unix.Close(p.internal.closePipeW), unix.Close(p.internal.closePipeR), unix.IoctlSetInt(p.internal.handle, unix.TIOCNXCL, 0), unix.Close(p.internal.handle), ) if err != nil { return newOSError(err) } return nil } func (p *Port) ReadyToRead() (uint32, error) { if err := p.checkValid(); err != nil { return 0, err } n, err := unix.IoctlGetInt(p.internal.handle, FIONREAD) if err != nil { return 0, newOSError(err) } return uint32(n), nil } func (p *Port) Read(b []byte) (int, error) { if err := p.checkValid(); err != nil { return 0, err } size, read := len(b), 0 fds := unixutils.NewFDSet(p.internal.handle, p.internal.closePipeR) buf := make([]byte, size) now := time.Now() deadline := now.Add(time.Duration(p.internal.readTimeout) * time.Millisecond) for read < size { res, err := unixutils.Select(fds, nil, fds, deadline.Sub(now)) if err != nil { if err == unix.EINTR { continue } return read, newOSError(err) } if res.IsReadable(p.internal.closePipeR) { return read, &PortError{code: PortClosed} } if !res.IsReadable(p.internal.handle) { return read, nil } n, err := unix.Read(p.internal.handle, buf[read:]) if err != nil { if err == unix.EINTR { continue } return read, newOSError(err) } // read should always return some data as select reported, it was ready to read when we got to this point. if n == 0 { return read, &PortError{code: ReadFailed} } copy(b[read:], buf[read:read+n]) read += n now = time.Now() if !now.Before(deadline) || p.internal.firstByteTimeout { return read, nil } } return read, nil } func (p *Port) Write(b []byte) (int, error) { if err := p.checkValid(); err != nil { return 0, err } size, written := len(b), 0 fds := unixutils.NewFDSet(p.internal.handle) clFds := unixutils.NewFDSet(p.internal.closePipeR) deadline := time.Now().Add(time.Duration(p.internal.writeTimeout) * time.Millisecond) for written < size { n, err := unix.Write(p.internal.handle, b[written:]) if err != nil { return written, newOSError(err) } if p.internal.writeTimeout == 0 { return n, nil } written += n now := time.Now() if p.internal.writeTimeout > 0 && !now.Before(deadline) { return written, nil } res, err := unixutils.Select(clFds, fds, fds, deadline.Sub(now)) if err != nil { return written, newOSError(err) } if res.IsReadable(p.internal.closePipeR) { return written, &PortError{code: PortClosed} } if !res.IsWritable(p.internal.handle) { return written, &PortError{code: WriteFailed} } } return written, nil } func (p *Port) ResetInputBuffer() error { if err := p.checkValid(); err != nil { return err } if err := unix.IoctlSetInt(p.internal.handle, ioctlTcflsh, unix.TCIFLUSH); err != nil { return newOSError(err) } return nil } func (p *Port) ResetOutputBuffer() error { if err := p.checkValid(); err != nil { return err } if err := unix.IoctlSetInt(p.internal.handle, ioctlTcflsh, unix.TCOFLUSH); err != nil { return newOSError(err) } return nil } func (p *Port) SetDTR(dtr bool) error { if err := p.checkValid(); err != nil { return err } status, err := p.retrieveModemBitsStatus() if err != nil { return err // port.retrieveModemBitsStatus already returned PortError } if dtr { status |= unix.TIOCM_DTR } else { status &^= unix.TIOCM_DTR } return p.applyModemBitsStatus(status) // already returned PortError } func (p *Port) SetRTS(rts bool) error { if err := p.checkValid(); err != nil { return err } status, err := p.retrieveModemBitsStatus() if err != nil { return err // port.retrieveModemBitsStatus() already returned PortError } if rts { status |= unix.TIOCM_RTS } else { status &^= unix.TIOCM_RTS } return p.applyModemBitsStatus(status) // already returned PortError } func (p *Port) SetReadTimeout(t int) error { if err := p.checkValid(); err != nil { return err } p.setReadTimeoutValues(t) return nil // timeout is done via select } // TODO Second argument was forget here while interface type that forces to implement it was removed. // To support backward compatibility keep it here until version v3 func (p *Port) SetReadTimeoutEx(t, _ uint32) error { if err := p.checkValid(); err != nil { return err } s, err := p.retrieveTermSettings() if err != nil { return err // port.retrieveTermSettings() already returned PortError } vtime := t / 100 // VTIME tenths of a second elapses between bytes if vtime > 255 || vtime*100 != t { return &PortError{code: InvalidTimeoutValue} } if vtime > 0 { s.termios.Cc[unix.VMIN] = 1 s.termios.Cc[unix.VTIME] = uint8(t) } else { s.termios.Cc[unix.VMIN] = 0 s.termios.Cc[unix.VTIME] = 0 } if err = p.applyTermSettings(s); err != nil { return err // port.applyTermSettings() already returned PortError } p.internal.firstByteTimeout = false p.internal.readTimeout = int(t) return nil } func (p *Port) SetFirstByteReadTimeout(t uint32) error { if err := p.checkValid(); err != nil { return err } if t > 0 && t < 0xFFFFFFFF { p.internal.firstByteTimeout = true p.internal.readTimeout = int(t) return nil } else { return &PortError{code: InvalidTimeoutValue} } } func (p *Port) SetWriteTimeout(t int) error { if err := p.checkValid(); err != nil { return err } p.setWriteTimeoutValues(t) return nil // timeout is done via select } func (p *Port) GetModemStatusBits() (*ModemStatusBits, error) { if err := p.checkValid(); err != nil { return nil, err } status, err := p.retrieveModemBitsStatus() if err != nil { return nil, err // port.retrieveModemBitsStatus() already returned PortError } 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 (p *Port) closeAndReturnError(code PortErrorCode, err error) *PortError { return &PortError{code: code, causedBy: multierr.Combine( err, unix.IoctlSetInt(p.internal.handle, unix.TIOCNXCL, 0), unix.Close(p.internal.handle), )} } func (p *Port) setReadTimeoutValues(t int) { p.internal.firstByteTimeout = false p.internal.readTimeout = t } func (p *Port) setWriteTimeoutValues(t int) { p.internal.writeTimeout = t } func (p *Port) retrieveModemBitsStatus() (int, error) { s, err := unix.IoctlGetInt(p.internal.handle, unix.TIOCMGET) if err != nil { return 0, newOSError(err) } return s, nil } func (p *Port) applyModemBitsStatus(status int) error { if err := unix.IoctlSetInt(p.internal.handle, unix.TIOCMSET, status); err != nil { return newOSError(err) } return nil } func (p *Port) reconfigure() error { if err := p.checkValid(); err != nil { return err } s, err := p.retrieveTermSettings() if err != nil { return err // port.retrieveTermSettings() already returned PortError } if err := s.setBaudrate(p.baudRate); err != nil { return err } if err := s.setParity(p.parity); err != nil { return err } if err := s.setDataBits(p.dataBits); err != nil { return err } if err := s.setStopBits(p.stopBits); err != nil { return err } s.setRawMode(p.hupcl) // Explicitly disable RTS/CTS flow control s.setCtsRts(false) return p.applyTermSettings(s) // already returned PortError } func GetPortsList() ([]string, error) { files, err := ioutil.ReadDir(devFolder) if err != nil { return nil, err } ports := make([]string, 0, len(files)) for _, f := range files { // Skip folders if f.IsDir() { continue } // Keep only devices with the correct name match, err := regexp.MatchString(regexFilter, f.Name()) if err != nil { return nil, err } if !match { continue } name := devFolder + "/" + f.Name() // Check if serial port is real or is a placeholder serial port "ttySxx" if strings.HasPrefix(f.Name(), "ttyS") { port, err := Open(name) if err != nil { serr, ok := err.(*PortError) if ok && serr.Code() == InvalidSerialPort { continue } } else { port.Close() } } // Save serial port in the resulting list ports = append(ports, name) } return ports, nil } func isHandleValid(h int) bool { return h != 0 } go-serial-2.3.0/serial_windows.go000066400000000000000000000276721401544173500170000ustar00rootroot00000000000000// // Copyright 2014-2018 Cristian Maglie. // Copyright 2019 Veniamin Albaev // 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 // Useful links: // https://msdn.microsoft.com/en-us/library/ff802693.aspx // https://msdn.microsoft.com/en-us/library/ms810467.aspx // https://github.com/pyserial/pyserial // https://pythonhosted.org/pyserial/ // https://playground.arduino.cc/Interfacing/CPPWindows // https://www.tldp.org/HOWTO/Serial-HOWTO-19.html import "syscall" var parityMap = map[Parity]byte{ NoParity: 0, OddParity: 1, EvenParity: 2, MarkParity: 3, SpaceParity: 4, } var stopBitsMap = map[StopBits]byte{ OneStopBit: 0, OnePointFiveStopBits: 1, TwoStopBits: 2, } type port struct { handle syscall.Handle timeouts *commTimeouts } func Open(name string, opts ...Option) (*Port, error) { path, err := syscall.UTF16PtrFromString("\\\\.\\" + name) if err != nil { return nil, err } handle, err := syscall.CreateFile( path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, // exclusive access nil, // no security syscall.OPEN_EXISTING, syscall.FILE_ATTRIBUTE_NORMAL|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 } port := newWithDefaults(name, &port{ handle: handle, timeouts: &commTimeouts{ // Read blocks until done. ReadIntervalTimeout: 0, ReadTotalTimeoutMultiplier: 0, ReadTotalTimeoutConstant: 0, // Write blocks until done. WriteTotalTimeoutMultiplier: 0, WriteTotalTimeoutConstant: 0, }, }) if err = port.Reconfigure(opts...); err != nil { port.Close() return nil, err } return port, nil } func (p *Port) Reconfigure(opts ...Option) error { if err := p.checkValid(); err != nil { return err } for _, o := range opts { o(p) } return p.reconfigure() } func (p *Port) Close() error { if err := p.checkValid(); err != nil { return err } if p.internal.handle == syscall.InvalidHandle { return nil } err := syscall.CloseHandle(p.internal.handle) p.internal.handle = syscall.InvalidHandle if err != nil { return &PortError{code: OsError, causedBy: err} } return nil } func (p *Port) ReadyToRead() (uint32, error) { if err := p.checkValid(); err != nil { return 0, err } var errs uint32 var stat comstat if err := clearCommError(p.internal.handle, &errs, &stat); err != nil { return 0, &PortError{code: OsError, causedBy: err} } return stat.inque, nil } func (p *Port) Read(b []byte) (int, error) { if err := p.checkValid(); err != nil { return 0, err } if p.internal.handle == syscall.InvalidHandle { return 0, &PortError{code: PortClosed, causedBy: nil} } handle := p.internal.handle errs := new(uint32) stat := new(comstat) if err := clearCommError(handle, errs, stat); err != nil { return 0, &PortError{code: InvalidSerialPort, causedBy: err} } size := uint32(len(b)) var readSize uint32 if p.internal.timeouts.ReadTotalTimeoutConstant == 0 && p.internal.timeouts.ReadTotalTimeoutMultiplier == 0 { if stat.inque < size { readSize = stat.inque } else { readSize = size } } else { readSize = size } if readSize > 0 { var read uint32 overlapped, err := createOverlappedStruct() if err != nil { return 0, &PortError{code: OsError, causedBy: err} } defer syscall.CloseHandle(overlapped.HEvent) err = syscall.ReadFile(handle, b[:readSize], &read, overlapped) if err != nil && err != syscall.ERROR_IO_PENDING { return 0, &PortError{code: OsError, causedBy: err} } err = getOverlappedResult(handle, overlapped, &read, true) if err != nil && err != syscall.ERROR_OPERATION_ABORTED { return 0, &PortError{code: OsError, causedBy: err} } return int(read), nil } else { return 0, nil } } func (p *Port) Write(b []byte) (int, error) { if err := p.checkValid(); err != nil { return 0, err } h := p.internal.handle errs := new(uint32) stat := new(comstat) if err := clearCommError(h, errs, stat); err != nil { return 0, &PortError{code: InvalidSerialPort, causedBy: err} } overlapped, err := createOverlappedStruct() if err != nil { return 0, err } defer syscall.CloseHandle(overlapped.HEvent) var written uint32 err = syscall.WriteFile(h, b, &written, overlapped) if err == nil || err == syscall.ERROR_IO_PENDING || err == syscall.ERROR_OPERATION_ABORTED { err = getOverlappedResult(h, overlapped, &written, true) if err == nil || err == syscall.ERROR_OPERATION_ABORTED { return int(written), nil } } return int(written), err } func (p *Port) ResetInputBuffer() error { if err := p.checkValid(); err != nil { return err } return purgeComm(p.internal.handle, purgeRxClear|purgeRxAbort) } func (p *Port) ResetOutputBuffer() error { if err := p.checkValid(); err != nil { return err } return purgeComm(p.internal.handle, purgeTxClear|purgeTxAbort) } func (p *Port) SetDTR(dtr bool) error { if err := p.checkValid(); err != nil { return err } // 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 p.hupcl = dtr params := &dcb{} if err := getCommState(p.internal.handle, params); err != nil { return &PortError{causedBy: err} } params.Flags &= dcbDTRControlDisableMask if dtr { params.Flags |= dcbDTRControlEnable } if err := setCommState(p.internal.handle, params); err != nil { return &PortError{causedBy: err} } return nil } func (p *Port) SetRTS(rts bool) error { if err := p.checkValid(); err != nil { return err } // 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(p.internal.handle, params); err != nil { return &PortError{causedBy: err} } params.Flags &= dcbRTSControlDisableMask if rts { params.Flags |= dcbRTSControlEnable } if err := setCommState(p.internal.handle, params); err != nil { return &PortError{causedBy: err} } return nil } func (p *Port) GetModemStatusBits() (*ModemStatusBits, error) { if err := p.checkValid(); err != nil { return nil, err } var bits uint32 if !getCommModemStatus(p.internal.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 (p *Port) SetReadTimeout(t int) error { if err := p.checkValid(); err != nil { return err } p.setReadTimeoutValues(t) return p.reconfigure() } func (p *Port) SetReadTimeoutEx(t, i uint32) error { if err := p.checkValid(); err != nil { return err } p.internal.timeouts.ReadIntervalTimeout = i p.internal.timeouts.ReadTotalTimeoutMultiplier = 0 p.internal.timeouts.ReadTotalTimeoutConstant = t return p.reconfigure() } func (p *Port) SetFirstByteReadTimeout(t uint32) error { if err := p.checkValid(); err != nil { return err } if t > 0 && t < 0xFFFFFFFF { p.internal.timeouts.ReadIntervalTimeout = 0xFFFFFFFF p.internal.timeouts.ReadTotalTimeoutMultiplier = 0xFFFFFFFF p.internal.timeouts.ReadTotalTimeoutConstant = t return p.reconfigure() } else { return &PortError{code: InvalidTimeoutValue} } } func (p *Port) SetWriteTimeout(t int) error { if err := p.checkValid(); err != nil { return err } p.setWriteTimeoutValues(t) return p.reconfigure() } func (p *Port) setReadTimeoutValues(t int) { switch { case t < 0: // Block until the buffer is full. p.internal.timeouts.ReadIntervalTimeout = 0 p.internal.timeouts.ReadTotalTimeoutMultiplier = 0 p.internal.timeouts.ReadTotalTimeoutConstant = 0 case t == 0: // Return immediately with or without data. p.internal.timeouts.ReadIntervalTimeout = 0xFFFFFFFF p.internal.timeouts.ReadTotalTimeoutMultiplier = 0 p.internal.timeouts.ReadTotalTimeoutConstant = 0 case t > 0: // Block until the buffer is full or timeout occurs. p.internal.timeouts.ReadIntervalTimeout = 0 p.internal.timeouts.ReadTotalTimeoutMultiplier = 0 p.internal.timeouts.ReadTotalTimeoutConstant = uint32(t) } } func (p *Port) setWriteTimeoutValues(t int) { switch { case t < 0: p.internal.timeouts.WriteTotalTimeoutMultiplier = 0 p.internal.timeouts.WriteTotalTimeoutConstant = 0 case t == 0: p.internal.timeouts.WriteTotalTimeoutMultiplier = 0 p.internal.timeouts.WriteTotalTimeoutConstant = 0xFFFFFFFF case t > 0: p.internal.timeouts.WriteTotalTimeoutMultiplier = 0 p.internal.timeouts.WriteTotalTimeoutConstant = uint32(t) } } func (p *Port) reconfigure() error { if err := setCommTimeouts(p.internal.handle, p.internal.timeouts); err != nil { p.Close() return &PortError{code: InvalidSerialPort, causedBy: err} } if err := setCommMask(p.internal.handle, evErr); err != nil { p.Close() return &PortError{code: InvalidSerialPort, causedBy: err} } params := &dcb{} if err := getCommState(p.internal.handle, params); err != nil { p.Close() return &PortError{code: InvalidSerialPort, causedBy: err} } params.Flags &= dcbRTSControlDisableMask params.Flags |= dcbRTSControlEnable params.Flags &= dcbDTRControlDisableMask if p.hupcl { params.Flags |= dcbDTRControlEnable } 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 params.BaudRate = uint32(p.baudRate) params.ByteSize = byte(p.dataBits) params.Parity = parityMap[p.parity] params.StopBits = stopBitsMap[p.stopBits] if err := setCommState(p.internal.handle, params); err != nil { p.Close() return &PortError{code: InvalidSerialPort, causedBy: err} } return nil } func GetPortsList() ([]string, error) { subKey, err := syscall.UTF16PtrFromString("HARDWARE\\DEVICEMAP\\SERIALCOMM\\") if err != nil { return nil, &PortError{code: ErrorEnumeratingPorts} } var h syscall.Handle if syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE, subKey, 0, syscall.KEY_READ, &h) != nil { return nil, &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 isHandleValid(h syscall.Handle) bool { return h != syscall.InvalidHandle } go-serial-2.3.0/syscall_windows.go000066400000000000000000000122471401544173500171630ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // package serial import ( "syscall" ) //sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) = advapi32.RegEnumValueW const ( ceBreak uint32 = 0x0010 ceFrame = 0x0008 ceOverrun = 0x0002 ceRxover = 0x0001 ceRxparity = 0x0004 ) type comstat struct { /* typedef struct _COMSTAT { DWORD fCtsHold :1; DWORD fDsrHold :1; DWORD fRlsdHold :1; DWORD fXoffHold :1; DWORD fXoffSent :1; DWORD fEof :1; DWORD fTxim :1; DWORD fReserved :25; DWORD cbInQue; DWORD cbOutQue; } COMSTAT, *LPCOMSTAT; */ flags uint32 inque uint32 outque uint32 } //sys clearCommError(handle syscall.Handle, lpErrors *uint32, lpStat *comstat) (err error) = ClearCommError 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 dcbRTSControlDisableMask = ^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 } //sys getCommState(handle syscall.Handle, dcb *dcb) (err error) = GetCommState //sys setCommState(handle syscall.Handle, dcb *dcb) (err error) = SetCommState type commTimeouts struct { ReadIntervalTimeout uint32 ReadTotalTimeoutMultiplier uint32 ReadTotalTimeoutConstant uint32 WriteTotalTimeoutMultiplier uint32 WriteTotalTimeoutConstant uint32 } //sys setCommTimeouts(handle syscall.Handle, timeouts *commTimeouts) (err error) = SetCommTimeouts const ( commFunctionSetXOFF = 1 commFunctionSetXON = 2 commFunctionSetRTS = 3 commFunctionClrRTS = 4 commFunctionSetDTR = 5 commFunctionClrDTR = 6 commFunctionSetBreak = 8 commFunctionClrBreak = 9 ) //sys escapeCommFunction(handle syscall.Handle, function uint32) (res bool) = EscapeCommFunction const ( msCTSOn = 0x0010 msDSROn = 0x0020 msRingOn = 0x0040 msRLSDOn = 0x0080 ) //sys getCommModemStatus(handle syscall.Handle, bits *uint32) (res bool) = GetCommModemStatus const ( evBreak uint32 = 0x0040 // A break was detected on input. evCts = 0x0008 // The CTS (clear-to-send) signal changed state. evDsr = 0x0010 // The DSR (data-set-ready) signal changed state. evErr = 0x0080 // A line-status error occurred. Line-status errors are CE_FRAME, CE_OVERRUN, and CE_RXPARITY. evRing = 0x0100 // A ring indicator was detected. evRlsd = 0x0020 // The RLSD (receive-line-signal-detect) signal changed state. evRxChar = 0x0001 // A character was received and placed in the input buffer. evRxFlag = 0x0002 // The event character was received and placed in the input buffer. The event character is specified in the device's DCB structure, which is applied to a serial port by using the SetCommState function. evTxEmpty = 0x0004 // The last character in the output buffer was sent. ) //sys setCommMask(handle syscall.Handle, mask uint32) (err error) = SetCommMask //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 const ( purgeRxAbort uint32 = 0x0002 purgeRxClear = 0x0008 purgeTxAbort = 0x0001 purgeTxClear = 0x0004 ) //sys purgeComm(handle syscall.Handle, flags uint32) (err error) = PurgeComm func createOverlappedStruct() (*syscall.Overlapped, error) { if h, err := createEvent(nil, true, false, nil); err == nil { return &syscall.Overlapped{HEvent: h}, nil } else { return nil, err } } go-serial-2.3.0/termios_baudrate_darwin.go000066400000000000000000000004521401544173500206270ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // // +build darwin package serial func (s *settings) setBaudrate(speed int) error { s.specificBaudrate = speed return nil } go-serial-2.3.0/termios_baudrate_linux.go000066400000000000000000000012621401544173500205020ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // // +build linux package serial import ( "golang.org/x/sys/unix" ) func (s *settings) setBaudrate(r int) error { if rate, ok := baudrateMap[r]; ok { // clear all standard baudrate bits for _, b := range baudrateMap { s.termios.Cflag &^= b } // set selected baudrate bit s.termios.Cflag |= rate s.termios.Ispeed = rate s.termios.Ospeed = rate } else { s.termios.Cflag &^= unix.CBAUD s.termios.Cflag |= unix.BOTHER s.termios.Ispeed = uint32(r) s.termios.Ospeed = uint32(r) } return nil } go-serial-2.3.0/termios_baudrate_unix.go000066400000000000000000000011361401544173500203260ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // // +build freebsd openbsd package serial func (s *settings) setBaudrate(speed int) error { baudrate, ok := baudrateMap[speed] if !ok { return &PortError{code: InvalidSpeed} } // revert old baudrate for _, rate := range baudrateMap { s.termios.Cflag &^= rate } // set new baudrate s.termios.Cflag |= baudrate s.termios.Ispeed = toTermiosSpeedType(baudrate) s.termios.Ospeed = toTermiosSpeedType(baudrate) return nil } go-serial-2.3.0/termios_setting_darwin.go000066400000000000000000000005021401544173500205110ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // // +build darwin package serial import ( "golang.org/x/sys/unix" ) type settings struct { termios *unix.Termios specificBaudrate int } go-serial-2.3.0/termios_setting_unix.go000066400000000000000000000004621401544173500202150ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // // +build linux freebsd openbsd package serial import ( "golang.org/x/sys/unix" ) type settings struct { termios *unix.Termios } go-serial-2.3.0/termios_unix.go000066400000000000000000000061071401544173500164620ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // // +build linux darwin freebsd openbsd package serial import ( "golang.org/x/sys/unix" ) func (s *settings) setParity(parity Parity) error { switch parity { case NoParity: s.termios.Cflag &^= unix.PARENB s.termios.Cflag &^= unix.PARODD s.termios.Cflag &^= tcCMSPAR s.termios.Iflag &^= unix.INPCK case OddParity: s.termios.Cflag |= unix.PARENB s.termios.Cflag |= unix.PARODD s.termios.Cflag &^= tcCMSPAR s.termios.Iflag |= unix.INPCK case EvenParity: s.termios.Cflag |= unix.PARENB s.termios.Cflag &^= unix.PARODD s.termios.Cflag &^= tcCMSPAR s.termios.Iflag |= unix.INPCK case MarkParity: if tcCMSPAR == 0 { return &PortError{code: InvalidParity} } s.termios.Cflag |= unix.PARENB s.termios.Cflag |= unix.PARODD s.termios.Cflag |= tcCMSPAR s.termios.Iflag |= unix.INPCK case SpaceParity: if tcCMSPAR == 0 { return &PortError{code: InvalidParity} } s.termios.Cflag |= unix.PARENB s.termios.Cflag &^= unix.PARODD s.termios.Cflag |= tcCMSPAR s.termios.Iflag |= unix.INPCK default: return &PortError{code: InvalidParity} } return nil } func (s *settings) setDataBits(bits int) error { databits, ok := databitsMap[bits] if !ok { return &PortError{code: InvalidDataBits} } // Remove previous databits setting s.termios.Cflag &^= unix.CSIZE // Set requested databits s.termios.Cflag |= databits return nil } func (s *settings) setStopBits(bits StopBits) error { switch bits { case OneStopBit: s.termios.Cflag &^= unix.CSTOPB case OnePointFiveStopBits: return &PortError{code: InvalidStopBits} case TwoStopBits: s.termios.Cflag |= unix.CSTOPB default: return &PortError{code: InvalidStopBits} } return nil } func (s *settings) setCtsRts(enable bool) { if enable { s.termios.Cflag |= tcCRTSCTS } else { s.termios.Cflag &^= tcCRTSCTS } } func (s *settings) setRawMode(hupcl bool) { // Set local mode s.termios.Cflag |= unix.CREAD s.termios.Cflag |= unix.CLOCAL if hupcl { s.termios.Cflag |= unix.HUPCL } else { s.termios.Cflag &^= unix.HUPCL } // Set raw mode s.termios.Lflag &^= unix.ICANON s.termios.Lflag &^= unix.ECHO s.termios.Lflag &^= unix.ECHOE s.termios.Lflag &^= unix.ECHOK s.termios.Lflag &^= unix.ECHONL s.termios.Lflag &^= unix.ECHOCTL s.termios.Lflag &^= unix.ECHOPRT s.termios.Lflag &^= unix.ECHOKE s.termios.Lflag &^= unix.ISIG s.termios.Lflag &^= unix.IEXTEN s.termios.Iflag &^= unix.IXON s.termios.Iflag &^= unix.IXOFF s.termios.Iflag &^= unix.IXANY s.termios.Iflag &^= unix.INPCK s.termios.Iflag &^= unix.IGNPAR s.termios.Iflag &^= unix.PARMRK s.termios.Iflag &^= unix.ISTRIP s.termios.Iflag &^= unix.IGNBRK s.termios.Iflag &^= unix.BRKINT s.termios.Iflag &^= unix.INLCR s.termios.Iflag &^= unix.IGNCR s.termios.Iflag &^= unix.ICRNL s.termios.Iflag &^= tcIUCLC s.termios.Oflag &^= unix.OPOST // Block reads until at least one char is available (no timeout) s.termios.Cc[unix.VMIN] = 1 s.termios.Cc[unix.VTIME] = 0 } go-serial-2.3.0/unixutils/000077500000000000000000000000001401544173500154465ustar00rootroot00000000000000go-serial-2.3.0/unixutils/select_unix.go000066400000000000000000000050171401544173500203220ustar00rootroot00000000000000// // Copyright 2014-2017 Cristian Maglie. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // // +build 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 = ©OfRd // Determine max fd. max = rd.max } if wr != nil { // fdsets are copied so the parameters are left untouched copyOfWr := wr.set res.writeable = ©OfWr // Determine max fd. if wr.max > max { max = wr.max } } if er != nil { // fdsets are copied so the parameters are left untouched copyOfEr := er.set res.errors = ©OfEr // Determine max fd. if er.max > max { max = er.max } } err := goselect.Select(int(max+1), res.readable, res.writeable, res.errors, timeout) return res, err } go-serial-2.3.0/zsyscall_windows.go000066400000000000000000000126031401544173500173510ustar00rootroot00000000000000// 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) ) // errnoErr returns common boxed Errno values, to prevent // allocations at runtime. func errnoErr(e syscall.Errno) error { switch e { case 0: return nil case errnoERROR_IO_PENDING: return errERROR_IO_PENDING } // TODO: add more here, after collecting data on the common // error values see on Windows. (perhaps when running // all.bat?) return e } var ( modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") modkernel32 = windows.NewLazySystemDLL("kernel32.dll") procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") procClearCommError = modkernel32.NewProc("ClearCommError") procGetCommState = modkernel32.NewProc("GetCommState") procSetCommState = modkernel32.NewProc("SetCommState") procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction") procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus") procSetCommMask = modkernel32.NewProc("SetCommMask") procCreateEventW = modkernel32.NewProc("CreateEventW") procResetEvent = modkernel32.NewProc("ResetEvent") procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") procPurgeComm = modkernel32.NewProc("PurgeComm") ) func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) { 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 clearCommError(handle syscall.Handle, lpErrors *uint32, lpStat *comstat) (err error) { r1, _, e1 := syscall.Syscall(procClearCommError.Addr(), 3, uintptr(handle), uintptr(unsafe.Pointer(lpErrors)), uintptr(unsafe.Pointer(lpStat))) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func getCommState(handle syscall.Handle, dcb *dcb) (err error) { r1, _, e1 := syscall.Syscall(procGetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func setCommState(handle syscall.Handle, dcb *dcb) (err error) { r1, _, e1 := syscall.Syscall(procSetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func setCommTimeouts(handle syscall.Handle, timeouts *commTimeouts) (err error) { r1, _, e1 := syscall.Syscall(procSetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } 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 setCommMask(handle syscall.Handle, mask uint32) (err error) { r1, _, e1 := syscall.Syscall(procSetCommMask.Addr(), 2, uintptr(handle), uintptr(mask), 0) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func createEvent(eventAttributes *uint32, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { var _p0 uint32 if manualReset { _p0 = 1 } else { _p0 = 0 } var _p1 uint32 if initialState { _p1 = 1 } else { _p1 = 0 } r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(eventAttributes)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) handle = syscall.Handle(r0) if handle == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func resetEvent(handle syscall.Handle) (err error) { r1, _, e1 := syscall.Syscall(procResetEvent.Addr(), 1, uintptr(handle), 0, 0) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func getOverlappedResult(handle syscall.Handle, overlapEvent *syscall.Overlapped, n *uint32, wait bool) (err error) { var _p0 uint32 if wait { _p0 = 1 } else { _p0 = 0 } r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapEvent)), uintptr(unsafe.Pointer(n)), uintptr(_p0), 0, 0) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return } func purgeComm(handle syscall.Handle, flags uint32) (err error) { r1, _, e1 := syscall.Syscall(procPurgeComm.Addr(), 2, uintptr(handle), uintptr(flags), 0) if r1 == 0 { if e1 != 0 { err = errnoErr(e1) } else { err = syscall.EINVAL } } return }