diff --git a/serial.go b/serial.go index 4717738..f61ea28 100644 --- a/serial.go +++ b/serial.go @@ -56,9 +56,29 @@ Example usage: package serial import ( + "errors" "time" ) +const DefaultSize = 8 // Default value for Config.Size + +type StopBits byte +type Parity byte + +const ( + Stop1 StopBits = 1 + Stop1Half StopBits = 15 + Stop2 StopBits = 2 +) + +const ( + ParityNone Parity = 'N' + ParityOdd Parity = 'O' + ParityEven Parity = 'E' + ParityMark Parity = 'M' // parity bit is always 1 + ParitySpace Parity = 'S' // parity bit is always 0 +) + // Config contains the information needed to open a serial port. // // Currently few options are implemented, but more may be added in the @@ -79,9 +99,14 @@ type Config struct { Baud int ReadTimeout time.Duration // Total timeout - // Size int // 0 get translated to 8 - // Parity SomeNewTypeToGetCorrectDefaultOf_None - // StopBits SomeNewTypeToGetCorrectDefaultOf_1 + // Size is the number of data bits. If 0, DefaultSize is used. + Size byte + + // Parity is the bit to use and defaults to ParityNone (no parity bit). + Parity Parity + + // Number of stop bits to use. Default is 1 (1 stop bit). + StopBits StopBits // RTSFlowControl bool // DTRFlowControl bool @@ -90,9 +115,28 @@ type Config struct { // CRLFTranslate bool } +// ErrBadSize is returned if Size is not supported. +var ErrBadSize error = errors.New("unsupported serial data size") + +// ErrBadStopBits is returned if the specified StopBits setting not supported. +var ErrBadStopBits error = errors.New("unsupported stop bit setting") + +// ErrBadParity is returned if the parity is not supported. +var ErrBadParity error = errors.New("unsupported parity setting") + // OpenPort opens a serial port with the specified configuration func OpenPort(c *Config) (*Port, error) { - return openPort(c.Name, c.Baud, c.ReadTimeout) + size, par, stop := c.Size, c.Parity, c.StopBits + if size == 0 { + size = DefaultSize + } + if par == 0 { + par = ParityNone + } + if stop == 0 { + stop = Stop1 + } + return openPort(c.Name, c.Baud, size, par, stop, c.ReadTimeout) } // Converts the timeout values for Linux / POSIX systems diff --git a/serial_linux.go b/serial_linux.go index 9f0f884..adf18c6 100644 --- a/serial_linux.go +++ b/serial_linux.go @@ -9,7 +9,7 @@ import ( "unsafe" ) -func openPort(name string, baud int, readTimeout time.Duration) (p *Port, err error) { +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, @@ -60,11 +60,47 @@ func openPort(name string, baud int, readTimeout time.Duration) (p *Port, err er } }() + // 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: syscall.CS8 | syscall.CREAD | syscall.CLOCAL | rate, + Cflag: cflagToUse, Cc: [32]uint8{syscall.VMIN: vmin, syscall.VTIME: vtime}, Ispeed: rate, Ospeed: rate, diff --git a/serial_posix.go b/serial_posix.go index 95a592b..ac2bc21 100644 --- a/serial_posix.go +++ b/serial_posix.go @@ -17,7 +17,7 @@ import ( //"unsafe" ) -func openPort(name string, baud int, readTimeout time.Duration) (p *Port, err error) { +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 @@ -72,8 +72,41 @@ func openPort(name string, baud int, readTimeout time.Duration) (p *Port, err er // 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) - + 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 + 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) diff --git a/serial_windows.go b/serial_windows.go index c158dfa..ca2eeac 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -37,7 +37,7 @@ type structTimeouts struct { WriteTotalTimeoutConstant uint32 } -func openPort(name string, baud int, readTimeout time.Duration) (p *Port, err error) { +func openPort(name string, baud int, databits byte, parity Parity, stopbits StopBits, readTimeout time.Duration) (p *Port, err error) { if len(name) > 0 && name[0] != '\\' { name = "\\\\.\\" + name } @@ -59,7 +59,7 @@ func openPort(name string, baud int, readTimeout time.Duration) (p *Port, err er } }() - if err = setCommState(h, baud); err != nil { + if err = setCommState(h, baud, databits, parity, stopbits); err != nil { return } if err = setupComm(h, 64, 64); err != nil { @@ -171,7 +171,7 @@ func getProcAddr(lib syscall.Handle, name string) uintptr { return addr } -func setCommState(h syscall.Handle, baud int) error { +func setCommState(h syscall.Handle, baud int, databits byte, parity Parity, stopbits StopBits) error { var params structDCB params.DCBlength = uint32(unsafe.Sizeof(params)) @@ -179,7 +179,34 @@ func setCommState(h syscall.Handle, baud int) error { params.flags[0] |= 0x10 // Assert DSR params.BaudRate = uint32(baud) - params.ByteSize = 8 + + params.ByteSize = databits + + switch parity { + case ParityNone: + params.Parity = 0 + case ParityOdd: + params.Parity = 1 + case ParityEven: + params.Parity = 2 + case ParityMark: + params.Parity = 3 + case ParitySpace: + params.Parity = 4 + default: + return ErrBadParity + } + + switch stopbits { + case Stop1: + params.StopBits = 0 + case Stop1Half: + params.StopBits = 1 + case Stop2: + params.StopBits = 2 + default: + return ErrBadStopBits + } r, _, err := syscall.Syscall(nSetCommState, 2, uintptr(h), uintptr(unsafe.Pointer(¶ms)), 0) if r == 0 {