blob: efd62628971ae0ca236f86207a3fa26e9bba9741 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package serial
import (
"fmt"
"io"
"os"
"syscall"
"golang.org/x/sys/unix"
)
var supportedBaudRates = map[int]uint32{
50: unix.B50,
75: unix.B75,
110: unix.B110,
134: unix.B134,
150: unix.B150,
200: unix.B200,
300: unix.B300,
600: unix.B600,
1200: unix.B1200,
1800: unix.B1800,
2400: unix.B2400,
4800: unix.B4800,
9600: unix.B9600,
19200: unix.B19200,
38400: unix.B38400,
57600: unix.B57600,
115200: unix.B115200,
230400: unix.B230400,
460800: unix.B460800,
500000: unix.B500000,
576000: unix.B576000,
921600: unix.B921600,
1000000: unix.B1000000,
1152000: unix.B1152000,
1500000: unix.B1500000,
2000000: unix.B2000000,
2500000: unix.B2500000,
3000000: unix.B3000000,
3500000: unix.B3500000,
4000000: unix.B4000000,
}
// cfmakeraw makes a termios configuration that is similar to the configuration
// produced by glibc's cfmakeraw, which is to turn off all input and output
// processing, and configure in non-canonical mode - aka "just give me the data
// stream"
func cfmakeraw(t *unix.Termios) {
t.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
t.Oflag &^= unix.OPOST
t.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
t.Cflag &^= unix.CSIZE | unix.PARENB
t.Cflag |= unix.CS8
}
func open(name string, baudRate int) (io.ReadWriteCloser, error) {
rate, ok := supportedBaudRates[baudRate]
if !ok {
return nil, fmt.Errorf("unsupported baud rate: %d", baudRate)
}
// open non-blocking to avoid waiting for carrier detect
f, err := os.OpenFile(name, unix.O_RDWR|unix.O_NOCTTY|unix.O_NONBLOCK, 0666)
if err != nil {
return nil, err
}
// we actually want blocking semantics downstream, so unset that. note: use
// syscall, not unix, because we're going to call Read/Write and may need the
// runtime poller to know we've done this.
if err := syscall.SetNonblock(int(f.Fd()), false); err != nil {
f.Close()
return nil, err
}
t := unix.Termios{}
// start with a raw mode configuration, that is non-canonical mode, post
// processing off, no line end processing, etc.
cfmakeraw(&t)
// ignore framing and parity errors
t.Iflag |= unix.IGNPAR
// enable receiver, and ignore modem control lines
t.Cflag |= unix.CREAD | unix.CLOCAL
// one stop bit
t.Cflag &^= unix.CSTOPB
// disable hardware flow control (currently generally not connected)
t.Cflag &^= unix.CRTSCTS
// do not delay reads and do not return 0 reads
t.Cc[unix.VTIME] = 0
// return 1 or more characters as soon as they are ready
t.Cc[unix.VMIN] = 1
// set baud rate
t.Ispeed = rate
t.Ospeed = rate
if err := unix.IoctlSetTermios(int(f.Fd()), unix.TCSETA, &t); err != nil {
f.Close()
return nil, err
}
return f, nil
}