pax_global_header00006660000000000000000000000064126214540230014511gustar00rootroot0000000000000052 comment=edb66533729585748de3d69af96aa8c2deeb8aac golang-github-tarm-serial-0.0+git20151113/000077500000000000000000000000001262145402300177125ustar00rootroot00000000000000golang-github-tarm-serial-0.0+git20151113/LICENSE000066400000000000000000000027071262145402300207250ustar00rootroot00000000000000Copyright (c) 2009 The Go Authors. 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 Google Inc. 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 OWNER 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-tarm-serial-0.0+git20151113/README.md000066400000000000000000000044551262145402300212010ustar00rootroot00000000000000[![GoDoc](https://godoc.org/github.com/tarm/serial?status.svg)](http://godoc.org/github.com/tarm/serial) Serial ======== A Go package to allow you to read and write from the serial port as a stream of bytes. Details ------- It aims to have the same API on all platforms, including windows. As an added bonus, the windows package does not use cgo, so you can cross compile for windows from another platform. You can cross compile with GOOS=windows GOARCH=386 go install github.com/tarm/serial Currently there is very little in the way of configurability. You can set the baud rate. Then you can Read(), Write(), or Close() the connection. By default Read() will block until at least one byte is returned. Write is the same. Currently all ports are opened with 8 data bits, 1 stop bit, no parity, no hardware flow control, and no software flow control. This works fine for many real devices and many faux serial devices including usb-to-serial converters and bluetooth serial ports. You may Read() and Write() simulantiously on the same connection (from different goroutines). Usage ----- ```go package main import ( "log" "github.com/tarm/serial" ) func main() { c := &serial.Config{Name: "COM45", Baud: 115200} s, err := serial.OpenPort(c) if err != nil { log.Fatal(err) } n, err := s.Write([]byte("test")) if err != nil { log.Fatal(err) } buf := make([]byte, 128) n, err = s.Read(buf) if err != nil { log.Fatal(err) } log.Printf("%q", buf[:n]) } ``` NonBlocking Mode ---------------- By default the returned Port reads in blocking mode. Which means `Read()` will block until at least one byte is returned. If that's not what you want, specify a positive ReadTimeout and the Read() will timeout returning 0 bytes if no bytes are read. Please note that this is the total timeout the read operation will wait and not the interval timeout between two bytes. ```go c := &serial.Config{Name: "COM45", Baud: 115200, ReadTimeout: time.Second * 5} // In this mode, you will want to suppress error for read // as 0 bytes return EOF error on Linux / POSIX n, _ = s.Read(buf) ``` Possible Future Work -------------------- - better tests (loopback etc) golang-github-tarm-serial-0.0+git20151113/basic_test.go000066400000000000000000000020751262145402300223650ustar00rootroot00000000000000package serial import ( "testing" "time" ) func TestConnection(t *testing.T) { c0 := &Config{Name: "/dev/ttyUSB0", Baud: 115200} c1 := &Config{Name: "/dev/ttyUSB1", Baud: 115200} s1, err := OpenPort(c0) if err != nil { t.Fatal(err) } s2, err := OpenPort(c1) if err != nil { t.Fatal(err) } ch := make(chan int, 1) go func() { buf := make([]byte, 128) var readCount int for { n, err := s2.Read(buf) if err != nil { t.Fatal(err) } readCount++ t.Logf("Read %v %v bytes: % 02x %s", readCount, n, buf[:n], buf[:n]) select { case <-ch: ch <- readCount close(ch) default: } } }() if _, err = s1.Write([]byte("hello")); err != nil { t.Fatal(err) } if _, err = s1.Write([]byte(" ")); err != nil { t.Fatal(err) } time.Sleep(time.Second) if _, err = s1.Write([]byte("world")); err != nil { t.Fatal(err) } time.Sleep(time.Second / 10) ch <- 0 s1.Write([]byte(" ")) // We could be blocked in the read without this c := <-ch exp := 5 if c >= exp { t.Fatalf("Expected less than %v read, got %v", exp, c) } } golang-github-tarm-serial-0.0+git20151113/serial.go000066400000000000000000000067161262145402300215320ustar00rootroot00000000000000/* Goserial is a simple go package to allow you to read and write from the serial port as a stream of bytes. It aims to have the same API on all platforms, including windows. As an added bonus, the windows package does not use cgo, so you can cross compile for windows from another platform. Unfortunately goinstall does not currently let you cross compile so you will have to do it manually: GOOS=windows make clean install Currently there is very little in the way of configurability. You can set the baud rate. Then you can Read(), Write(), or Close() the connection. Read() will block until at least one byte is returned. Write is the same. There is currently no exposed way to set the timeouts, though patches are welcome. Currently all ports are opened with 8 data bits, 1 stop bit, no parity, no hardware flow control, and no software flow control. This works fine for many real devices and many faux serial devices including usb-to-serial converters and bluetooth serial ports. You may Read() and Write() simulantiously on the same connection (from different goroutines). Example usage: package main import ( "github.com/tarm/serial" "log" ) func main() { c := &serial.Config{Name: "COM5", Baud: 115200} s, err := serial.OpenPort(c) if err != nil { log.Fatal(err) } n, err := s.Write([]byte("test")) if err != nil { log.Fatal(err) } buf := make([]byte, 128) n, err = s.Read(buf) if err != nil { log.Fatal(err) } log.Print("%q", buf[:n]) } */ package serial import ( "time" ) // Config contains the information needed to open a serial port. // // Currently few options are implemented, but more may be added in the // future (patches welcome), so it is recommended that you create a // new config addressing the fields by name rather than by order. // // For example: // // c0 := &serial.Config{Name: "COM45", Baud: 115200, ReadTimeout: time.Millisecond * 500} // or // c1 := new(serial.Config) // c1.Name = "/dev/tty.usbserial" // c1.Baud = 115200 // c1.ReadTimeout = time.Millisecond * 500 // type Config struct { Name string Baud int ReadTimeout time.Duration // Total timeout // Size int // 0 get translated to 8 // Parity SomeNewTypeToGetCorrectDefaultOf_None // StopBits SomeNewTypeToGetCorrectDefaultOf_1 // RTSFlowControl bool // DTRFlowControl bool // XONFlowControl bool // CRLFTranslate bool } // OpenPort opens a serial port with the specified configuration 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() // func RegisterBreakHandler(func()) golang-github-tarm-serial-0.0+git20151113/serial_linux.go000066400000000000000000000046011262145402300227400ustar00rootroot00000000000000// +build linux,!cgo package serial import ( "os" "syscall" "time" "unsafe" ) func openPort(name string, baud int, readTimeout time.Duration) (p *Port, err error) { var bauds = map[int]uint32{ 50: syscall.B50, 75: syscall.B75, 110: syscall.B110, 134: syscall.B134, 150: syscall.B150, 200: syscall.B200, 300: syscall.B300, 600: syscall.B600, 1200: syscall.B1200, 1800: syscall.B1800, 2400: syscall.B2400, 4800: syscall.B4800, 9600: syscall.B9600, 19200: syscall.B19200, 38400: syscall.B38400, 57600: syscall.B57600, 115200: syscall.B115200, 230400: syscall.B230400, 460800: syscall.B460800, 500000: syscall.B500000, 576000: syscall.B576000, 921600: syscall.B921600, 1000000: syscall.B1000000, 1152000: syscall.B1152000, 1500000: syscall.B1500000, 2000000: syscall.B2000000, 2500000: syscall.B2500000, 3000000: syscall.B3000000, 3500000: syscall.B3500000, 4000000: syscall.B4000000, } rate := bauds[baud] if rate == 0 { return } f, err := os.OpenFile(name, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666) if err != nil { return nil, err } defer func() { if err != nil && f != nil { f.Close() } }() fd := f.Fd() vmin, vtime := posixTimeoutValues(readTimeout) t := syscall.Termios{ Iflag: syscall.IGNPAR, Cflag: syscall.CS8 | syscall.CREAD | syscall.CLOCAL | rate, Cc: [32]uint8{syscall.VMIN: vmin, syscall.VTIME: vtime}, Ispeed: rate, Ospeed: rate, } if _, _, errno := syscall.Syscall6( syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TCSETS), uintptr(unsafe.Pointer(&t)), 0, 0, 0, ); errno != 0 { return nil, errno } if err = syscall.SetNonblock(int(fd), false); err != nil { return } return &Port{f: f}, nil } type Port struct { // We intentionly do not use an "embedded" struct so that we // don't export File f *os.File } func (p *Port) Read(b []byte) (n int, err error) { return p.f.Read(b) } func (p *Port) Write(b []byte) (n int, err error) { return p.f.Write(b) } // Discards data written to the port but not transmitted, // or data received but not read func (p *Port) Flush() error { const TCFLSH = 0x540B _, _, err := syscall.Syscall( syscall.SYS_IOCTL, uintptr(p.f.Fd()), uintptr(TCFLSH), uintptr(syscall.TCIOFLUSH), ) return err } func (p *Port) Close() (err error) { return p.f.Close() } golang-github-tarm-serial-0.0+git20151113/serial_posix.go000066400000000000000000000061331262145402300227450ustar00rootroot00000000000000// +build !windows,cgo package serial // #include // #include import "C" // TODO: Maybe change to using syscall package + ioctl instead of cgo import ( "errors" "fmt" "os" "syscall" "time" //"unsafe" ) func openPort(name string, baud int, readTimeout time.Duration) (p *Port, err error) { f, err := os.OpenFile(name, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666) if err != nil { return } fd := C.int(f.Fd()) if C.isatty(fd) != 1 { f.Close() return nil, errors.New("File is not a tty") } var st C.struct_termios _, err = C.tcgetattr(fd, &st) if err != nil { 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) } _, err = C.cfsetispeed(&st, speed) if err != nil { f.Close() return nil, err } _, err = C.cfsetospeed(&st, speed) if err != nil { f.Close() return nil, err } // Turn off break interrupts, CR->NL, Parity checks, strip, and IXON st.c_iflag &= ^C.tcflag_t(C.BRKINT | C.ICRNL | C.INPCK | C.ISTRIP | C.IXOFF | C.IXON | C.PARMRK) // Select local mode, turn off parity, set to 8 bits st.c_cflag &= ^C.tcflag_t(C.CSIZE | C.PARENB) st.c_cflag |= (C.CLOCAL | C.CREAD | C.CS8) // Select raw mode st.c_lflag &= ^C.tcflag_t(C.ICANON | C.ECHO | C.ECHOE | C.ISIG) st.c_oflag &= ^C.tcflag_t(C.OPOST) // set blocking / non-blocking read /* * http://man7.org/linux/man-pages/man3/termios.3.html * - Supports blocking read and read with timeout operations */ vmin, vtime := posixTimeoutValues(readTimeout) st.c_cc[C.VMIN] = C.cc_t(vmin) st.c_cc[C.VTIME] = C.cc_t(vtime) _, err = C.tcsetattr(fd, C.TCSANOW, &st) if err != nil { f.Close() return nil, err } //fmt.Println("Tweaking", name) r1, _, e := syscall.Syscall(syscall.SYS_FCNTL, uintptr(f.Fd()), uintptr(syscall.F_SETFL), uintptr(0)) if e != 0 || r1 != 0 { s := fmt.Sprint("Clearing NONBLOCK syscall error:", e, r1) f.Close() return nil, errors.New(s) } /* r1, _, e = syscall.Syscall(syscall.SYS_IOCTL, uintptr(f.Fd()), uintptr(0x80045402), // IOSSIOSPEED uintptr(unsafe.Pointer(&baud))); if e != 0 || r1 != 0 { s := fmt.Sprint("Baudrate syscall error:", e, r1) f.Close() return nil, os.NewError(s) } */ return &Port{f: f}, nil } type Port struct { // We intentionly do not use an "embedded" struct so that we // don't export File f *os.File } func (p *Port) Read(b []byte) (n int, err error) { return p.f.Read(b) } func (p *Port) Write(b []byte) (n int, err error) { return p.f.Write(b) } // Discards data written to the port but not transmitted, // or data received but not read func (p *Port) Flush() error { _, err := C.tcflush(C.int(p.f.Fd()), C.TCIOFLUSH) return err } func (p *Port) Close() (err error) { return p.f.Close() } golang-github-tarm-serial-0.0+git20151113/serial_windows.go000066400000000000000000000153321262145402300232760ustar00rootroot00000000000000// +build windows package serial import ( "fmt" "os" "sync" "syscall" "time" "unsafe" ) type Port struct { f *os.File fd syscall.Handle rl sync.Mutex wl sync.Mutex ro *syscall.Overlapped wo *syscall.Overlapped } type structDCB struct { DCBlength, BaudRate uint32 flags [4]byte wReserved, XonLim, XoffLim uint16 ByteSize, Parity, StopBits byte XonChar, XoffChar, ErrorChar, EofChar, EvtChar byte wReserved1 uint16 } type structTimeouts struct { ReadIntervalTimeout uint32 ReadTotalTimeoutMultiplier uint32 ReadTotalTimeoutConstant uint32 WriteTotalTimeoutMultiplier uint32 WriteTotalTimeoutConstant uint32 } func openPort(name string, baud int, readTimeout time.Duration) (p *Port, err error) { if len(name) > 0 && name[0] != '\\' { name = "\\\\.\\" + name } h, err := syscall.CreateFile(syscall.StringToUTF16Ptr(name), syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_ATTRIBUTE_NORMAL|syscall.FILE_FLAG_OVERLAPPED, 0) if err != nil { return nil, err } f := os.NewFile(uintptr(h), name) defer func() { if err != nil { f.Close() } }() if err = setCommState(h, baud); err != nil { return } if err = setupComm(h, 64, 64); err != nil { return } if err = setCommTimeouts(h, readTimeout); err != nil { return } if err = setCommMask(h); err != nil { return } ro, err := newOverlapped() if err != nil { return } wo, err := newOverlapped() if err != nil { return } port := new(Port) port.f = f port.fd = h port.ro = ro port.wo = wo return port, nil } func (p *Port) Close() error { return p.f.Close() } func (p *Port) Write(buf []byte) (int, error) { p.wl.Lock() defer p.wl.Unlock() if err := resetEvent(p.wo.HEvent); err != nil { return 0, err } var n uint32 err := syscall.WriteFile(p.fd, buf, &n, p.wo) if err != nil && err != syscall.ERROR_IO_PENDING { return int(n), err } return getOverlappedResult(p.fd, p.wo) } func (p *Port) Read(buf []byte) (int, error) { if p == nil || p.f == nil { return 0, fmt.Errorf("Invalid port on read %v %v", p, p.f) } p.rl.Lock() defer p.rl.Unlock() if err := resetEvent(p.ro.HEvent); err != nil { return 0, err } var done uint32 err := syscall.ReadFile(p.fd, buf, &done, p.ro) if err != nil && err != syscall.ERROR_IO_PENDING { return int(done), err } return getOverlappedResult(p.fd, p.ro) } // Discards data written to the port but not transmitted, // or data received but not read func (p *Port) Flush() error { return purgeComm(p.fd) } var ( nSetCommState, nSetCommTimeouts, nSetCommMask, nSetupComm, nGetOverlappedResult, nCreateEvent, nResetEvent, nPurgeComm, nFlushFileBuffers uintptr ) func init() { k32, err := syscall.LoadLibrary("kernel32.dll") if err != nil { panic("LoadLibrary " + err.Error()) } defer syscall.FreeLibrary(k32) nSetCommState = getProcAddr(k32, "SetCommState") nSetCommTimeouts = getProcAddr(k32, "SetCommTimeouts") nSetCommMask = getProcAddr(k32, "SetCommMask") nSetupComm = getProcAddr(k32, "SetupComm") nGetOverlappedResult = getProcAddr(k32, "GetOverlappedResult") nCreateEvent = getProcAddr(k32, "CreateEventW") nResetEvent = getProcAddr(k32, "ResetEvent") nPurgeComm = getProcAddr(k32, "PurgeComm") nFlushFileBuffers = getProcAddr(k32, "FlushFileBuffers") } func getProcAddr(lib syscall.Handle, name string) uintptr { addr, err := syscall.GetProcAddress(lib, name) if err != nil { panic(name + " " + err.Error()) } return addr } 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 params.BaudRate = uint32(baud) params.ByteSize = 8 r, _, err := syscall.Syscall(nSetCommState, 2, uintptr(h), uintptr(unsafe.Pointer(¶ms)), 0) if r == 0 { return err } return nil } func setCommTimeouts(h syscall.Handle, readTimeout time.Duration) error { var timeouts structTimeouts const MAXDWORD = 1<<32 - 1 // blocking read by default var timeoutMs int64 = MAXDWORD - 1 if readTimeout > 0 { // non-blocking read timeoutMs = readTimeout.Nanoseconds() / 1e6 if timeoutMs < 1 { timeoutMs = 1 } else if timeoutMs > MAXDWORD-1 { timeoutMs = MAXDWORD - 1 } } /* From http://msdn.microsoft.com/en-us/library/aa363190(v=VS.85).aspx For blocking I/O see below: Remarks: If an application sets ReadIntervalTimeout and ReadTotalTimeoutMultiplier to MAXDWORD and sets ReadTotalTimeoutConstant to a value greater than zero and less than MAXDWORD, one of the following occurs when the ReadFile function is called: If there are any bytes in the input buffer, ReadFile returns immediately with the bytes in the buffer. If there are no bytes in the input buffer, ReadFile waits until a byte arrives and then returns immediately. If no bytes arrive within the time specified by ReadTotalTimeoutConstant, ReadFile times out. */ timeouts.ReadIntervalTimeout = MAXDWORD timeouts.ReadTotalTimeoutMultiplier = MAXDWORD timeouts.ReadTotalTimeoutConstant = uint32(timeoutMs) r, _, err := syscall.Syscall(nSetCommTimeouts, 2, uintptr(h), uintptr(unsafe.Pointer(&timeouts)), 0) if r == 0 { return err } return nil } func setupComm(h syscall.Handle, in, out int) error { r, _, err := syscall.Syscall(nSetupComm, 3, uintptr(h), uintptr(in), uintptr(out)) if r == 0 { return err } return nil } func setCommMask(h syscall.Handle) error { const EV_RXCHAR = 0x0001 r, _, err := syscall.Syscall(nSetCommMask, 2, uintptr(h), EV_RXCHAR, 0) if r == 0 { return err } return nil } func resetEvent(h syscall.Handle) error { r, _, err := syscall.Syscall(nResetEvent, 1, uintptr(h), 0, 0) if r == 0 { return err } return nil } func purgeComm(h syscall.Handle) error { const PURGE_TXABORT = 0x0001 const PURGE_RXABORT = 0x0002 const PURGE_TXCLEAR = 0x0004 const PURGE_RXCLEAR = 0x0008 r, _, err := syscall.Syscall(nPurgeComm, 2, uintptr(h), PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR, 0) if r == 0 { return err } return nil } func newOverlapped() (*syscall.Overlapped, error) { var overlapped syscall.Overlapped r, _, err := syscall.Syscall6(nCreateEvent, 4, 0, 1, 0, 0, 0, 0) if r == 0 { return nil, err } overlapped.HEvent = syscall.Handle(r) return &overlapped, nil } func getOverlappedResult(h syscall.Handle, overlapped *syscall.Overlapped) (int, error) { var n int r, _, err := syscall.Syscall6(nGetOverlappedResult, 4, uintptr(h), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(&n)), 1, 0, 0) if r == 0 { return n, err } return n, nil }