diff --git a/serial_linux.go b/serial_linux.go index 589c2f6..7a2ab14 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -11,19 +11,6 @@ import "regexp" import "syscall" import "unsafe" -//sys ioctl(fd int, req uint64, data uintptr) (err error) -func ioctl(fd int, req uint64, data uintptr) (err error) { - _, _, e1 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data)) - if e1 != 0 { - err = e1 - } - return -} - -func getErrno(err error) int { - return int(err.(syscall.Errno)) -} - // OS dependent values const devFolder = "/dev" @@ -34,50 +21,6 @@ type linuxSerialPort struct { handle int } -func GetPortsList() ([]string, error) { - files, err := ioutil.ReadDir(devFolder) - if err != nil { - return nil, err - } - - ports := make([]string, 0, len(files)) - for _, f := range files { - // Skip folders - if f.IsDir() { - continue - } - - // Keep only devices with the correct name - match, err := regexp.MatchString(regexFilter, f.Name()) - if err != nil { - return nil, err - } - if !match { - continue - } - - portName := devFolder + "/" + f.Name() - - // Check if serial port is real or is a placeholder serial port "ttySxx" - if f.Name()[:4] == "ttyS" { - port, err := OpenPort(portName, false) - if err != nil { - serr, ok := err.(*SerialPortError) - if ok && serr.Code() == ERROR_INVALID_SERIAL_PORT { - continue - } - } else { - port.Close() - } - } - - // Save serial port in the resulting list - ports = append(ports, portName) - } - - return ports, nil -} - func (port *linuxSerialPort) Close() error { port.releaseExclusiveAccess() return syscall.Close(port.handle) @@ -122,57 +65,6 @@ func (port *linuxSerialPort) SetSpeed(speed int) error { return port.setTermSettings(settings) } -var baudrateMap = 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, -} - -func setTermSettingsBaudrate(speed int, settings *syscall.Termios) error { - baudrate, ok := baudrateMap[speed] - if !ok { - return &SerialPortError{code: ERROR_INVALID_PORT_SPEED} - } - // revert old baudrate - var BAUDMASK uint32 = 0 - for _, rate := range baudrateMap { - BAUDMASK |= rate - } - settings.Cflag &= ^uint32(BAUDMASK) - // set new baudrate - settings.Cflag |= baudrate - settings.Ispeed = baudrate - settings.Ospeed = baudrate - return nil -} - func (port *linuxSerialPort) SetParity(parity Parity) error { settings, err := port.getTermSettings() if err != nil { @@ -184,38 +76,6 @@ func (port *linuxSerialPort) SetParity(parity Parity) error { return port.setTermSettings(settings) } -func setTermSettingsParity(parity Parity, settings *syscall.Termios) error { - const FIXED_PARITY_FLAG uint32 = 0 // may be CMSPAR or PAREXT - switch parity { - case PARITY_NONE: - settings.Cflag &= ^uint32(syscall.PARENB | syscall.PARODD | FIXED_PARITY_FLAG) - settings.Iflag &= ^uint32(syscall.INPCK) - case PARITY_ODD: - settings.Cflag |= syscall.PARENB | syscall.PARODD - settings.Cflag &= ^uint32(FIXED_PARITY_FLAG) - settings.Iflag |= syscall.INPCK - case PARITY_EVEN: - settings.Cflag &= ^uint32(syscall.PARODD | FIXED_PARITY_FLAG) - settings.Cflag |= syscall.PARENB - settings.Iflag |= syscall.INPCK - case PARITY_MARK: - settings.Cflag |= syscall.PARENB | syscall.PARODD | FIXED_PARITY_FLAG - settings.Iflag |= syscall.INPCK - case PARITY_SPACE: - settings.Cflag &= ^uint32(syscall.PARODD) - settings.Cflag |= syscall.PARENB | FIXED_PARITY_FLAG - settings.Iflag |= syscall.INPCK - } - return nil -} - -var databitsMap = map[int]uint32{ - 5: syscall.CS5, - 6: syscall.CS6, - 7: syscall.CS7, - 8: syscall.CS8, -} - func (port *linuxSerialPort) SetDataBits(bits int) error { settings, err := port.getTermSettings() if err != nil { @@ -227,16 +87,6 @@ func (port *linuxSerialPort) SetDataBits(bits int) error { return port.setTermSettings(settings) } -func setTermSettingsDataBits(bits int, settings *syscall.Termios) error { - databits, ok := databitsMap[bits] - if !ok { - return &SerialPortError{code: ERROR_INVALID_PORT_DATA_BITS} - } - settings.Cflag &= ^uint32(syscall.CSIZE) - settings.Cflag |= databits - return nil -} - func (port *linuxSerialPort) SetStopBits(bits StopBits) error { settings, err := port.getTermSettings() if err != nil { @@ -248,36 +98,6 @@ func (port *linuxSerialPort) SetStopBits(bits StopBits) error { return port.setTermSettings(settings) } -func setTermSettingsStopBits(bits StopBits, settings *syscall.Termios) error { - switch bits { - case STOPBITS_ONE: - settings.Cflag &= ^uint32(syscall.CSTOPB) - case STOPBITS_ONEPOINTFIVE, STOPBITS_TWO: - settings.Cflag |= syscall.CSTOPB - } - return nil -} - -// native syscall wrapper functions - -func (port *linuxSerialPort) getExclusiveAccess() error { - return ioctl(port.handle, syscall.TIOCEXCL, 0) -} - -func (port *linuxSerialPort) releaseExclusiveAccess() error { - return ioctl(port.handle, syscall.TIOCNXCL, 0) -} - -func (port *linuxSerialPort) getTermSettings() (*syscall.Termios, error) { - settings := &syscall.Termios{} - err := ioctl(port.handle, syscall.TCGETS, uintptr(unsafe.Pointer(settings))) - return settings, err -} - -func (port *linuxSerialPort) setTermSettings(settings *syscall.Termios) error { - return ioctl(port.handle, syscall.TCSETS, uintptr(unsafe.Pointer(settings))) -} - func OpenPort(portName string, exclusive bool) (SerialPort, error) { h, err := syscall.Open(portName, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NDELAY, 0) if err != nil { @@ -330,4 +150,182 @@ func OpenPort(portName string, exclusive bool) (SerialPort, error) { return port, nil } +func GetPortsList() ([]string, error) { + files, err := ioutil.ReadDir(devFolder) + if err != nil { + return nil, err + } + + ports := make([]string, 0, len(files)) + for _, f := range files { + // Skip folders + if f.IsDir() { + continue + } + + // Keep only devices with the correct name + match, err := regexp.MatchString(regexFilter, f.Name()) + if err != nil { + return nil, err + } + if !match { + continue + } + + portName := devFolder + "/" + f.Name() + + // Check if serial port is real or is a placeholder serial port "ttySxx" + if f.Name()[:4] == "ttyS" { + port, err := OpenPort(portName, false) + if err != nil { + serr, ok := err.(*SerialPortError) + if ok && serr.Code() == ERROR_INVALID_SERIAL_PORT { + continue + } + } else { + port.Close() + } + } + + // Save serial port in the resulting list + ports = append(ports, portName) + } + + return ports, nil +} + +// termios manipulation functions + +var baudrateMap = 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, +} + +var databitsMap = map[int]uint32{ + 5: syscall.CS5, + 6: syscall.CS6, + 7: syscall.CS7, + 8: syscall.CS8, +} + +func setTermSettingsBaudrate(speed int, settings *syscall.Termios) error { + baudrate, ok := baudrateMap[speed] + if !ok { + return &SerialPortError{code: ERROR_INVALID_PORT_SPEED} + } + // revert old baudrate + var BAUDMASK uint32 = 0 + for _, rate := range baudrateMap { + BAUDMASK |= rate + } + settings.Cflag &= ^uint32(BAUDMASK) + // set new baudrate + settings.Cflag |= baudrate + settings.Ispeed = baudrate + settings.Ospeed = baudrate + return nil +} + +func setTermSettingsParity(parity Parity, settings *syscall.Termios) error { + const FIXED_PARITY_FLAG uint32 = 0 // may be CMSPAR or PAREXT + switch parity { + case PARITY_NONE: + settings.Cflag &= ^uint32(syscall.PARENB | syscall.PARODD | FIXED_PARITY_FLAG) + settings.Iflag &= ^uint32(syscall.INPCK) + case PARITY_ODD: + settings.Cflag |= syscall.PARENB | syscall.PARODD + settings.Cflag &= ^uint32(FIXED_PARITY_FLAG) + settings.Iflag |= syscall.INPCK + case PARITY_EVEN: + settings.Cflag &= ^uint32(syscall.PARODD | FIXED_PARITY_FLAG) + settings.Cflag |= syscall.PARENB + settings.Iflag |= syscall.INPCK + case PARITY_MARK: + settings.Cflag |= syscall.PARENB | syscall.PARODD | FIXED_PARITY_FLAG + settings.Iflag |= syscall.INPCK + case PARITY_SPACE: + settings.Cflag &= ^uint32(syscall.PARODD) + settings.Cflag |= syscall.PARENB | FIXED_PARITY_FLAG + settings.Iflag |= syscall.INPCK + } + return nil +} + +func setTermSettingsDataBits(bits int, settings *syscall.Termios) error { + databits, ok := databitsMap[bits] + if !ok { + return &SerialPortError{code: ERROR_INVALID_PORT_DATA_BITS} + } + settings.Cflag &= ^uint32(syscall.CSIZE) + settings.Cflag |= databits + return nil +} + +func setTermSettingsStopBits(bits StopBits, settings *syscall.Termios) error { + switch bits { + case STOPBITS_ONE: + settings.Cflag &= ^uint32(syscall.CSTOPB) + case STOPBITS_ONEPOINTFIVE, STOPBITS_TWO: + settings.Cflag |= syscall.CSTOPB + } + return nil +} + +// native syscall wrapper functions + +func (port *linuxSerialPort) getExclusiveAccess() error { + return ioctl(port.handle, syscall.TIOCEXCL, 0) +} + +func (port *linuxSerialPort) releaseExclusiveAccess() error { + return ioctl(port.handle, syscall.TIOCNXCL, 0) +} + +func (port *linuxSerialPort) getTermSettings() (*syscall.Termios, error) { + settings := &syscall.Termios{} + err := ioctl(port.handle, syscall.TCGETS, uintptr(unsafe.Pointer(settings))) + return settings, err +} + +func (port *linuxSerialPort) setTermSettings(settings *syscall.Termios) error { + return ioctl(port.handle, syscall.TCSETS, uintptr(unsafe.Pointer(settings))) +} + +//sys ioctl(fd int, req uint64, data uintptr) (err error) +func ioctl(fd int, req uint64, data uintptr) (err error) { + _, _, e1 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data)) + if e1 != 0 { + err = e1 + } + return +} + // vi:ts=2