diff --git a/serial_freebsd.go b/serial_freebsd.go new file mode 100644 index 0000000..b3c6479 --- /dev/null +++ b/serial_freebsd.go @@ -0,0 +1,150 @@ +// +build freebsd,!cgo + +package serial + +import ( + "fmt" + "os" + "syscall" + "time" + "unsafe" +) + +func openPort(name string, baud int, databits byte, parity Parity, stopbits StopBits, 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, + 921600: syscall.B921600, + } + + rate := bauds[baud] + + if rate == 0 { + return nil, fmt.Errorf("invalid baud rate %d", baud) + } + + f, err := os.OpenFile(name, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK|syscall.O_EXCL, 0666) + if err != nil { + return nil, err + } + + defer func() { + if err != nil && f != nil { + f.Close() + } + }() + + // Base settings + cflagToUse := syscall.CREAD | syscall.CLOCAL | rate + switch databits { + case 5: + cflagToUse |= syscall.CS5 + case 6: + cflagToUse |= syscall.CS6 + case 7: + cflagToUse |= syscall.CS7 + case 8: + cflagToUse |= syscall.CS8 + default: + return nil, ErrBadSize + } + // Stop bits settings + switch stopbits { + case Stop1: + // default is 1 stop bit + case Stop2: + cflagToUse |= syscall.CSTOPB + default: + // Don't know how to set 1.5 + return nil, ErrBadStopBits + } + // Parity settings + switch parity { + case ParityNone: + // default is no parity + case ParityOdd: + cflagToUse |= syscall.PARENB + cflagToUse |= syscall.PARODD + case ParityEven: + cflagToUse |= syscall.PARENB + default: + return nil, ErrBadParity + } + fd := f.Fd() + vmin, vtime := posixTimeoutValues(readTimeout) + t := syscall.Termios{ + Iflag: syscall.IGNPAR, + Cflag: cflagToUse, + Cc: [20]uint8{syscall.VMIN: vmin, syscall.VTIME: vtime}, + Ispeed: rate, + Ospeed: rate, + } + + // from https://fossies.org/linux/cdrtools/include/schily/termios.h + // in FreeBSD TCSETS = syscall.TIOCSETAW + if _, _, errno := syscall.Syscall6( + syscall.SYS_IOCTL, + uintptr(fd), + uintptr(syscall.TIOCSETAW), + 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() +}