-
Notifications
You must be signed in to change notification settings - Fork 452
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
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,10 @@ import ( | |
"os" | ||
"syscall" | ||
"time" | ||
"unsafe" | ||
"strings" | ||
"unsafe" | ||
log "github.com/Sirupsen/logrus" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,6 @@ import ( | |
"os" | ||
"syscall" | ||
"time" | ||
//"unsafe" | ||
) | ||
|
||
func openPort(name string, baud int, readTimeout time.Duration) (p *Port, err error) { | ||
|
@@ -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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: Can you point to any standards that say these extra baud rates are available on other platforms? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
package serial | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"os" | ||
"sync" | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: |
||
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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 ( | ||
|
@@ -142,7 +219,9 @@ var ( | |
nCreateEvent, | ||
nResetEvent, | ||
nPurgeComm, | ||
nFlushFileBuffers uintptr | ||
nFlushFileBuffers, | ||
nClearCommError, | ||
nRegEnumValueW uintptr | ||
) | ||
|
||
func init() { | ||
|
@@ -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") | ||
|
@@ -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 { | ||
|
@@ -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 | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please run gofmt