func open(name string) (p *port, err error) { fd, err := poller.Open(name, poller.O_RW) if err != nil { return nil, newErr("open: " + err.Error()) } if err := fd.Lock(); err != nil { return nil, ErrClosed } defer fd.Unlock() // Get attributes var tiosOrig termios.Termios err = tiosOrig.GetFd(fd.Sysfd()) if err != nil { return nil, newErr("tcgetattr: " + err.Error()) } // ?? Set HUPCL ?? // tiosOrig.CFlag().Set(termios.HUPCL) // err = tiosOrig.SetFd(fd.Sysfd(), termios.TCSANOW) // if err != nil { // return nil, newErr("tcsetattr: " + err.Error()) // } noReset := !tiosOrig.CFlag().Any(termios.HUPCL) // Set raw mode tios := tiosOrig tios.MakeRaw() err = tios.SetFd(fd.Sysfd(), termios.TCSANOW) if err != nil { return nil, newErr("tcsetattr: " + err.Error()) } return &port{fd: fd, origTermios: tiosOrig, noReset: noReset}, nil }
func TestMakeRaw(t *testing.T) { ti := termios.Termios{} ti.MakeRaw() // Check some arbitrary fields if ti.CFlag().Msk(termios.CSIZE) != termios.CS8 { t.Fatalf("Bad CFlag: %b", ti.CFlag().Val()) } /* if !ti.CFlag().All(termios.CREAD) { t.Fatalf("Bad CFlag: %b", ti.CFlag().Val()) } */ if ti.Cc(termios.VMIN) != 1 { t.Fatalf("Bad VMIN: %b", ti.Cc(termios.VMIN)) } if ti.Cc(termios.VTIME) != 0 { t.Fatalf("Bad VTIME: %b", ti.Cc(termios.VTIME)) } }
func (p *port) getConf() (conf Conf, err error) { var tios termios.Termios var noReset bool if err = p.fd.Lock(); err != nil { return conf, ErrClosed } err = tios.GetFd(p.fd.Sysfd()) noReset = p.noReset p.fd.Unlock() if err != nil { return conf, newErr("tcgetattr: " + err.Error()) } // Baudrate conf.Baudrate, err = tios.GetOSpeed() if err != nil { return conf, newErr("getospeed: " + err.Error()) } // Databits switch tios.CFlag().Msk(termios.CSIZE) { case termios.CS5: conf.Databits = 5 case termios.CS6: conf.Databits = 6 case termios.CS7: conf.Databits = 7 case termios.CS8: conf.Databits = 8 default: return conf, newErr("cannot decode databits") } // Stopbits if tios.CFlag().Any(termios.CSTOPB) { conf.Stopbits = 2 } else { conf.Stopbits = 1 } // Parity if !tios.CFlag().Any(termios.PARENB) { conf.Parity = ParityNone } else if tios.CFlag().Any(termios.CMSPAR) { if tios.CFlag().Any(termios.PARODD) { conf.Parity = ParityMark } else { conf.Parity = ParitySpace } } else { if tios.CFlag().Any(termios.PARODD) { conf.Parity = ParityOdd } else { conf.Parity = ParityEven } } // Flow rtscts := tios.CFlag().Any(termios.CRTSCTS) xoff := tios.IFlag().Any(termios.IXOFF) xon := tios.IFlag().Any(termios.IXON | termios.IXANY) if rtscts && !xoff && !xon { conf.Flow = FlowRTSCTS } else if !rtscts && xoff && xon { conf.Flow = FlowXONXOFF } else if !rtscts && !xoff && !xon { conf.Flow = FlowNone } else { conf.Flow = FlowOther } // NoReset conf.NoReset = noReset return conf, nil }
func (p *port) confSome(conf Conf, flags ConfFlags) error { if err := p.fd.Lock(); err != nil { return ErrClosed } defer p.fd.Unlock() var tios termios.Termios err := tios.GetFd(p.fd.Sysfd()) if err != nil { return newErr("tcgetattr: " + err.Error()) } if flags&ConfBaudrate != 0 { err := tios.SetOSpeed(conf.Baudrate) if err != nil { return newErr("setospeed: " + err.Error()) } err = tios.SetISpeed(conf.Baudrate) if err != nil { return newErr("setispeed: " + err.Error()) } } if flags&ConfDatabits != 0 { switch conf.Databits { case 5: tios.CFlag().Clr(termios.CSIZE).Set(termios.CS5) case 6: tios.CFlag().Clr(termios.CSIZE).Set(termios.CS6) case 7: tios.CFlag().Clr(termios.CSIZE).Set(termios.CS7) case 8: tios.CFlag().Clr(termios.CSIZE).Set(termios.CS8) default: return newErr("invalid databits value: " + strconv.Itoa(conf.Databits)) } } if flags&ConfStopbits != 0 { switch conf.Stopbits { case 1: tios.CFlag().Clr(termios.CSTOPB) case 2: tios.CFlag().Set(termios.CSTOPB) default: return newErr("invalid stopbits value: " + strconv.Itoa(conf.Stopbits)) } } if flags&ConfParity != 0 { switch conf.Parity { case ParityEven: tios.CFlag().Clr(termios.PARODD | termios.CMSPAR) tios.CFlag().Set(termios.PARENB) case ParityOdd: tios.CFlag().Clr(termios.CMSPAR) tios.CFlag().Set(termios.PARENB | termios.PARODD) case ParityMark: if termios.CMSPAR == 0 { return newErr("ParityMark not supported") } tios.CFlag().Set(termios.PARENB | termios.PARODD | termios.CMSPAR) case ParitySpace: if termios.CMSPAR == 0 { return newErr("ParitySpace not supported") } tios.CFlag().Clr(termios.PARODD) tios.CFlag().Set(termios.PARENB | termios.CMSPAR) case ParityNone: tios.CFlag().Clr(termios.PARENB | termios.PARODD | termios.CMSPAR) default: return newErr("invalid parity mode: " + conf.Parity.String()) } } if flags&ConfFlow != 0 { switch conf.Flow { case FlowRTSCTS: if termios.CRTSCTS == 0 { return newErr("FlowRTSCTS not supported") } tios.CFlag().Set(termios.CRTSCTS) tios.IFlag().Clr(termios.IXON | termios.IXOFF | termios.IXANY) case FlowXONXOFF: tios.CFlag().Clr(termios.CRTSCTS) tios.IFlag().Set(termios.IXON | termios.IXOFF) case FlowNone: tios.CFlag().Clr(termios.CRTSCTS) tios.IFlag().Clr(termios.IXON | termios.IXOFF | termios.IXANY) default: return newErr("invalid flow-control mode: " + conf.Flow.String()) } } if flags&ConfNoReset != 0 { p.noReset = conf.NoReset if p.noReset { tios.CFlag().Clr(termios.HUPCL) } else { tios.CFlag().Set(termios.HUPCL) } } err = tios.SetFd(p.fd.Sysfd(), termios.TCSANOW) if err != nil { return newErr("tcsetattr: " + err.Error()) } return nil }
func TestFlags(t *testing.T) { ti := termios.Termios{} if *ti.IFlag() != 0 || *ti.OFlag() != 0 || *ti.CFlag() != 0 || *ti.LFlag() != 0 { t.Fatalf("Termios struct not zero: %+v", ti) } ti.CFlag().Clr(termios.CSIZE).Set(termios.CS8) if ti.CFlag().Msk(termios.CSIZE) != termios.CS8 { t.Fatalf("Bad CFlag: %b", *ti.CFlag()) } if !ti.CFlag().Set(termios.CLOCAL).Any(termios.CLOCAL | termios.HUPCL) { t.Fatalf("Bad CFlag: %b", ti.CFlag().Val()) } if ti.CFlag().All(termios.CLOCAL | termios.HUPCL) { t.Fatalf("Bad CFlag: %b", ti.CFlag().Val()) } }
func TestMisc(t *testing.T) { if dev == "" { t.Skip("No TEST_SERIAL_DEV variable set.") } f, err := os.OpenFile(dev, os.O_RDWR, 0) if err != nil { t.Fatal("Open:", err) } defer f.Close() var ti termios.Termios err = ti.GetFd(int(f.Fd())) if err != nil { t.Fatal("Cannot get termios:", err) } /* Set low baudrate */ baudrate := 1200 ti.SetOSpeed(baudrate) ti.SetISpeed(baudrate) /* Disable flow control */ ti.CFlag().Clr(termios.CRTSCTS) ti.IFlag().Clr(termios.IXON | termios.IXOFF | termios.IXANY) err = ti.SetFd(int(f.Fd()), termios.TCSANOW) if err != nil { t.Fatal("Cannot set termios:", err) } /* Try to test Drain */ b := make([]byte, 600) start := time.Now() if _, err := f.Write(b); err != nil { t.Fatal("Cannot write:", err) } err = termios.Drain(int(f.Fd())) if err != nil { t.Fatal("Cannot drain:", err) } dur := time.Since(start) charTime := 10 * time.Second / time.Duration(baudrate) chars := int(dur / charTime) // Allow some fuzz for h/w queues and stuff. if chars < len(b)-16 || chars > len(b)+16 { t.Logf("Invalid tx time %v (%d chars):", dur, chars) } /* Try to test SendBreak */ start = time.Now() termios.SendBreak(int(f.Fd())) dur = time.Since(start) // POSIX says SendBreak should last between 0.25 and 1 Sec. if dur < 200*time.Millisecond || dur > 1100*time.Millisecond { t.Log("Bad SendBreak duration:", dur) } // Just call Flush if err := termios.Flush(int(f.Fd()), termios.TCIFLUSH); err != nil { t.Fatal("Flush In failed:", err) } if err := termios.Flush(int(f.Fd()), termios.TCOFLUSH); err != nil { t.Fatal("Flush Out failed:", err) } if err := termios.Flush(int(f.Fd()), termios.TCIOFLUSH); err != nil { t.Fatal("Flush InOut failed:", err) } // This should normally fail (depends on system tcflush() // implementation) if err := termios.Flush(int(f.Fd()), 4242); err == nil { t.Logf("Flush 4242 should fail!") } }