pax_global_header00006660000000000000000000000064144136565410014523gustar00rootroot0000000000000052 comment=e9c0658fe77a9f850a8ba1043c21c092caa15c3c golang-github-albenik-go-serial-2.6.0/000077500000000000000000000000001441365654100175425ustar00rootroot00000000000000golang-github-albenik-go-serial-2.6.0/.editorconfig000066400000000000000000000010161441365654100222150ustar00rootroot00000000000000root = true [*] charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.go] indent_style = tab indent_size = tab ij_go_add_parentheses_for_single_import = true ij_go_remove_redundant_import_aliases = true ij_go_import_sorting = goimports ij_go_move_all_imports_in_one_declaration = true ij_go_group_stdlib_imports = true ij_go_move_all_stdlib_imports_in_one_group = true ij_go_local_group_mode = project ij_go_GROUP_CURRENT_PROJECT_IMPORTS = true [*.{yml, yaml}] indent_style = space indent_size = 2 golang-github-albenik-go-serial-2.6.0/.github/000077500000000000000000000000001441365654100211025ustar00rootroot00000000000000golang-github-albenik-go-serial-2.6.0/.github/workflows/000077500000000000000000000000001441365654100231375ustar00rootroot00000000000000golang-github-albenik-go-serial-2.6.0/.github/workflows/codeql-analysis.yml000066400000000000000000000044071441365654100267570ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ v* ] pull_request: branches: [ v* ] schedule: - cron: '41 1 * * 3' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'go' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://git.io/codeql-language-support steps: - name: Checkout repository uses: actions/checkout@v2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v1 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # âœī¸ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 golang-github-albenik-go-serial-2.6.0/.github/workflows/golang.yml000066400000000000000000000054061441365654100251360ustar00rootroot00000000000000name: Go on: push: branches: [ v* ] pull_request: branches: [ v* ] jobs: # Test tests: name: Test go${{ matrix.go }} on ${{ matrix.os }} strategy: matrix: os: - 'ubuntu-latest' - 'macos-latest' - 'windows-latest' go: - '1.18' - '1.19' - '1.20' 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 ./... # Lint # lint: # name: Lint go${{ matrix.go }} # # strategy: # matrix: # go: # - 1.16 # - 1.17 # - 1.18 # # runs-on: ubuntu-latest # # steps: # - uses: actions/checkout@v2 # - uses: actions/setup-go@v2 # with: # go-version: ${{ matrix.go }} # - uses: golangci/golangci-lint-action@v2 # with: # version: v1.45 # Build build: name: Build ${{ matrix.goos }}/${{ matrix.goarch }} on ${{ matrix.os }} strategy: matrix: # `go tool dist list` with many exclusions include: - { os: ubuntu-latest, goos: linux, goarch: amd64 } - { os: ubuntu-latest, goos: linux, goarch: arm64 } - { os: ubuntu-latest, goos: linux, goarch: ppc64le } - { os: ubuntu-latest, goos: android, goarch: amd64 } - { os: ubuntu-latest, goos: android, goarch: arm64 } - { os: ubuntu-latest, goos: freebsd, goarch: amd64 } - { os: ubuntu-latest, goos: freebsd, goarch: arm64 } - { os: ubuntu-latest, goos: openbsd, goarch: amd64 } - { os: ubuntu-latest, goos: openbsd, goarch: arm64 } # MacOS IOKit required, so no cross-build available # - { os: ubuntu-latest, goos: darwin, goarch: amd64 } # - { os: ubuntu-latest, goos: darwin, goarch: arm64 } - { os: macos-latest, goos: darwin, goarch: amd64 } # Currently `macos-latest` is equeal to `macos-11` which does not support arm64 # - { os: macos-latest, goos: darwin, goarch: arm64 } - { os: windows-latest, goos: windows, goarch: amd64 } - { os: windows-latest, goos: windows, goarch: arm64 } runs-on: ${{ matrix.os }} env: GOOS: ${{ matrix.goos }} GOARCH: ${{ matrix.goarch }} steps: - name: Checkout uses: actions/checkout@v3 - name: Setup Go uses: actions/setup-go@v3 with: go-version: '1.20' - name: Get dependencies run: go mod download - name: Build run: go build golang-github-albenik-go-serial-2.6.0/.gitignore000066400000000000000000000003001441365654100215230ustar00rootroot00000000000000# 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 golang-github-albenik-go-serial-2.6.0/.golangci.yml000066400000000000000000000073741441365654100221410ustar00rootroot00000000000000run: timeout: 3m issues-exit-code: 1 concurrency: 4 allow-parallel-runners: true tests: true output: format: colored-line-number print-issued-lines: true print-linter-name: true linters-settings: cyclop: max-complexity: 20 package-average: 0.0 # the maximal average package complexity. If it's higher than 0.0 (float) the check is enabled (default 0.0) skip-tests: true funlen: lines: 60 statements: 40 golint: min-confidence: 0.9 gci: no-inline-comments: true no-prefix-comments: true sections: - standard - default - prefix(github.com/albenik/go-serial) section-separators: - newLine lll: line-length: 140 linters: enable-all: true # Please keep in order disable: - deadcode # The linter 'deadcode' is deprecated (since v1.49.0) due to: The owner seems to have abandoned the linter. Replaced by unused. - exhaustruct # Unecessary annoying! - exhaustivestruct # Unecessary annoying! - goerr113 # Useful but not in this package! - gochecknoglobals # Too paranoic! - goimports # Latest gci with sections is good enougth - golint # The linter 'golint' is deprecated (since v1.41.0) due to: The repository of the linter has been archived by the owner. Replaced by revive. - ifshort # The linter 'ifshort' is deprecated (since v1.48.0) due to: The repository of the linter has been deprecated by the owner. - interfacer # The linter 'interfacer' is deprecated (since v1.38.0) due to: The repository of the linter has been archived by the owner. - ireturn # Yes! Some functions retures interfaces. - maligned # The linter 'maligned' is deprecated (since v1.38.0) due to: The repository of the linter has been archived by the owner. Replaced by govet 'fieldalignment'. - nlreturn # Personally I hate mandatory blank lines before returns. - nosnakecase # The linter 'nosnakecase' is deprecated (since v1.48.1) due to: The repository of the linter has been deprecated by the owner. Replaced by revive(var-naming). - paralleltest - rowserrcheck # rowserrcheck is disabled because of generics. You can track the evolution of the generics support by following the https://github.com/golangci/golangci-lint/issues/2649. - scopelint # The linter 'scopelint' is deprecated (since v1.39.0) due to: The repository of the linter has been deprecated by the owner. Replaced by exportloopref. - structcheck # structcheck is disabled because of generics. You can track the evolution of the generics support by following the https://github.com/golangci/golangci-lint/issues/2649. - testableexamples # New linetr, project is not adapted for this linter yet. - varcheck # The linter 'varcheck' is deprecated (since v1.49.0) due to: The owner seems to have abandoned the linter. Replaced by unused. - varnamelen # Annoying! - unparam # Panics with GOOS=linux - wastedassign # wastedassign is disabled because of generics. You can track the evolution of the generics support by following the https://github.com/golangci/golangci-lint/issues/2649. - wrapcheck # Not needed! - wsl # Unecessary annoying! issues: exclude-rules: - path: _test\.go linters: - gochecknoglobals - funlen - lll - wrapcheck - path: enumerator/ linters: - gci - gofumpt - revive - text: "Line contains TODO/BUG/FIXME" linters: - godox - source: "//go:generate" linters: - lll - source: "C\\." linters: - unconvert - source: "(?:dataBits:\\s+\\d+|baudRate:\\s+\\d+)" linters: - gomnd golang-github-albenik-go-serial-2.6.0/CHANGELOG.md000066400000000000000000000035341441365654100213600ustar00rootroot00000000000000# Changelog ## 2.6.0 * `go mod tudy -go 1.18`. * CI Tests: `go1.18`, `go1.19`, `go1.20`. * CI Cross-build: cleanup. * `golangci-lint` added & code cleaned. * obsolete `darwin/386` code removed. ## 2.5.1 * `ppc64le` build supported [#33](https://github.com/albenik/go-serial/pull/33). ## 2.5.0 * `GOOS=android` build supported [#29](https://github.com/albenik/go-serial/issues/29). * Unused second argument for unix build in method `Port.SetTimeoutEx()` was made optional in backward compatibility manner. * `go 1.13` errors supported: `PortError.Unwrap()` method added, `PortError.Cause()` method marked as deprecated. ## 2.4.0 * `GOOS=darwin GOARCH=arm64` build supported [#25](https://github.com/albenik/go-serial/pull/25). * Fixed regression in `GOOS=darwin` build was introduced in `v2.3.0` ## 2.3.0 * Some fixes backported from https://github.com/bugst/go-serial [#22](https://github.com/albenik/go-serial/pull/22). ## 2.2.0 * `PortError.Cause()` method added ## 2.1.0 * MacOS extended baudrate support added [#14](https://github.com/albenik/go-serial/pull/14). * MacOS wrong generated syscall fixed [#15](https://github.com/albenik/go-serial/issues/15). ## 2.0.0 * New Go Module import path `github.com/albenik/go-serial/v2` * `serial.Port` interface discarded 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` [#7](https://github.com/albenik/go-serial/pull/7) * `WithHUPCL(bool)` option introduced * Minor bugfix & refactoring ## 1.x.x * Forked from https://github.com/bugst/go-serial * Minor but incompatible interface & logic changes implemented * Import path altered golang-github-albenik-go-serial-2.6.0/LICENSE000066400000000000000000000027611441365654100205550ustar00rootroot00000000000000BSD 3-Clause License Copyright (c) 2019-2022, 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. golang-github-albenik-go-serial-2.6.0/README.md000066400000000000000000000017101441365654100210200ustar00rootroot00000000000000# github.com/albenik/go-serial/v2 ![Go](https://github.com/albenik/go-serial/workflows/Go/badge.svg) 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 ``` ## MacOS build note * Since version **v2.1.0** the macos build requires `IOKit` as dependency and is only possible on Mac with cgo enabled. * Apple M1 (darwin/arm64) is supported. _(Thanks to [martinhpedersen](https://github.com/albenik/go-serial/pull/25))_ ## Documentation and examples See the godoc here: https://pkg.go.dev/github.com/albenik/go-serial/v2 ## License The software is release under a BSD 3-clause license golang-github-albenik-go-serial-2.6.0/constants_darwin.go000066400000000000000000000012671441365654100234570ustar00rootroot00000000000000// // 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 ( devicesBasePath = "/dev" regexFilter = "^(cu|tty)\\..*" ioctlTcflsh = unix.TIOCFLUSH tcCMSPAR uint64 = 0 // may be CMSPAR or PAREXT tcIUCLC uint64 = 0 tcCCTS_OFLOW uint64 = 0x00010000 //nolint:revive,stylecheck tcCRTS_IFLOW uint64 = 0x00020000 //nolint:revive,stylecheck tcCRTSCTS = tcCCTS_OFLOW | tcCRTS_IFLOW ) var databitsMap = map[int]uint64{ 0: unix.CS8, // Default to 8 bits 5: unix.CS5, 6: unix.CS6, 7: unix.CS7, 8: unix.CS8, } golang-github-albenik-go-serial-2.6.0/constants_freebsd.go000066400000000000000000000023611441365654100236010ustar00rootroot00000000000000// // 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 ( devicesBasePath = "/dev" regexFilter = "^(cu|tty)\\..*" tcCMSPAR uint32 = 0 // may be CMSPAR or PAREXT tcIUCLC uint32 = 0 tcCCTS_OFLOW uint32 = 0x00010000 //nolint:revive,stylecheck // tcCRTS_IFLOW uint32 = 0x00020000 //nolint:revive,stylecheck tcCRTSCTS = tcCCTS_OFLOW ioctlTcflsh = unix.TIOCFLUSH ) 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, } databitsMap = map[int]uint32{ 0: unix.CS8, // Default to 8 bits 5: unix.CS5, 6: unix.CS6, 7: unix.CS7, 8: unix.CS8, } ) func toTermiosSpeedType(speed uint32) uint32 { return speed } golang-github-albenik-go-serial-2.6.0/constants_linux.go000066400000000000000000000025531441365654100233310ustar00rootroot00000000000000// // 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 ( devicesBasePath = "/dev" regexFilter = "(ttyS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO)[0-9]{1,3}" tcCMSPAR = unix.CMSPAR tcIUCLC = unix.IUCLC tcCRTSCTS uint32 = unix.CRTSCTS ioctlTcflsh = unix.TCFLSH ) 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, } databitsMap = map[int]uint32{ 0: unix.CS8, // Default to 8 bits 5: unix.CS5, 6: unix.CS6, 7: unix.CS7, 8: unix.CS8, } ) golang-github-albenik-go-serial-2.6.0/constants_openbsd.go000066400000000000000000000023371441365654100236240ustar00rootroot00000000000000// // 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 ( devicesBasePath = "/dev" regexFilter = "^(cu|tty)\\..*" tcCMSPAR uint32 = 0 // may be CMSPAR or PAREXT tcIUCLC uint32 = 0 tcCCTS_OFLOW uint32 = 0x00010000 //nolint:revive,stylecheck // tcCRTS_IFLOW uint32 = 0x00020000 //nolint:revive,stylecheck tcCRTSCTS = tcCCTS_OFLOW ioctlTcflsh = unix.TIOCFLUSH ) 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, } func toTermiosSpeedType(speed uint32) int32 { return int32(speed) } golang-github-albenik-go-serial-2.6.0/doc.go000066400000000000000000000057401441365654100206440ustar00rootroot00000000000000// // 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 golang-github-albenik-go-serial-2.6.0/enumerator/000077500000000000000000000000001441365654100217235ustar00rootroot00000000000000golang-github-albenik-go-serial-2.6.0/enumerator/doc.go000066400000000000000000000014651441365654100230250ustar00rootroot00000000000000// // 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 golang-github-albenik-go-serial-2.6.0/enumerator/enumerator.go000066400000000000000000000023621441365654100244360ustar00rootroot00000000000000// // 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 golang.org/x/sys/windows/mkwinsyscall -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 } golang-github-albenik-go-serial-2.6.0/enumerator/example_getdetailedportlist_test.go000066400000000000000000000012521441365654100311000ustar00rootroot00000000000000// // 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) } } } golang-github-albenik-go-serial-2.6.0/enumerator/syscall_windows.go000066400000000000000000000022351441365654100255000ustar00rootroot00000000000000package 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 golang-github-albenik-go-serial-2.6.0/enumerator/usb_darwin.go000066400000000000000000000143631441365654100244160ustar00rootroot00000000000000// // 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. // //go: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) { ports := make([]*PortDetails, 0, 8) //nolint:gomnd 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: %w", err) } 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 %v)", 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) //nolint:gomnd 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 { //nolint:gocritic 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) //nolint:gomnd C.IOObjectGetClass(*o, &class[0]) return C.GoString(&class[0]) } golang-github-albenik-go-serial-2.6.0/enumerator/usb_freebsd.go000066400000000000000000000004541441365654100245400ustar00rootroot00000000000000// // 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{} } golang-github-albenik-go-serial-2.6.0/enumerator/usb_linux.go000066400000000000000000000046561441365654100242750ustar00rootroot00000000000000// // 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 } serialNum, err := readLine(filepath.Join(usbDevicePath, "serial")) if err != nil { return err } details.IsUSB = true details.VID = vid details.PID = pid details.SerialNumber = serialNum 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 } golang-github-albenik-go-serial-2.6.0/enumerator/usb_ole_windows.go000066400000000000000000000055571441365654100254700ustar00rootroot00000000000000// // 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. // //go: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 } golang-github-albenik-go-serial-2.6.0/enumerator/usb_windows.go000066400000000000000000000240471441365654100246240ustar00rootroot00000000000000// // 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 } golang-github-albenik-go-serial-2.6.0/enumerator/usb_windows_test.go000066400000000000000000000031471441365654100256610ustar00rootroot00000000000000// // 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) } golang-github-albenik-go-serial-2.6.0/enumerator/zsyscall_windows.go000066400000000000000000000103241441365654100256700ustar00rootroot00000000000000// 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) errERROR_EINVAL error = syscall.EINVAL ) // errnoErr returns common boxed Errno values, to prevent // allocations at runtime. func errnoErr(e syscall.Errno) error { switch e { case 0: return errERROR_EINVAL case errnoERROR_IO_PENDING: return errERROR_IO_PENDING } // TODO: add more here, after collecting data on the common // error values see on Windows. (perhaps when running // all.bat?) return e } var ( modsetupapi = windows.NewLazySystemDLL("setupapi.dll") procSetupDiClassGuidsFromNameW = modsetupapi.NewProc("SetupDiClassGuidsFromNameW") procSetupDiDestroyDeviceInfoList = modsetupapi.NewProc("SetupDiDestroyDeviceInfoList") procSetupDiEnumDeviceInfo = modsetupapi.NewProc("SetupDiEnumDeviceInfo") procSetupDiGetClassDevsW = modsetupapi.NewProc("SetupDiGetClassDevsW") procSetupDiGetDeviceInstanceIdW = modsetupapi.NewProc("SetupDiGetDeviceInstanceIdW") procSetupDiGetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiGetDeviceRegistryPropertyW") procSetupDiOpenDevRegKey = modsetupapi.NewProc("SetupDiOpenDevRegKey") ) 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 { err = errnoErr(e1) } return } func setupDiDestroyDeviceInfoList(set hDevInfo) (err error) { r1, _, e1 := syscall.Syscall(procSetupDiDestroyDeviceInfoList.Addr(), 1, uintptr(set), 0, 0) if r1 == 0 { err = errnoErr(e1) } 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 { err = errnoErr(e1) } 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 { err = errnoErr(e1) } 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 { err = errnoErr(e1) } 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 } 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 { err = errnoErr(e1) } return } golang-github-albenik-go-serial-2.6.0/errors.go000066400000000000000000000062361441365654100214140ustar00rootroot00000000000000package serial // PortErrorCode is a code to easily identify the type of error. type PortErrorCode int const ( PortErrorUnknown PortErrorCode = iota // PortBusy the serial port is already in used by another process. PortBusy // PortNotFound the requested port doesn't exist. PortNotFound // InvalidSerialPort the requested port is not a serial port. InvalidSerialPort // PermissionDenied the user doesn't have enough priviledges. PermissionDenied // InvalidSpeed the requested speed is not valid or not supported. InvalidSpeed // InvalidDataBits the number of data bits is not valid or not supported. InvalidDataBits // InvalidParity the selected parity is not valid or not supported. InvalidParity // InvalidStopBits the selected number of stop bits is not valid or not supported. InvalidStopBits // InvalidTimeoutValue 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 // OsError Operating system function error. OsError // WriteFailed Port write failed. WriteFailed // ReadFailed Port read failed. ReadFailed ) // PortError is a platform independent error type for serial ports. type PortError struct { code PortErrorCode wrapped error } // EncodedErrorString returns a string explaining the error code. func (e PortError) EncodedErrorString() string { switch e.code { case PortErrorUnknown: return "error code not set" 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 ReadFailed: return "read filed" 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.wrapped != nil { return e.EncodedErrorString() + ": " + e.wrapped.Error() } return e.EncodedErrorString() } func (e PortError) Unwrap() error { return e.wrapped } // 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 // Deprecated: Use go1.13 error iterface Unwrap() instead. func (e PortError) Cause() error { return e.Unwrap() } func newPortOSError(err error) *PortError { return &PortError{code: OsError, wrapped: err} } golang-github-albenik-go-serial-2.6.0/example_getportlist_test.go000066400000000000000000000007761441365654100252350ustar00rootroot00000000000000// // 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) } } } golang-github-albenik-go-serial-2.6.0/example_modem_bits_test.go000066400000000000000000000027071441365654100247730ustar00rootroot00000000000000// // 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.Println(err) // DO NOT USER log.Fatal or `port.Close()` deferred call will never happened! return } fmt.Printf("Status: %+v\n", status) time.Sleep(time.Second) count++ if count == 5 { if err = port.SetDTR(false); err != nil { log.Println(err) return } fmt.Println("Set DTR OFF") } if count == 10 { if err = port.SetDTR(true); err != nil { log.Println(err) return } fmt.Println("Set DTR ON") } if count == 15 { if err = port.SetRTS(false); err != nil { log.Println(err) return } fmt.Println("Set RTS OFF") } if count == 20 { if err = port.SetRTS(true); err != nil { log.Println(err) return } fmt.Println("Set RTS ON") } } } golang-github-albenik-go-serial-2.6.0/example_serialport_test.go000066400000000000000000000012401441365654100250240ustar00rootroot00000000000000// // 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") } golang-github-albenik-go-serial-2.6.0/example_test.go000066400000000000000000000027011441365654100225630ustar00rootroot00000000000000// // 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])) } } golang-github-albenik-go-serial-2.6.0/go.mod000066400000000000000000000007361441365654100206560ustar00rootroot00000000000000module github.com/albenik/go-serial/v2 go 1.18 require ( github.com/creack/goselect v0.1.2 github.com/stretchr/testify v1.7.1 go.uber.org/multierr v1.9.0 golang.org/x/sys v0.5.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect go.uber.org/atomic v1.10.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) golang-github-albenik-go-serial-2.6.0/go.sum000066400000000000000000000046451441365654100207060ustar00rootroot00000000000000github.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.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-github-albenik-go-serial-2.6.0/options.go000066400000000000000000000015011441365654100215610ustar00rootroot00000000000000// // Copyright 2019-2022 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 } } golang-github-albenik-go-serial-2.6.0/serial.go000066400000000000000000000045171441365654100213570ustar00rootroot00000000000000// // Copyright 2014-2018 Cristian Maglie. // Copyright 2019-2022 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, wrapped: 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, } } golang-github-albenik-go-serial-2.6.0/serial_open_android.go000066400000000000000000000011071441365654100240700ustar00rootroot00000000000000// // Copyright 2014-2018 Cristian Maglie. All rights reserved. // Copyright 2019-2022 Veniamin Albaev .. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // //go:build android package serial import ( "go.uber.org/multierr" "golang.org/x/sys/unix" ) func accquireExclusiveAccess(_ int) error { return nil } func (p *Port) closeAndReturnError(code PortErrorCode, err error) *PortError { return &PortError{ code: code, wrapped: multierr.Combine( err, unix.Close(p.internal.handle), ), } } golang-github-albenik-go-serial-2.6.0/serial_open_unix.go000066400000000000000000000013161441365654100234350ustar00rootroot00000000000000// // Copyright 2014-2018 Cristian Maglie. All rights reserved. // Copyright 2019-2022 Veniamin Albaev . // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // //go:build (linux && !android) || darwin || freebsd || openbsd package serial import ( "go.uber.org/multierr" "golang.org/x/sys/unix" ) func accquireExclusiveAccess(h int) error { return unix.IoctlSetInt(h, unix.TIOCEXCL, 0) } func (p *Port) closeAndReturnError(code PortErrorCode, err error) *PortError { return &PortError{ code: code, wrapped: multierr.Combine( err, unix.IoctlSetInt(p.internal.handle, unix.TIOCNXCL, 0), unix.Close(p.internal.handle), ), } } golang-github-albenik-go-serial-2.6.0/serial_termsettings_android.go000066400000000000000000000012661441365654100256650ustar00rootroot00000000000000// // 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. // //go:build android package serial import ( "golang.org/x/sys/unix" ) func (p *Port) retrieveTermSettings() (s *settings, err error) { s = &settings{ termios: new(unix.Termios), } if s.termios, err = unix.IoctlGetTermios(p.internal.handle, unix.TCGETS); err != nil { return nil, newPortOSError(err) } return s, nil } func (p *Port) applyTermSettings(s *settings) error { if err := unix.IoctlSetTermios(p.internal.handle, unix.TCSETS, s.termios); err != nil { return newPortOSError(err) } return nil } golang-github-albenik-go-serial-2.6.0/serial_termsettings_bsd.go000066400000000000000000000013111441365654100250040ustar00rootroot00000000000000// // 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. // //go: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, newPortOSError(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 newPortOSError(err) } return nil } golang-github-albenik-go-serial-2.6.0/serial_termsettings_darwin.go000066400000000000000000000020471441365654100255270ustar00rootroot00000000000000// // 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. // //go:build darwin package serial // #include // #include import "C" import ( "unsafe" "golang.org/x/sys/unix" ) func (p *Port) retrieveTermSettings() (*settings, error) { s := &settings{termios: new(unix.Termios), specificBaudrate: 0} var err error if s.termios, err = unix.IoctlGetTermios(p.internal.handle, unix.TIOCGETA); err != nil { return nil, newPortOSError(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 newPortOSError(err) } speed := s.specificBaudrate if err := unix.IoctlSetPointerInt(p.internal.handle, C.IOSSIOSPEED, speed); err != nil { return newPortOSError(err) } return nil } golang-github-albenik-go-serial-2.6.0/serial_termsettings_linux.go000066400000000000000000000017361441365654100254060ustar00rootroot00000000000000// // 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. // //go:build linux && !android && !ppc64le package serial import ( "golang.org/x/sys/unix" ) func (p *Port) retrieveTermSettings() (s *settings, err error) { s = &settings{ termios: new(unix.Termios), } if s.termios, err = unix.IoctlGetTermios(p.internal.handle, unix.TCGETS); err != nil { return nil, newPortOSError(err) } if s.termios.Cflag&unix.BOTHER == unix.BOTHER { if s.termios, err = unix.IoctlGetTermios(p.internal.handle, unix.TCGETS2); err != nil { return nil, newPortOSError(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 newPortOSError(err) } return nil } golang-github-albenik-go-serial-2.6.0/serial_termsettings_linux_ppc64le.go000066400000000000000000000016311441365654100267350ustar00rootroot00000000000000// // 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. // // linux/ppc64le specific implementation, that does not use TCGETS2 or TCSETS2, // as they are not supported by golang v1.18 for that platform circa 2022-03. // Code is identical to android implementation. //go:build linux && !android && ppc64le package serial import ( "golang.org/x/sys/unix" ) func (p *Port) retrieveTermSettings() (s *settings, err error) { s = &settings{ termios: new(unix.Termios), } if s.termios, err = unix.IoctlGetTermios(p.internal.handle, unix.TCGETS); err != nil { return nil, newPortOSError(err) } return s, nil } func (p *Port) applyTermSettings(s *settings) error { if err := unix.IoctlSetTermios(p.internal.handle, unix.TCSETS, s.termios); err != nil { return newPortOSError(err) } return nil } golang-github-albenik-go-serial-2.6.0/serial_test.go000066400000000000000000000057601441365654100224170ustar00rootroot00000000000000package serial_test import ( "os" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/albenik/go-serial/v2" ) func TestPortNilReciever_Error(t *testing.T) { checkError := func(err error) { var portErr *serial.PortError require.ErrorAs(t, err, &portErr) assert.Equal(t, serial.PortClosed, portErr.Code()) assert.ErrorIs(t, err, os.ErrInvalid) } t.Run("Close", func(t *testing.T) { checkError((*serial.Port)(nil).Close()) }) t.Run("Reconfigure", func(t *testing.T) { checkError((*serial.Port)(nil).Reconfigure()) }) t.Run("ReadyToRead", func(t *testing.T) { _, err := (*serial.Port)(nil).ReadyToRead() checkError(err) }) t.Run("Read", func(t *testing.T) { _, err := (*serial.Port)(nil).Read(make([]byte, 16)) checkError(err) }) t.Run("Write", func(t *testing.T) { _, err := (*serial.Port)(nil).Write([]byte{1, 2, 3, 4, 5, 6, 7, 8}) checkError(err) }) t.Run("ResetInputBuffer", func(t *testing.T) { checkError((*serial.Port)(nil).ResetInputBuffer()) }) t.Run("ResetOutputBuffer", func(t *testing.T) { checkError((*serial.Port)(nil).ResetOutputBuffer()) }) t.Run("SetDTR", func(t *testing.T) { checkError((*serial.Port)(nil).SetDTR(false)) }) t.Run("SetRTS", func(t *testing.T) { checkError((*serial.Port)(nil).SetRTS(false)) }) t.Run("SetReadTimeout", func(t *testing.T) { checkError((*serial.Port)(nil).SetReadTimeout(1)) }) t.Run("SetReadTimeoutEx", func(t *testing.T) { checkError((*serial.Port)(nil).SetReadTimeoutEx(1, 0)) }) t.Run("SetFirstByteReadTimeout", func(t *testing.T) { checkError((*serial.Port)(nil).SetFirstByteReadTimeout(1)) }) t.Run("SetWriteTimeout", func(t *testing.T) { checkError((*serial.Port)(nil).SetWriteTimeout(1)) }) t.Run("GetModemStatusBits", func(t *testing.T) { _, err := (*serial.Port)(nil).GetModemStatusBits() checkError(err) }) } func TestPortTestPortNilReciever_String(t *testing.T) { var p *serial.Port assert.Equal(t, "Error: port instance", p.String()) } /* func TestSerial_Operations(t *testing.T) { ports, err := serial.GetPortsList() require.NoError(t, err) if len(ports) == 0 { t.Log("No serial ports found") } for _, name := range ports { t.Logf("Found port: %q", name) port, err := serial.Open(name, 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 { t.Logf("Port %q opened", name) if assert.NoError(t, port.Close()) { t.Logf("Port %q closed without errors", name) } continue } var portErr serial.PortError expected := errors.As(err, &portErr) && !in(portErr.Code(), serial.OsError, serial.InvalidSerialPort) if !expected { assert.NoError(t, err) } } } func in(code serial.PortErrorCode, list ...serial.PortErrorCode) bool { for _, c := range list { if code == c { return true } } return false } */ golang-github-albenik-go-serial-2.6.0/serial_unix.go000066400000000000000000000240171441365654100224170ustar00rootroot00000000000000// // Copyright 2014-2018 Cristian Maglie. All rights reserved. // Copyright 2019-2022 Veniamin Albaev . // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // //go:build linux || darwin || freebsd || openbsd package serial import ( "errors" "io/ioutil" "path" "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} portNameRx = regexp.MustCompile(regexFilter) ) 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 { case errors.Is(err, unix.EBUSY): return nil, &PortError{code: PortBusy} case errors.Is(err, unix.EACCES): return nil, &PortError{code: PermissionDenied} default: return nil, err } } // does nothing in build for android if err = accquireExclusiveAccess(h); err != nil { return nil, newPortOSError(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) 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 newPortOSError(err) } return nil } func (p *Port) Reconfigure(opts ...Option) error { for _, o := range opts { o(p) } return p.reconfigure() } 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, newPortOSError(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 errors.Is(err, unix.EINTR) { continue } return read, newPortOSError(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 errors.Is(err, unix.EINTR) { continue } return read, newPortOSError(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, newPortOSError(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, newPortOSError(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 newPortOSError(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 newPortOSError(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 } // SetReadTimeoutEx Sets advanced timeouts. // Second argument was forget here due refactoring and keeping now for backward compatibility. // TODO Remove second argument in version v3. func (p *Port) SetReadTimeoutEx(t uint32, _ ...uint32) error { if err := p.checkValid(); err != nil { return err } s, err := p.retrieveTermSettings() if err != nil { return err // port.retrieveTermSettings() already returned PortError } //nolint:gomnd 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 } 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) 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, newPortOSError(err) } return s, nil } func (p *Port) applyModemBitsStatus(status int) error { if err := unix.IoctlSetInt(p.internal.handle, unix.TIOCMSET, status); err != nil { return newPortOSError(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(devicesBasePath) 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 if !portNameRx.MatchString(f.Name()) { continue } name := path.Join(devicesBasePath, f.Name()) // Check if serial port is real or is a placeholder serial port "ttySxx" if strings.HasPrefix(f.Name(), "ttyS") { if port, err := Open(name); err != nil { var portErr *PortError if errors.As(err, &portErr) && portErr.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 } golang-github-albenik-go-serial-2.6.0/serial_windows.go000066400000000000000000000276601441365654100231350ustar00rootroot00000000000000// // Copyright 2014-2018 Cristian Maglie. // Copyright 2019-2022 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) 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, wrapped: err} } return 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) 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, wrapped: 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, wrapped: nil} } handle := p.internal.handle errs := new(uint32) stat := new(comstat) if err := clearCommError(handle, errs, stat); err != nil { return 0, &PortError{code: InvalidSerialPort, wrapped: 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, wrapped: 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, wrapped: err} } err = getOverlappedResult(handle, overlapped, &read, true) if err != nil && err != syscall.ERROR_OPERATION_ABORTED { return 0, &PortError{code: OsError, wrapped: 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, wrapped: 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{wrapped: err} } params.Flags &= dcbDTRControlDisableMask if dtr { params.Flags |= dcbDTRControlEnable } if err := setCommState(p.internal.handle, params); err != nil { return &PortError{wrapped: 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{wrapped: err} } params.Flags &= dcbRTSControlDisableMask if rts { params.Flags |= dcbRTSControlEnable } if err := setCommState(p.internal.handle, params); err != nil { return &PortError{wrapped: err} } return 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) 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) 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, wrapped: err} } if err := setCommMask(p.internal.handle, evErr); err != nil { p.Close() return &PortError{code: InvalidSerialPort, wrapped: err} } params := &dcb{} if err := getCommState(p.internal.handle, params); err != nil { p.Close() return &PortError{code: InvalidSerialPort, wrapped: 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, wrapped: 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 } golang-github-albenik-go-serial-2.6.0/syscall_windows.go000066400000000000000000000122471441365654100233230ustar00rootroot00000000000000// // 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 } } golang-github-albenik-go-serial-2.6.0/termios_baudrate_bsd.go000066400000000000000000000011421441365654100242500ustar00rootroot00000000000000// // 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. // //go: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 } golang-github-albenik-go-serial-2.6.0/termios_baudrate_darwin.go000066400000000000000000000004531441365654100247700ustar00rootroot00000000000000// // 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. // //go:build darwin package serial func (s *settings) setBaudrate(speed int) error { s.specificBaudrate = speed return nil } golang-github-albenik-go-serial-2.6.0/termios_baudrate_linux.go000066400000000000000000000012631441365654100246430ustar00rootroot00000000000000// // 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. // //go: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 } golang-github-albenik-go-serial-2.6.0/termios_setting_darwin.go000066400000000000000000000005031441365654100246520ustar00rootroot00000000000000// // 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. // //go:build darwin package serial import ( "golang.org/x/sys/unix" ) type settings struct { termios *unix.Termios specificBaudrate int } golang-github-albenik-go-serial-2.6.0/termios_setting_unix.go000066400000000000000000000004711441365654100243550ustar00rootroot00000000000000// // 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. // //go:build linux || freebsd || openbsd package serial import ( "golang.org/x/sys/unix" ) type settings struct { termios *unix.Termios } golang-github-albenik-go-serial-2.6.0/termios_unix.go000066400000000000000000000061211441365654100226160ustar00rootroot00000000000000// // 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. // //go: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 } golang-github-albenik-go-serial-2.6.0/unixutils/000077500000000000000000000000001441365654100216065ustar00rootroot00000000000000golang-github-albenik-go-serial-2.6.0/unixutils/select_unix.go000066400000000000000000000050321441365654100244570ustar00rootroot00000000000000// // 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. // //go:build linux || darwin || freebsd || openbsd package unixutils import ( "time" "github.com/creack/goselect" ) // FDSet is a set of file descriptors suitable for a select call. type FDSet struct { set goselect.FDSet max uintptr } // NewFDSet creates a set of file descriptors suitable for a Select call. func NewFDSet(fds ...int) *FDSet { s := &FDSet{} s.Add(fds...) return s } // Add adds the file descriptors passed as parameter to the FDSet. func (s *FDSet) Add(fds ...int) { for _, fd := range fds { f := uintptr(fd) s.set.Set(f) if f > s.max { s.max = f } } } // FDResultSets contains the result of a Select operation. type FDResultSets struct { readable *goselect.FDSet writeable *goselect.FDSet errors *goselect.FDSet } // IsReadable test if a file descriptor is ready to be read. func (r *FDResultSets) IsReadable(fd int) bool { return r.readable.IsSet(uintptr(fd)) } // IsWritable test if a file descriptor is ready to be written. func (r *FDResultSets) IsWritable(fd int) bool { return r.writeable.IsSet(uintptr(fd)) } // IsError test if a file descriptor is in error state. func (r *FDResultSets) IsError(fd int) bool { return r.errors.IsSet(uintptr(fd)) } // Select performs a select system call, // file descriptors in the rd set are tested for read-events, // file descriptors in the wd set are tested for write-events and // file descriptors in the er set are tested for error-events. // The function will block until an event happens or the timeout expires. // The function return an FDResultSets that contains all the file descriptor // that have a pending read/write/error event. func Select(rd, wr, er *FDSet, timeout time.Duration) (*FDResultSets, error) { max := uintptr(0) res := &FDResultSets{} if rd != nil { // fdsets are copied so the parameters are left untouched copyOfRd := rd.set res.readable = ©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 } golang-github-albenik-go-serial-2.6.0/zsyscall_windows.go000066400000000000000000000116201441365654100235070ustar00rootroot00000000000000// Code generated by 'go generate'; DO NOT EDIT. package serial import ( "syscall" "unsafe" "golang.org/x/sys/windows" ) var _ unsafe.Pointer // Do the interface allocations only once for common // Errno values. const ( errnoERROR_IO_PENDING = 997 ) var ( errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) errERROR_EINVAL error = syscall.EINVAL ) // errnoErr returns common boxed Errno values, to prevent // allocations at runtime. func errnoErr(e syscall.Errno) error { switch e { case 0: return errERROR_EINVAL case errnoERROR_IO_PENDING: return errERROR_IO_PENDING } // TODO: add more here, after collecting data on the common // error values see on Windows. (perhaps when running // all.bat?) return e } var ( modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") modkernel32 = windows.NewLazySystemDLL("kernel32.dll") procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") procClearCommError = modkernel32.NewProc("ClearCommError") procCreateEventW = modkernel32.NewProc("CreateEventW") procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction") procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus") procGetCommState = modkernel32.NewProc("GetCommState") procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") procPurgeComm = modkernel32.NewProc("PurgeComm") procResetEvent = modkernel32.NewProc("ResetEvent") procSetCommMask = modkernel32.NewProc("SetCommMask") procSetCommState = modkernel32.NewProc("SetCommState") procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") ) func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) { r0, _, _ := syscall.Syscall9(procRegEnumValueW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(class)), uintptr(unsafe.Pointer(value)), uintptr(unsafe.Pointer(valueLen)), 0) if r0 != 0 { regerrno = syscall.Errno(r0) } return } func 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 { err = errnoErr(e1) } return } func createEvent(eventAttributes *uint32, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { var _p0 uint32 if manualReset { _p0 = 1 } var _p1 uint32 if initialState { _p1 = 1 } r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(eventAttributes)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) handle = syscall.Handle(r0) if handle == 0 { err = errnoErr(e1) } return } func escapeCommFunction(handle syscall.Handle, function uint32) (res bool) { r0, _, _ := syscall.Syscall(procEscapeCommFunction.Addr(), 2, uintptr(handle), uintptr(function), 0) res = r0 != 0 return } func getCommModemStatus(handle syscall.Handle, bits *uint32) (res bool) { r0, _, _ := syscall.Syscall(procGetCommModemStatus.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(bits)), 0) res = r0 != 0 return } func getCommState(handle syscall.Handle, dcb *dcb) (err error) { r1, _, e1 := syscall.Syscall(procGetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0) if r1 == 0 { err = errnoErr(e1) } return } func getOverlappedResult(handle syscall.Handle, overlapEvent *syscall.Overlapped, n *uint32, wait bool) (err error) { var _p0 uint32 if wait { _p0 = 1 } r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapEvent)), uintptr(unsafe.Pointer(n)), uintptr(_p0), 0, 0) if r1 == 0 { err = errnoErr(e1) } return } func purgeComm(handle syscall.Handle, flags uint32) (err error) { r1, _, e1 := syscall.Syscall(procPurgeComm.Addr(), 2, uintptr(handle), uintptr(flags), 0) if r1 == 0 { err = errnoErr(e1) } return } func resetEvent(handle syscall.Handle) (err error) { r1, _, e1 := syscall.Syscall(procResetEvent.Addr(), 1, uintptr(handle), 0, 0) if r1 == 0 { err = errnoErr(e1) } return } func setCommMask(handle syscall.Handle, mask uint32) (err error) { r1, _, e1 := syscall.Syscall(procSetCommMask.Addr(), 2, uintptr(handle), uintptr(mask), 0) if r1 == 0 { err = errnoErr(e1) } return } func setCommState(handle syscall.Handle, dcb *dcb) (err error) { r1, _, e1 := syscall.Syscall(procSetCommState.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(dcb)), 0) if r1 == 0 { err = errnoErr(e1) } return } func setCommTimeouts(handle syscall.Handle, timeouts *commTimeouts) (err error) { r1, _, e1 := syscall.Syscall(procSetCommTimeouts.Addr(), 2, uintptr(handle), uintptr(unsafe.Pointer(timeouts)), 0) if r1 == 0 { err = errnoErr(e1) } return }