Files
invertergui/vendor/github.com/mikepb/go-serial/serial.go
Hendrik van Wyk 33ad6b8d1d Vendor go-serial.
2017-09-16 15:25:31 +02:00

1175 lines
26 KiB
Go

/*
Package serial provides a binding to libserialport for serial port
functionality. Serial ports are commonly used with embedded systems,
such as the Arduino platform.
Example Usage
package main
import (
"github.com/mikepb/go-serial"
"log"
)
func main() {
options := serial.RawOptions
options.BitRate = 115200
p, err := options.Open("/dev/tty")
if err != nil {
log.Panic(err)
}
defer p.Close()
buf := make([]byte, 1)
if c, err := p.Read(buf); err != nil {
log.Panic(err)
} else {
log.Println(buf)
}
}
*/
package serial
/*
#cgo CFLAGS: -g -O2 -Wall -Wextra -DSP_PRIV= -DSP_API=
#cgo darwin LDFLAGS: -framework IOKit -framework CoreFoundation
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include "libserialport.h"
void debug_handler(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
void setdebug(int enable) {
sp_set_debug_handler(enable ? debug_handler : NULL);
}
*/
import "C"
import (
"bytes"
"log"
"net"
"reflect"
"runtime"
"time"
"unsafe"
)
// Debug flag
const Debug = false
// Port access modes
const (
MODE_READ = C.SP_MODE_READ // Open port for read access
MODE_WRITE = C.SP_MODE_WRITE // Open port for write access
MODE_READ_WRITE = C.SP_MODE_READ_WRITE // Open port for read and write access.
)
// Port events.
const (
EVENT_RX_READY = C.SP_EVENT_RX_READY // Data received and ready to read.
EVENT_TX_READY = C.SP_EVENT_TX_READY // Ready to transmit new data.
EVENT_ERROR = C.SP_EVENT_ERROR // Error occured.
)
// Parity settings.
const (
PARITY_INVALID = iota // Special value to indicate setting should be left alone.
PARITY_NONE // No parity.
PARITY_ODD // Odd parity.
PARITY_EVEN // Even parity.
PARITY_MARK // Mark parity.
PARITY_SPACE // Space parity.
)
// RTS pin behaviour.
const (
RTS_INVALID = iota // Special value to indicate setting should be left alone.
RTS_OFF // RTS off.
RTS_ON // RTS on.
RTS_FLOW_CONTROL // RTS used for flow control.
)
// CTS pin behaviour.
const (
CTS_INVALID = iota // Special value to indicate setting should be left alone.
CTS_IGNORE // CTS ignored.
CTS_FLOW_CONTROL // CTS used for flow control.
)
// DTR pin behaviour.
const (
DTR_INVALID = iota // Special value to indicate setting should be left alone.
DTR_OFF // DTR off.
DTR_ON // DTR on.
DTR_FLOW_CONTROL // DTR used for flow control.
)
// DSR pin behaviour.
const (
DSR_INVALID = iota // Special value to indicate setting should be left alone.
DSR_IGNORE // DSR ignored.
DSR_FLOW_CONTROL // DSR used for flow control.
)
// XON/XOFF flow control behaviour.
const (
XONXOFF_INVALID = iota // Special value to indicate setting should be left alone.
XONXOFF_DISABLED // XON/XOFF disabled.
XONXOFF_IN // XON/XOFF enabled for input only.
XONXOFF_OUT // XON/XOFF enabled for output only.
XONXOFF_INOUT // XON/XOFF enabled for input and output.
)
// Standard flow control combinations.
const (
_ = iota
FLOWCONTROL_NONE // No flow control.
FLOWCONTROL_XONXOFF // Software flow control using XON/XOFF characters.
FLOWCONTROL_RTSCTS // Hardware flow control using RTS/CTS signals.
FLOWCONTROL_DTRDSR // Hardware flow control using DTR/DSR signals.
)
// Input signals
const (
SIG_CTS = C.SP_SIG_CTS // Clear to send
SIG_DSR = C.SP_SIG_DSR // Data set ready
SIG_DCD = C.SP_SIG_DCD // Data carrier detect
SIG_RI = C.SP_SIG_RI // Ring indicator
)
// Transport types.
const (
TRANSPORT_NATIVE = C.SP_TRANSPORT_NATIVE // Native platform serial port.
TRANSPORT_USB = C.SP_TRANSPORT_USB // USB serial port adapter.
TRANSPORT_BLUETOOTH = C.SP_TRANSPORT_BLUETOOTH // Bluetooh serial port adapter.
)
// Serial port info.
type Info struct {
p *C.struct_sp_port
opened bool
}
// Serial port options.
type Options struct {
Mode int // read, write; default is read
BitRate int // number of bits per second (baudrate)
DataBits int // number of data bits (5, 6, 7, 8)
StopBits int // number of stop bits (1, 2)
Parity int // none, odd, even, mark, space
FlowControl int // none, xonxoff, rtscts, dtrdsr
RTS int
CTS int
DTR int
DSR int
}
// Serial port.
type Port struct {
Info
c *C.struct_sp_port_config
readDeadline time.Time
writeDeadline time.Time
}
// Implementation of net.Addr
type Addr struct {
name string
}
// Implementation of net.Error
type Error struct {
msg string
timeout bool
temporary bool
}
var RawOptions = Options{
DataBits: 8,
Parity: PARITY_NONE,
StopBits: 1,
FlowControl: FLOWCONTROL_NONE,
}
var ErrInvalidArguments = &Error{msg: "Invalid arguments were passed to the function"}
var ErrSystem = &Error{msg: "A system error occured while executing the operation"}
var ErrMemoryAllocation = &Error{msg: "A memory allocation failed while executing the operation"}
var ErrUnsupportedOperation = &Error{msg: "The requested operation is not supported by this system or device"}
var ErrTimeout = &Error{msg: "Operation timed out", timeout: true}
// Map error codes to errors.
func errmsg(err C.enum_sp_return) error {
switch err {
case C.SP_ERR_ARG:
return ErrInvalidArguments
case C.SP_ERR_FAIL:
return ErrSystem
case C.SP_ERR_MEM:
return ErrMemoryAllocation
case C.SP_ERR_SUPP:
return ErrUnsupportedOperation
}
return nil
}
// Wrap a sp_port struct in a go Port struct and set finalizer for
// garbage collection.
func newInfo(p *C.struct_sp_port) (*Info, error) {
info := &Info{p: p}
runtime.SetFinalizer(info, (*Info).free)
return info, nil
}
// Finalizer callback for garbage collection.
func (i *Info) free() {
if i.p != nil {
if i.opened {
C.sp_close(i.p)
}
C.sp_free_port(i.p)
}
i.opened = false
i.p = nil
}
// Wrap a sp_port struct in a go Port struct and set finalizer for
// garbage collection.
func newPort(info *Info) (*Port, error) {
port := &Port{}
// copy info
if info != nil {
if err := errmsg(C.sp_copy_port(info.p, &port.p)); err != nil {
return nil, err
}
}
// set finalizers
runtime.SetFinalizer(port, (*Port).free)
return port, nil
}
// Finalizer callback for garbage collection.
func (p *Port) free() {
p.Info.free()
if p.c != nil {
C.sp_free_config(p.c)
}
p.c = nil
}
// calculate milliseconds until deadline (rounded up)
func deadline2millis(deadline time.Time) int64 {
delta := deadline.Sub(time.Now())
duration := time.Duration(delta.Nanoseconds())
duration += duration + time.Millisecond - time.Nanosecond
duration /= time.Millisecond
millis := int64(duration)
if Debug {
log.Printf("timeout: %d ns %d ms", delta, millis)
}
return millis
}
// Print libserialport debug messages to stderr.
func SetDebug(enable bool) {
if enable {
C.setdebug(1)
} else {
C.setdebug(0)
}
}
// Get a port by name.
func PortByName(name string) (*Info, error) {
if p, err := portByName(name); err != nil {
return nil, err
} else {
return newInfo(p)
}
}
func portByName(name string) (*C.struct_sp_port, error) {
var p *C.struct_sp_port
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
if err := errmsg(C.sp_get_port_by_name(cname, &p)); err != nil {
return nil, err
}
return p, nil
}
// List the serial ports available on the system.
func ListPorts() ([]*Info, error) {
var p **C.struct_sp_port
if err := C.sp_list_ports(&p); err != C.SP_OK {
return nil, errmsg(err)
}
defer C.sp_free_port_list(p)
// Convert the C array into a Go slice
// See: https://code.google.com/p/go-wiki/wiki/cgo
pp := (*[1 << 15]*C.struct_sp_port)(unsafe.Pointer(p))
// count number of ports
c := 0
for ; uintptr(unsafe.Pointer(pp[c])) != 0; c++ {
}
// populate
ports := make([]*Info, c)
for j := 0; j < c; j++ {
var pc *C.struct_sp_port
if err := errmsg(C.sp_copy_port(pp[j], &pc)); err != nil {
return nil, err
}
if sp, err := newInfo(pc); err != nil {
return nil, err
} else {
ports[j] = sp
}
}
return ports, nil
}
// Get the name of a port.
func (i *Info) Name() string {
return C.GoString(C.sp_get_port_name(i.p))
}
// Get a description for a port, to present to end user.
func (i *Info) Description() string {
return C.GoString(C.sp_get_port_description(i.p))
}
// Get the transport type used by a port.
func (i *Info) Transport() int {
t := C.sp_get_port_transport(i.p)
return int(t)
}
// Get the USB bus number and address on bus of a USB serial adapter port.
func (i *Info) USBBusAddress() (int, int, error) {
var bus, address C.int
if err := errmsg(C.sp_get_port_usb_bus_address(i.p, &bus, &address)); err != nil {
return 0, 0, err
}
return int(bus), int(address), nil
}
// Get the USB Vendor ID and Product ID of a USB serial adapter port.
func (i *Info) USBVIDPID() (int, int, error) {
var vid, pid C.int
if err := errmsg(C.sp_get_port_usb_vid_pid(i.p, &vid, &pid)); err != nil {
return 0, 0, err
}
return int(vid), int(pid), nil
}
// Get the USB manufacturer string of a USB serial adapter port.
func (i *Info) USBManufacturer() string {
cdesc := C.sp_get_port_usb_manufacturer(i.p)
return C.GoString(cdesc)
}
// Get the USB product string of a USB serial adapter port.
func (i *Info) USBProduct() string {
cdesc := C.sp_get_port_usb_product(i.p)
return C.GoString(cdesc)
}
// Get the USB serial number string of a USB serial adapter port.
func (i *Info) USBSerialNumber() string {
cdesc := C.sp_get_port_usb_serial(i.p)
return C.GoString(cdesc)
}
// Get the MAC address of a Bluetooth serial adapter port.
func (i *Info) BluetoothAddress() string {
cdesc := C.sp_get_port_bluetooth_address(i.p)
return C.GoString(cdesc)
}
// Open the port for reading and default options.
func (i *Info) Open() (*Port, error) {
return i.OpenPort(&Options{Mode: MODE_READ})
}
// Open the port with the specified options.
func (i *Info) OpenPort(options *Options) (*Port, error) {
// create port
port, err := newPort(i)
if err != nil {
return nil, err
}
// open port
mode := MODE_READ
if options.Mode != 0 {
mode = options.Mode
}
if err = port.open(mode); err != nil {
return nil, err
}
// apply options
if err = port.Apply(options); err != nil {
port.Close()
return nil, err
}
return port, nil
}
func (i *Info) createPortAndInvalidateInfo() (*Port, error) {
port, err := newPort(nil)
if err != nil {
return nil, err
}
port.p = i.p
port.opened = i.opened
i.p = nil
i.opened = false
return port, nil
}
// Open a port at the given name using the options object.
func (o *Options) Open(name string) (port *Port, err error) {
if info, err := PortByName(name); err != nil {
return nil, err
} else {
return info.OpenPort(o)
}
}
// Open a port at the given info using the options object.
func (o *Options) OpenAt(info *Info) (port *Port, err error) {
return info.OpenPort(o)
}
// Open a port for reading.
func Open(name string) (port *Port, err error) {
// get the port by name
if info, err := PortByName(name); err != nil {
return nil, err
} else if p, err := info.createPortAndInvalidateInfo(); p != nil {
return nil, err
} else {
port = p
}
// open port with read mode
if err = port.open(MODE_READ); err != nil {
return nil, err
}
return
}
func (p *Port) open(mode int) error {
if p.opened {
panic("already opened")
}
err := errmsg(C.sp_open(p.p, C.enum_sp_mode(mode)))
p.opened = err == nil
return p.getConf()
}
// Close the serial port.
func (p *Port) Close() error {
if !p.opened {
panic("already closed")
}
err := errmsg(C.sp_close(p.p))
p.opened = false
return err
}
func (p *Port) getConf() error {
if p.c == nil {
if err := errmsg(C.sp_new_config(&p.c)); err != nil {
return err
}
}
return errmsg(C.sp_get_config(p.p, p.c))
}
// Apply port options.
func (p *Port) Apply(o *Options) (err error) {
// get port config
var conf *C.struct_sp_port_config
if err = errmsg(C.sp_new_config(&conf)); err != nil {
return
}
defer C.sp_free_config(conf)
// set bit rate
if o.BitRate != 0 {
err = errmsg(C.sp_set_config_baudrate(conf, C.int(o.BitRate)))
if err != nil {
return
}
}
// set data bits
if o.DataBits != 0 {
err = errmsg(C.sp_set_config_bits(conf, C.int(o.DataBits)))
if err != nil {
return
}
}
// set stop bits
if o.StopBits != 0 {
err = errmsg(C.sp_set_config_stopbits(conf, C.int(o.StopBits)))
if err != nil {
return
}
}
// set parity
if o.Parity != 0 {
cparity := parity2c(o.Parity)
if err = errmsg(C.sp_set_config_parity(conf, cparity)); err != nil {
return
}
}
// set flow control
if o.FlowControl != 0 {
cfc, err := flow2c(o.FlowControl)
if err != nil {
return err
}
if err = errmsg(C.sp_set_config_flowcontrol(conf, cfc)); err != nil {
return err
}
}
// set RTS
if o.RTS != 0 {
crts := rts2c(o.RTS)
if err = errmsg(C.sp_set_config_rts(conf, crts)); err != nil {
return
}
}
// set CTS
if o.CTS != 0 {
ccts := cts2c(o.CTS)
if err = errmsg(C.sp_set_config_cts(conf, ccts)); err != nil {
return
}
}
// set DTR
if o.DTR != 0 {
cdtr := dtr2c(o.DTR)
if err = errmsg(C.sp_set_config_dtr(conf, cdtr)); err != nil {
return
}
}
// set DSR
if o.DSR != 0 {
cdsr := dsr2c(o.DSR)
if err = errmsg(C.sp_set_config_dsr(conf, cdsr)); err != nil {
return
}
}
// apply config
if err = errmsg(C.sp_set_config(p.p, conf)); err != nil {
return
}
// update local config
return p.getConf()
}
// Get the baud rate from a port configuration. The port must be
// opened for this operation.
func (p *Port) BitRate() (int, error) {
var bitrate C.int
if err := errmsg(C.sp_get_config_baudrate(p.c, &bitrate)); err != nil {
return 0, err
}
return int(bitrate), nil
}
// Set the baud rate for the serial port. The port must be opened for
// this operation. Call p.ApplyConfig() to apply the change.
func (p *Port) SetBitRate(bitrate int) error {
if err := errmsg(C.sp_set_baudrate(p.p, C.int(bitrate))); err != nil {
return err
}
return p.getConf()
}
// Get the data bits from a port configuration. The port must be
// opened for this operation.
func (p *Port) DataBits() (int, error) {
var bits C.int
if err := errmsg(C.sp_get_config_bits(p.c, &bits)); err != nil {
return 0, err
}
return int(bits), nil
}
// Set the number of data bits for the serial port. The port must be
// opened for this operation. Call p.ApplyConfig() to apply the
// change.
func (p *Port) SetDataBits(bits int) error {
if err := errmsg(C.sp_set_config_bits(p.c, C.int(bits))); err != nil {
return err
}
return p.getConf()
}
// Get the stop bits from a port configuration. The port must be
// opened for this operation.
func (p *Port) StopBits() (int, error) {
var stopbits C.int
if err := errmsg(C.sp_get_config_stopbits(p.c, &stopbits)); err != nil {
return 0, err
}
return int(stopbits), nil
}
// Set the stop bits for the serial port. The port must be opened for
// this operation. Call p.ApplyConfig() to apply the change.
func (p *Port) SetStopBits(stopbits int) error {
if err := errmsg(C.sp_set_config_stopbits(p.c, C.int(stopbits))); err != nil {
return err
}
return p.getConf()
}
// Get the parity setting from a port configuration. The port must be
// opened for this operation.
func (p *Port) Parity() (int, error) {
cparity := C.enum_sp_parity(C.SP_PARITY_INVALID)
if err := errmsg(C.sp_get_config_parity(p.c, &cparity)); err != nil {
return 0, err
}
return c2parity(cparity), nil
}
// Set the parity setting for the serial port. The port must be opened
// for this operation. Call p.ApplyConfig() to apply the change.
func (p *Port) SetParity(parity int) error {
cparity := parity2c(parity)
if err := errmsg(C.sp_set_config_parity(p.c, cparity)); err != nil {
return err
}
return p.getConf()
}
func c2parity(cparity C.enum_sp_parity) int {
switch cparity {
case C.SP_PARITY_NONE:
return PARITY_NONE
case C.SP_PARITY_ODD:
return PARITY_ODD
case C.SP_PARITY_EVEN:
return PARITY_EVEN
case C.SP_PARITY_MARK:
return PARITY_MARK
case C.SP_PARITY_SPACE:
return PARITY_SPACE
default:
return PARITY_INVALID
}
}
func parity2c(parity int) C.enum_sp_parity {
switch parity {
case PARITY_NONE:
return C.SP_PARITY_NONE
case PARITY_ODD:
return C.SP_PARITY_ODD
case PARITY_EVEN:
return C.SP_PARITY_EVEN
case PARITY_MARK:
return C.SP_PARITY_MARK
case PARITY_SPACE:
return C.SP_PARITY_SPACE
default:
return C.SP_PARITY_INVALID
}
}
// Get the RTS pin behaviour from a port configuration. The port must
// be opened for this operation.
func (p *Port) RTS() (int, error) {
rts := C.enum_sp_rts(C.SP_RTS_INVALID)
if err := errmsg(C.sp_get_config_rts(p.c, &rts)); err != nil {
return 0, err
}
return c2rts(rts), nil
}
// Set the RTS pin behaviour in a port configuration. The port must be
// opened for this operation. Call p.ApplyConfig() to apply the
// change.
func (p *Port) SetRTS(rts int) error {
crts := rts2c(rts)
if err := errmsg(C.sp_set_config_rts(p.c, crts)); err != nil {
return err
}
return p.getConf()
}
func c2rts(rts C.enum_sp_rts) int {
switch rts {
case C.SP_RTS_OFF:
return RTS_OFF
case C.SP_RTS_ON:
return RTS_ON
case C.SP_RTS_FLOW_CONTROL:
return RTS_FLOW_CONTROL
default:
return RTS_INVALID
}
}
func rts2c(rts int) C.enum_sp_rts {
switch rts {
case RTS_OFF:
return C.SP_RTS_OFF
case RTS_ON:
return C.SP_RTS_ON
case RTS_FLOW_CONTROL:
return C.SP_RTS_FLOW_CONTROL
default:
return C.SP_RTS_INVALID
}
}
// Get the CTS pin behaviour from a port configuration. The port must
// be opened for this operation.
func (p *Port) CTS() (int, error) {
cts := C.enum_sp_cts(C.SP_CTS_INVALID)
if err := errmsg(C.sp_get_config_cts(p.c, &cts)); err != nil {
return 0, err
}
return c2cts(cts), nil
}
// Set the CTS pin behaviour in a port configuration. The port must be
// opened for this operation. Call p.ApplyConfig() to apply the
// change.
func (p *Port) SetCTS(cts int) error {
ccts := cts2c(cts)
if err := errmsg(C.sp_set_config_cts(p.c, ccts)); err != nil {
return err
}
return p.getConf()
}
func c2cts(cts C.enum_sp_cts) int {
switch cts {
case C.SP_CTS_IGNORE:
return CTS_IGNORE
case C.SP_CTS_FLOW_CONTROL:
return CTS_FLOW_CONTROL
default:
return CTS_INVALID
}
}
func cts2c(cts int) C.enum_sp_cts {
switch cts {
case CTS_IGNORE:
return C.SP_CTS_IGNORE
case CTS_FLOW_CONTROL:
return C.SP_CTS_FLOW_CONTROL
default:
return C.SP_CTS_INVALID
}
}
// Get the DTR pin behaviour from a port configuration. The port must
// be opened for this operation.
func (p *Port) DTR() (int, error) {
dtr := C.enum_sp_dtr(C.SP_DTR_INVALID)
if err := errmsg(C.sp_get_config_dtr(p.c, &dtr)); err != nil {
return 0, err
}
return c2dtr(dtr), nil
}
// Set the DTR pin behaviour in a port configuration. The port must be
// opened for this operation. Call p.ApplyConfig() to apply the
// change.
func (p *Port) SetDTR(dtr int) error {
cdtr := dtr2c(dtr)
if err := errmsg(C.sp_set_config_dtr(p.c, cdtr)); err != nil {
return err
}
return p.getConf()
}
func c2dtr(dtr C.enum_sp_dtr) int {
switch dtr {
case C.SP_DTR_OFF:
return DTR_OFF
case C.SP_DTR_ON:
return DTR_ON
case C.SP_DTR_FLOW_CONTROL:
return DTR_FLOW_CONTROL
default:
return DTR_INVALID
}
}
func dtr2c(dtr int) C.enum_sp_dtr {
switch dtr {
case DTR_OFF:
return C.SP_DTR_OFF
case DTR_ON:
return C.SP_DTR_ON
case DTR_FLOW_CONTROL:
return C.SP_DTR_FLOW_CONTROL
default:
return C.SP_DTR_INVALID
}
}
// Get the DSR pin behaviour from a port configuration. The port must
// be opened for this operation.
func (p *Port) DSR() (int, error) {
dsr := C.enum_sp_dsr(C.SP_DSR_INVALID)
if err := errmsg(C.sp_get_config_dsr(p.c, &dsr)); err != nil {
return 0, err
}
return c2dsr(dsr), nil
}
// Set the DSR pin behaviour in a port configuration. The port must be
// opened for this operation. Call p.ApplyConfig() to apply the
// change.
func (p *Port) SetDSR(dsr int) error {
cdsr := dsr2c(dsr)
if err := errmsg(C.sp_set_config_dsr(p.c, cdsr)); err != nil {
return err
}
return p.getConf()
}
func c2dsr(dsr C.enum_sp_dsr) int {
switch dsr {
case C.SP_DSR_IGNORE:
return DSR_IGNORE
case C.SP_DSR_FLOW_CONTROL:
return DSR_FLOW_CONTROL
default:
return DSR_INVALID
}
}
func dsr2c(dsr int) C.enum_sp_dsr {
switch dsr {
case DSR_IGNORE:
return C.SP_DSR_IGNORE
case DSR_FLOW_CONTROL:
return C.SP_DSR_FLOW_CONTROL
default:
return C.SP_DSR_INVALID
}
}
// Get the XON/XOFF configuration from a port configuration. The port
// must be opened for this operation.
func (p *Port) XonXoff() (int, error) {
xon := C.enum_sp_xonxoff(C.SP_XONXOFF_INVALID)
if err := errmsg(C.sp_get_config_xon_xoff(p.c, &xon)); err != nil {
return 0, err
}
return c2xon(xon), nil
}
// Set the XON/XOFF configuration in a port configuration. The port
// must be opened for this operation. Call p.ApplyConfig() to apply
// the change.
func (p *Port) SetXonXoff(xon int) error {
cxon := xon2c(xon)
if err := errmsg(C.sp_set_config_xon_xoff(p.c, cxon)); err != nil {
return err
}
return p.getConf()
}
func c2xon(xon C.enum_sp_xonxoff) int {
switch xon {
case C.SP_XONXOFF_DISABLED:
return XONXOFF_DISABLED
case C.SP_XONXOFF_IN:
return XONXOFF_IN
case C.SP_XONXOFF_OUT:
return XONXOFF_OUT
default:
return XONXOFF_INVALID
}
}
func xon2c(xon int) C.enum_sp_xonxoff {
switch xon {
case XONXOFF_DISABLED:
return C.SP_XONXOFF_DISABLED
case XONXOFF_IN:
return C.SP_XONXOFF_IN
case XONXOFF_OUT:
return C.SP_XONXOFF_OUT
default:
return C.SP_XONXOFF_INVALID
}
}
// Set the flow control type in a port configuration. The port must be
// opened for this operation. Call p.ApplyConfig() to apply the
// change.
func (p *Port) SetFlowControl(fc int) error {
cfc, err := flow2c(fc)
if err != nil {
return err
}
if err := errmsg(C.sp_set_config_flowcontrol(p.c, cfc)); err != nil {
return err
}
return p.getConf()
}
func flow2c(fc int) (cfc C.enum_sp_flowcontrol, err error) {
switch fc {
case FLOWCONTROL_NONE:
cfc = C.SP_FLOWCONTROL_NONE
case FLOWCONTROL_XONXOFF:
cfc = C.SP_FLOWCONTROL_XONXOFF
case FLOWCONTROL_RTSCTS:
cfc = C.SP_FLOWCONTROL_RTSCTS
case FLOWCONTROL_DTRDSR:
cfc = C.SP_FLOWCONTROL_DTRDSR
default:
err = ErrInvalidArguments
}
return
}
// Implementation of io.Reader interface.
func (p *Port) Read(b []byte) (int, error) {
var c int32
var start time.Time
if Debug {
start = time.Now()
}
buf, size := unsafe.Pointer(&b[0]), C.size_t(len(b))
if p.readDeadline.IsZero() {
// no deadline
c = C.sp_blocking_read(p.p, buf, size, 0)
} else if millis := deadline2millis(p.readDeadline); millis <= 0 {
// call nonblocking read
c = C.sp_nonblocking_read(p.p, buf, size)
} else {
// call blocking read
c = C.sp_blocking_read(p.p, buf, size, C.uint(millis))
}
if Debug {
log.Printf("read time: %d ns", time.Since(start).Nanoseconds())
}
n := int(c)
// check for error
if n < 0 {
return 0, errmsg(c)
} else if n != len(b) {
return n, ErrTimeout
}
// update slice length
reflect.ValueOf(&b).Elem().SetLen(int(c))
return n, nil
}
// Implementation of io.Writer interface.
func (p *Port) Write(b []byte) (int, error) {
var c int32
var start time.Time
if Debug {
start = time.Now()
}
buf, size := unsafe.Pointer(&b[0]), C.size_t(len(b))
if p.writeDeadline.IsZero() {
// no deadline
c = C.sp_blocking_write(p.p, buf, size, 0)
} else if millis := deadline2millis(p.writeDeadline); millis <= 0 {
// call nonblocking write
c = C.sp_nonblocking_write(p.p, buf, size)
} else {
// call blocking write
c = C.sp_blocking_write(p.p, buf, size, C.uint(millis))
}
if Debug {
log.Printf("write time: %d ns", time.Since(start).Nanoseconds())
}
n := int(c)
// check for error
if n < 0 {
return 0, errmsg(c)
} else if n != len(b) {
return n, ErrTimeout
}
return n, nil
}
// WriteString is like Write, but writes the contents of string s
// rather than a slice of bytes.
func (p *Port) WriteString(s string) (int, error) {
return p.Write(bytes.NewBufferString(s).Bytes())
}
// Implementation of net.Conn.LocalAddr
func (p *Port) LocalAddr() net.Addr {
return &Addr{name: p.Name()}
}
// Implementation of net.Conn.RemoteAddr
func (p *Port) RemoteAddr() net.Addr {
return &Addr{name: p.Name()}
}
// Implementation of net.Conn.SetDeadline
func (p *Port) SetDeadline(t time.Time) error {
p.readDeadline = t
p.writeDeadline = t
return nil
}
// Implementation of net.Conn.SetReadDeadline
func (p *Port) SetReadDeadline(t time.Time) error {
p.readDeadline = t
return nil
}
// Implementation of net.Conn.SetWriteDeadline
func (p *Port) SetWriteDeadline(t time.Time) error {
p.writeDeadline = t
return nil
}
// Gets the number of bytes waiting in the input buffer.
func (p *Port) InputWaiting() (int, error) {
c := C.sp_input_waiting(p.p)
if c < 0 {
return 0, errmsg(c)
}
return int(c), nil
}
// Gets the number of bytes waiting in the output buffer.
func (p *Port) OutputWaiting() (int, error) {
c := C.sp_output_waiting(p.p)
if c < 0 {
return 0, errmsg(c)
}
return int(c), nil
}
// Wait for buffered data to be transmitted.
func (p *Port) Sync() error {
return errmsg(C.sp_drain(p.p))
}
// Discard buffered data.
func (p *Port) Reset() error {
return errmsg(C.sp_flush(p.p, C.SP_BUF_BOTH))
}
// Discard buffered input data.
func (p *Port) ResetInput() error {
return errmsg(C.sp_flush(p.p, C.SP_BUF_INPUT))
}
// Discard buffered output data.
func (p *Port) ResetOutput() error {
return errmsg(C.sp_flush(p.p, C.SP_BUF_OUTPUT))
}
// Implementation of net.Addr.Network()
func (a *Addr) Network() string {
return "serial"
}
// Implementation of net.Addr.String()
func (a *Addr) String() string {
return a.name
}
// Implementation of error.Error()
func (e *Error) Error() string {
return e.msg
}
// Implementation of net.Error.Timeout()
func (e *Error) Timeout() bool {
return e.timeout
}
// Implementation of net.Error.Temporary()
func (e *Error) Temporary() bool {
return e.temporary
}