// // rhctl // // Copyright (C) 2009-2016 Christian Pointner // // This file is part of rhctl. // // rhctl is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // any later version. // // rhctl is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with rhctl. If not, see . // package goserial import ( "bufio" "errors" "log" "strconv" "syscall" "github.com/schleibinger/sio" ) type Baudrate struct { value uint32 code uint32 } func NewBaudrate(value uint32) (b *Baudrate, err error) { b = &Baudrate{} err = b.FromUint(value) return } func (b Baudrate) String() string { return strconv.FormatUint(uint64(b.value), 10) } func (b Baudrate) MarshalText() (data []byte, err error) { data = []byte(b.String()) return } func (b Baudrate) MarshalJSON() (data []byte, err error) { data = []byte(b.String()) return } func (b Baudrate) MarshalTOML() (data []byte, err error) { data = []byte(b.String()) return } func (b Baudrate) MarshalYAML() (data []byte, err error) { data = []byte(b.String()) return } func (b *Baudrate) FromUint(value uint32) error { b.value = value switch b.value { case 50: b.code = syscall.B50 case 75: b.code = syscall.B75 case 110: b.code = syscall.B110 case 134: b.code = syscall.B134 case 150: b.code = syscall.B150 case 200: b.code = syscall.B200 case 300: b.code = syscall.B300 case 600: b.code = syscall.B600 case 1200: b.code = syscall.B1200 case 1800: b.code = syscall.B1800 case 2400: b.code = syscall.B2400 case 4800: b.code = syscall.B4800 case 9600: b.code = syscall.B9600 case 19200: b.code = syscall.B19200 case 38400: b.code = syscall.B38400 case 57600: b.code = syscall.B57600 case 115200: b.code = syscall.B115200 case 230400: b.code = syscall.B230400 case 460800: b.code = syscall.B460800 case 500000: b.code = syscall.B500000 case 576000: b.code = syscall.B576000 case 921600: b.code = syscall.B921600 case 1000000: b.code = syscall.B1000000 case 1152000: b.code = syscall.B1152000 case 1500000: b.code = syscall.B1500000 case 2000000: b.code = syscall.B2000000 case 2500000: b.code = syscall.B2500000 case 3000000: b.code = syscall.B3000000 case 3500000: b.code = syscall.B3500000 case 4000000: b.code = syscall.B4000000 default: return errors.New("invalid baudrate") } return nil } func (b *Baudrate) fromString(str string) error { vuint, err := strconv.ParseUint(str, 10, 32) if err != nil { return err } return b.FromUint(uint32(vuint)) } func (b *Baudrate) UnmarshalText(data []byte) error { return b.fromString(string(data)) } func (b *Baudrate) UnmarshalJSON(data []byte) error { return b.fromString(string(data)) } func (b *Baudrate) UnmarshalTOML(data []byte) error { return b.fromString(string(data)) } func (b *Baudrate) UnmarshalYAML(data []byte) error { return b.fromString(string(data)) } type Port struct { port *sio.Port RX <-chan string TX chan<- string newline string } func serialReader(c chan<- string, port *sio.Port, stop chan<- bool) { defer func() { port.Close() stop <- true }() scanner := bufio.NewScanner(port) scanner.Split(bufio.ScanLines) for scanner.Scan() { data := scanner.Text() if len(data) == 0 { continue } c <- data } if err := scanner.Err(); err != nil { log.Printf("device(%s): read error: %s", port.LocalAddr(), err) // TODO: this needs to be configurable } else { log.Printf("device(%s): closed", port.LocalAddr()) // TODO: this needs to be configurable } } func serialWriter(c <-chan string, port *sio.Port, newline string, stop chan<- bool) { defer func() { port.Close() stop <- true }() for data := range c { port.Write([]byte(data + newline)) } } func (port *Port) Run(stop chan<- bool) (err error) { tx := make(chan string, 10) rx := make(chan string, 20) go serialReader(rx, port.port, stop) go serialWriter(tx, port.port, port.newline, stop) port.RX = rx port.TX = tx return } func Open(device string, rate Baudrate, newline string) (port *Port, err error) { port = &Port{newline: newline} if port.port, err = sio.Open(device, rate.code); err != nil { return } return }