// +build !windows,!linux,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, databits byte, parity Parity, stopbits StopBits, 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 case 1200: speed = C.B1200 case 600: speed = C.B600 case 300: speed = C.B300 case 200: speed = C.B200 case 150: speed = C.B150 case 134: speed = C.B134 case 110: speed = C.B110 case 75: speed = C.B75 case 50: speed = C.B50 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) // databits switch databits { case 5: st.c_cflag |= C.CS5 case 6: st.c_cflag |= C.CS6 case 7: st.c_cflag |= C.CS7 case 8: st.c_cflag |= C.CS8 default: return nil, ErrBadSize } // Parity settings switch parity { case ParityNone: // default is no parity case ParityOdd: st.c_cflag |= C.PARENB st.c_cflag |= C.PARODD case ParityEven: st.c_cflag |= C.PARENB st.c_cflag &= ^C.tcflag_t(C.PARODD) default: return nil, ErrBadParity } // Stop bits settings switch stopbits { case Stop1: // as is, default is 1 bit case Stop2: st.c_cflag |= C.CSTOPB default: return nil, ErrBadStopBits } // 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() }