// Open a connection with a read timeout. func (connection *Connection) Open(timeout uint8) error { var err error // The serial port is basically a file we are writing to and reading from. // O_RDWR allows the program to read and write the file. // O_NOCTTY prevents the device from controlling the terminal. // O_NONBLOCK prevents the system from blocking for a long time. connection.f, err = os.OpenFile(connection.Port, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666) if err != nil { return err } // Close the file on error occurrence. defer func() { if err != nil && connection.f != nil { connection.f.Close() } }() // Create a plain termios, which allows the program to execute // input/output operations. termios := syscall.Termios{} // Setup the baud rate in the termios structure. baudrate := baudrates[connection.Baud] termios.Cflag |= baudrate termios.Ispeed = baudrate termios.Ospeed = baudrate // Setup stop bits in the termios structure. switch connection.StopBit { case StopBit1: termios.Cflag &^= syscall.CSTOPB // CSTOPB = 0x40 case StopBit2: termios.Cflag |= syscall.CSTOPB default: return errStopBit } // Setup data bits in the termios structure. databit := databits[connection.DataBit] termios.Cflag |= databit // Setup parity in the termios structure. switch connection.Parity { case ParityNone: termios.Cflag &^= syscall.PARENB // PARENB = 0x100 case ParityEven: termios.Cflag |= syscall.PARENB case ParityOdd: termios.Cflag |= syscall.PARENB termios.Cflag |= syscall.PARODD // PARODD = 0x200 default: return errParity } // // set blocking / non-blocking read vmin := uint8(0) if timeout == 0 { vmin = 1 } // Attach min bytes and timeout to the termios structure. termios.Cc = [32]uint8{ syscall.VMIN: vmin, // min bytes per transfer syscall.VTIME: timeout, // actual read timeout in deciseconds } // Execute IOCTL with the modified termios structure to apply the changes. if _, _, errno := syscall.Syscall6( // device-specific input/output operations syscall.SYS_IOCTL, // open file descriptor uintptr(connection.f.Fd()), // a request code number to set the current serial port settings uintptr(syscall.TCSETS), //TODO: it looks like syscall.TCSETS is not available under // freebsd and darwin. Is this a bug? uintptr(unsafe.Pointer(&termios)), // a pointer to the termios structure 0, 0, 0, ); errno != 0 { return errno } connection.isOpen = true return nil }