Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added port list functionality and increased range of baud rates available via serial_posix.go #37

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 0 additions & 22 deletions serial.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,28 +95,6 @@ func OpenPort(c *Config) (*Port, error) {
return openPort(c.Name, c.Baud, c.ReadTimeout)
}

// Converts the timeout values for Linux / POSIX systems
func posixTimeoutValues(readTimeout time.Duration) (vmin uint8, vtime uint8) {
const MAXUINT8 = 1<<8 - 1 // 255
// set blocking / non-blocking read
var minBytesToRead uint8 = 1
var readTimeoutInDeci int64
if readTimeout > 0 {
// EOF on zero read
minBytesToRead = 0
// convert timeout to deciseconds as expected by VTIME
readTimeoutInDeci = (readTimeout.Nanoseconds() / 1e6 / 100)
// capping the timeout
if readTimeoutInDeci < 1 {
// min possible timeout 1 Deciseconds (0.1s)
readTimeoutInDeci = 1
} else if readTimeoutInDeci > MAXUINT8 {
// max possible timeout is 255 deciseconds (25.5s)
readTimeoutInDeci = MAXUINT8
}
}
return minBytesToRead, uint8(readTimeoutInDeci)
}

// func SendBreak()

Expand Down
5 changes: 4 additions & 1 deletion serial_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import (
"os"
"syscall"
"time"
"unsafe"
"strings"
"unsafe"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please run gofmt

log "github.com/Sirupsen/logrus"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't include the extra dependancy.

"fmt"
)

func openPort(name string, baud int, readTimeout time.Duration) (p *Port, err error) {
Expand Down
104 changes: 84 additions & 20 deletions serial_posix.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"os"
"syscall"
"time"
//"unsafe"
)

func openPort(name string, baud int, readTimeout time.Duration) (p *Port, err error) {
Expand All @@ -35,27 +34,92 @@ func openPort(name string, baud int, readTimeout time.Duration) (p *Port, err er
f.Close()
return nil, err
}
var speed C.speed_t
switch baud {
case 115200:
speed = C.B115200
case 57600:
speed = C.B57600
case 38400:
speed = C.B38400
case 19200:
speed = C.B19200
case 9600:
speed = C.B9600
case 4800:
speed = C.B4800
case 2400:
speed = C.B2400
default:
f.Close()
return nil, fmt.Errorf("Unknown baud rate %v", baud)

var bauds = map[int]C.speed_t{
50: C.B50,
75: C.B75,
110: C.B110,
134: C.B134,
150: C.B150,
200: C.B200,
300: C.B300,
600: C.B600,
1200: C.B1200,
1800: C.B1800,
2400: C.B2400,
4800: C.B4800,
9600: C.B9600,
19200: C.B19200,
38400: C.B38400,
57600: C.B57600,
115200: C.B115200,
230400: C.B230400,
460800: C.B460800,
500000: C.B500000,
576000: C.B576000,
921600: C.B921600,
1000000: C.B1000000,
1152000: C.B1152000,
1500000: C.B1500000,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What platform testing have you done on these?

I think they are not portable. Likely available on linux, but not other posix systems.

Here's some documentation:
http://pubs.opengroup.org/onlinepubs/009695399/basedefs/termios.h.html

Can you point to any standards that say these extra baud rates are available on other platforms?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

230400 works on OSX -- only added the higher rates 2 days ago (from your serial_linux implementation), and don't have an OSX test machine at the minute. Your right - this area is pretty wooley. A note on cross-platform portability in the GLIBC manual lists the constants up to B430600 as being standard
(http://www.gnu.org/software/libc/manual/html_node/Line-Speed.html), but a bsd version of termios.h (which is probably a closer match to OSX than the linux version) only goes up to 230400
(http://fxr.watson.org/fxr/source/sys/termios.h?v=OPENBSD)

So could end the list at 230400 if you're happy with that, and review at a later date if I get access to an OSX machine to test higher baud rates on?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to keep the library cross-platform and recall having issues with other baud rates, so would prefer to be conservative.

It would be nice to have a way to test for the available rates.

One option would be to fully specialize the platforms so that higher rates are available on linux for example.

Another option might be to use an #ifdef within C or cgo code to check if those constants are available. That might get messy though.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could just specialise the baud-rate map, i.e. in something like termios_baudrates_linux.go, termios_baudrates_darwin.go, etc. and maybe a catchall conservative version in termios_baudrates_other.go? Some sort of #ifdef-type sniffing would probably be ugly but a better solution in some respects. No idea how to do that in go to be honest, tourist that I am - I'll look into it. Regardless, I'll remove this baud-rate stuff from this PR as you requested elsewhere and make it the subject of another.

2000000: C.B2000000,
2500000: C.B2500000,
3000000: C.B3000000,
3500000: C.B3500000,
4000000: C.B4000000,
}

// var speed C.speed_t


// switch baud {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove commented out code.

// case 230400:
// speed = C.B230400
// case 115200:
// speed = C.B115200
// case 57600:
// speed = C.B57600
// case 38400:
// speed = C.B38400
// case 19200:
// speed = C.B19200
// case 9600:
// speed = C.B9600
// case 4800:
// speed = C.B4800
// case 2400:
// speed = C.B2400
// case 1800:
// speed = C.B1800
// case 1200:
// speed = C.B1200
// case 600:
// speed = C.B600
// case 300:
// speed = C.B300
// case 200:
// speed = C.B200
// case 150:
// speed = C.B150
// case 134:
// speed = C.B134
// case 110:
// speed = C.B110
// case 75:
// speed = C.B75
// case 50:
// speed = C.B50
// default:
// f.Close()
// return nil, fmt.Errorf("Unknown baud rate %v", baud)
// }

speed := bauds[baud]

if speed == 0 {
f.Close()
return nil, fmt.Errorf("Unknown baud rate %v", baud)
}

_, err = C.cfsetispeed(&st, speed)
if err != nil {
f.Close()
Expand Down
105 changes: 103 additions & 2 deletions serial_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package serial

import (
"errors"
"fmt"
"os"
"sync"
Expand Down Expand Up @@ -130,7 +131,83 @@ func (p *Port) Read(buf []byte) (int, error) {
// Discards data written to the port but not transmitted,
// or data received but not read
func (p *Port) Flush() error {
return purgeComm(p.fd)
err := purgeComm(p.fd)
clearCommError(p.fd)
return err
}

/*
This function was taken with minor modifications from the go.bug.st/serial package (https://github.com/bugst/go-serial), and is subject to the conditions of its license (reproduced below):

Copyright (c) 2014, Cristian Maglie.
All rights reserved.

Redistribution and use in source and binary forms, with or without
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is almost the standard Go License, which tarm/serial is already under. The Go license is a 3 clause BSD license like this one Copyright the Go Authors. I would like to keep the existing license text and not add this.

Unfortunately @cmaglie is not in the standard Go AUTHORS file right now:
https://golang.org/AUTHORS

modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.

3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
func GetPortsList() ([]string, error) {
subKey, err := syscall.UTF16PtrFromString("HARDWARE\\DEVICEMAP\\SERIALCOMM\\")
if err != nil {
return nil, errors.New("Error enumerating ports")
}

var h syscall.Handle
if syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE, subKey, 0, syscall.KEY_READ, &h) != nil {
return nil, errors.New("Error enumerating ports")
}
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, errors.New("Error enumerating ports")
}

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, errors.New("Error enumerating ports")
}
list[i] = syscall.UTF16ToString(data[:])
}
return list, nil
}

func RegEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be exported.

r0, _, _ := syscall.Syscall9(nRegEnumValueW, 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 {
return syscall.Errno(r0)
}
return nil
}

var (
Expand All @@ -142,7 +219,9 @@ var (
nCreateEvent,
nResetEvent,
nPurgeComm,
nFlushFileBuffers uintptr
nFlushFileBuffers,
nClearCommError,
nRegEnumValueW uintptr
)

func init() {
Expand All @@ -151,6 +230,11 @@ func init() {
panic("LoadLibrary " + err.Error())
}
defer syscall.FreeLibrary(k32)
api32, err := syscall.LoadLibrary("advapi32.dll")
if err != nil {
panic("LoadLibrary " + err.Error())
}
defer syscall.FreeLibrary(api32)

nSetCommState = getProcAddr(k32, "SetCommState")
nSetCommTimeouts = getProcAddr(k32, "SetCommTimeouts")
Expand All @@ -161,6 +245,12 @@ func init() {
nResetEvent = getProcAddr(k32, "ResetEvent")
nPurgeComm = getProcAddr(k32, "PurgeComm")
nFlushFileBuffers = getProcAddr(k32, "FlushFileBuffers")
nClearCommError = getProcAddr(k32, "ClearCommError")
nRegEnumValueW = getProcAddr(api32, "RegEnumValueW")
}

func clearCommError(h syscall.Handle) error {
return processSyscall(nClearCommError, 1, uintptr(h), 0, 0)
}

func getProcAddr(lib syscall.Handle, name string) uintptr {
Expand All @@ -171,13 +261,24 @@ func getProcAddr(lib syscall.Handle, name string) uintptr {
return addr
}

func processSyscall(systemMethod, nargs, a1, a2, a3 uintptr) error {
result, _, err := syscall.Syscall(systemMethod, nargs, a1, a2, a3)
if result == 0 {
return err
}
return nil
}

func setCommState(h syscall.Handle, baud int) error {
var params structDCB
params.DCBlength = uint32(unsafe.Sizeof(params))

params.flags[0] = 0x01 // fBinary
params.flags[0] |= 0x10 // Assert DSR

//ADDITION: To help with this problem: http://zachsaw.blogspot.ie/2010/07/net-serialport-woes.html
params.flags[1] &= ^byte(0x40)

params.BaudRate = uint32(baud)
params.ByteSize = 8

Expand Down
Loading