func (tl *TraceLog) close() {
	if tl.cfg.TraceLog.Serial.Type == "tty" {
		tl.serialConn.Close()
		tl.serialConn = nil
	} else {
		if tl.fifoFile1 != nil {
			tl.fifoFile1.Close()
			tl.fifoFile1 = nil
		}
		if tl.fifoFile2 != nil {
			tl.fifoFile2.Close()
			tl.fifoFile2 = nil
		}
	}
}
Write to Serial or FIFO with timeout
package tracelog
import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"time"
	"go.bug.st/serial.v1"
	"github.com/9elements/autorev/config"
)
type LineType int
const (
	MEM32 LineType = iota
	IO
	MSR
	CPUID
	PCI
)
type TraceLogEntry struct {
	IP uint
	Type int
	Inout bool
	Address uint
	Value uint64
	AccessSize uint
}
type TraceLog struct {
	cfg config.Config
	inputChannel chan string
	serialConn serial.Port
	fifoFile1, fifoFile2 *os.File
	verbose bool
}
func ConvertToType(inputType string) int {
	switch inputType {
	case "m":
		return int(MEM32)
	case "i":
		return int(IO)
	case "s":
		return int(MSR)
	case "c":
		return int(CPUID)
	case "p":
		return int(PCI)
	}
	return -1
}
func ConvertToDir(inputDirection string) bool {
	switch inputDirection {
	case "I":
		return true
	case "O":
		return false
	}
	return false
}
func (tle *TraceLogEntry) String() string {
	var dir string
	if tle.Inout {
		dir = "in"
	} else {
		dir = "out"
	}
	var tracelogtype string
	switch tle.Type {
	case int(MEM32):
		tracelogtype = "m"
	case int(IO):
		tracelogtype = "i"
	case int(MSR):
		tracelogtype = "s"
	case int(CPUID):
		tracelogtype = "c"
	case int(PCI):
		tracelogtype = "p"
	}
	return fmt.Sprintf("IP: %08x, Type: %s, Dir: %s, Addr: %08x, Value: %016x, Access: %d",
		tle.IP, tracelogtype, dir, tle.Address, tle.Value, tle.AccessSize)
}
func (tle *TraceLogEntry) SmallString() string {
	var dir string
	if tle.Inout {
		dir = "<-"
	} else {
		dir = "->"
	}
	var tracelogtype string
	switch tle.Type {
	case int(MEM32):
		tracelogtype = "m"
	case int(IO):
		tracelogtype = "i"
	case int(MSR):
		tracelogtype = "s"
	case int(CPUID):
		tracelogtype = "c"
	case int(PCI):
		tracelogtype = "p"
	}
	return fmt.Sprintf("%s%s %08x %016x %d",
		tracelogtype, dir, tle.Address, tle.Value, tle.AccessSize)
}
func ParseLine(line string) (*TraceLogEntry, error) {
	var new TraceLogEntry
	parts := strings.Split(line, " ")
	if len(parts) == 0 {
		return nil, fmt.Errorf("Empty line")
	}
	if parts[0] != "#B!" {
		return nil, fmt.Errorf("Line doesn't start with trace prefix")
	}
	if len(parts) == 1 {
		return nil, fmt.Errorf("Line doesn't have IP value")
	}
	i, err := strconv.ParseInt(parts[1], 16, 64)
	if err != nil {
		return nil, err
	}
	new.IP = uint(i)
	if len(parts) == 2 {
		return nil, fmt.Errorf("Line doesn't have Type value")
	}
	t := ConvertToType(parts[2])
	if t == -1 {
		return nil, fmt.Errorf("Line has unknown type value")
	}
	new.Type = t
	if len(parts) == 3 {
		return nil, fmt.Errorf("Line doesn't have InOut value")
	}
	if parts[3] != "I" && parts[3] != "O" {
		return nil, fmt.Errorf("Line has unknown InOut value")
	}
	new.Inout = parts[3] == "I"
	if len(parts) == 4 {
		return nil, fmt.Errorf("Line doesn't have Address value")
	}
	i, err = strconv.ParseInt(parts[4], 16, 64)
	if err != nil {
		return nil, err
	}
	new.Address = uint(i)
	if len(parts) == 5 {
		return nil, fmt.Errorf("Line doesn't have Value value")
	}
	i, err = strconv.ParseInt(parts[5], 16, 64)
	if err != nil {
		return nil, err
	}
	new.Value = uint64(i)
	if t == int(MSR) {
		if len(parts) == 6 {
			return nil, fmt.Errorf("Line doesn't have second Value value")
		}
		i, err = strconv.ParseInt(parts[6], 16, 64)
		if err != nil {
			return nil, err
		}
		new.Value |= uint64(i) << 32
	}
	if t == int(IO) || t == int(MEM32) || t == int(PCI) {
		if len(parts) == 6 {
			return nil, fmt.Errorf("Line doesn't have AccessSize value")
		}
		i, err = strconv.ParseInt(parts[6], 10, 32)
		if err != nil {
			return nil, err
		}
		new.AccessSize = uint(i)
	}
	return &new, nil
}
func CreateTraceLog(devTTYDevicePath string, baudTTYDevice int, fifoDevicePath string, cfg config.Config) (*TraceLog, error) {
	var t TraceLog
	if len(devTTYDevicePath) > 0 && len(fifoDevicePath) > 0 {
		return nil, fmt.Errorf("Cannot specify both: -fifo and -dev")
	}
	if len(cfg.TraceLog.Serial.Port) == 0 {
		return nil, fmt.Errorf("Collecting a new trace, but serial port not specified")
	}
	if cfg.TraceLog.Serial.Type != "fifo" && cfg.TraceLog.Serial.Type != "tty" {
		return nil, fmt.Errorf("Collecting a new trace, but serial type is unknown (must be 'fifo' or 'tty')")
	}
	t.cfg = cfg
	if len(t.cfg.TraceLog.DutControl.InitCmd) > 0 {
		t.shellcmd(t.cfg.TraceLog.DutControl.InitCmd)
	}
	if len(devTTYDevicePath) > 0 {
		t.cfg.TraceLog.Serial.Type = "tty"
		t.cfg.TraceLog.Serial.Port = devTTYDevicePath
	}
	if len(fifoDevicePath) > 0 {
		t.cfg.TraceLog.Serial.Type = "fifo"
		t.cfg.TraceLog.Serial.Port = fifoDevicePath
	}
	if baudTTYDevice > 0 {
		t.cfg.TraceLog.Serial.BaudRate = baudTTYDevice
	}
	return &t, nil
}
func (tl *TraceLog) SetVerbose(v bool) {
	tl.verbose = v
}
func (tl *TraceLog) shellcmd(s string) {
	cmd := strings.Split(s, " ")[0]
	args := strings.Split(s, " ")[1:]
	abs, err := filepath.Abs(cmd)
	if err != nil {
		log.Fatal(err)
	} else {
		log.Printf("Running shell cmd '%s'\n", abs)
		execcmd := exec.Command(abs, args...)
		execcmd.Dir = filepath.Dir(abs)
		err := execcmd.Run()
		if err != nil {
			log.Fatal(err)
		}
	}
}
func (tl *TraceLog) openWaitForSerial(timeout uint) error {
	var err error
	n := time.Now()
	if timeout == 0 {
		timeout = 5
	}
	limit := time.Duration(timeout) * time.Second
	if tl.cfg.TraceLog.Serial.ReadWriteTimeout == 0 {
		tl.cfg.TraceLog.Serial.ReadWriteTimeout = 5
	}
	if tl.cfg.TraceLog.Serial.Type == "tty" {
		if tl.cfg.TraceLog.Serial.BaudRate == 0 {
			tl.cfg.TraceLog.Serial.BaudRate = 115200
		}
		var mode = serial.Mode{
			tl.cfg.TraceLog.Serial.BaudRate, 8, serial.NoParity, serial.OneStopBit,
		}
		for time.Since(n) < limit {
			tl.serialConn, err = serial.Open(tl.cfg.TraceLog.Serial.Port, &mode)
			if err == nil {
				break
			}
			time.Sleep(time.Millisecond)
		}
		if time.Since(n) >= limit {
			tl.serialConn = nil
			return fmt.Errorf("Timeout waiting for serial device")
		}
	} else if tl.cfg.TraceLog.Serial.Type == "fifo" {
		for time.Since(n) < limit {
			var res error
			c1 := make(chan error, 1)
			go func() {
				tl.fifoFile1, err = os.OpenFile(tl.cfg.TraceLog.Serial.Port+".in", os.O_WRONLY, 0)
				if err == nil {
					tl.fifoFile2, err = os.OpenFile(tl.cfg.TraceLog.Serial.Port+".out", os.O_RDONLY, 0)
					if err != nil {
						tl.fifoFile1.Close()
					}
				}
				c1 <- err
			}()
			log.Printf("Waiting for open file\n")
			select {
			case res = <-c1:
				{
					if res != nil {
						return res
					}
					if tl.fifoFile2 == nil || tl.fifoFile1 == nil {
						return fmt.Errorf("Failed to open FIFO")
					}
					return nil
				}
			case <-time.After(time.Second):
				{
				}
			}
		}
		if time.Since(n) >= limit {
			return fmt.Errorf("Timeout waiting for fifo device")
		}
	}
	return nil
}
func (tl *TraceLog) read() (byte, error) {
	var res byte
	limit := time.Duration(tl.cfg.TraceLog.Serial.ReadWriteTimeout) * time.Second
	c1 := make(chan byte, 1)
	go func() {
		b := make([]byte, 1)
		if tl.cfg.TraceLog.Serial.Type == "tty" {
			n, err := tl.serialConn.Read(b)
			if err == nil && n == 1 {
				c1 <- b[0]
			}
		} else {
			n, err := tl.fifoFile2.Read(b)
			if err == nil && n > 0 {
				c1 <- b[0]
			}
		}
	}()
	select {
	case res = <-c1:
		{
			return res, nil
		}
	case <-time.After(limit):
		{
		}
	}
	return 0, fmt.Errorf("Timeout waiting for serial char")
}
func (tl *TraceLog) readLine() (string, error) {
	var res string
	limit := time.Duration(tl.cfg.TraceLog.Serial.ReadWriteTimeout) * time.Second
	n := time.Now()
	for time.Since(n) < limit {
		b, err := tl.read()
		if err != nil {
			return "", err
		}
		if b == '\r' {
			b, err = tl.read()
			if err != nil {
				return "", err
			}
		}
		if b == '\n' {
			return res, nil
		}
		res += string(b)
	}
	return "", fmt.Errorf("Timeout waiting for line from serial device")
}
func (tl *TraceLog) readString(search string) (string, error) {
	var res string
	limit := time.Duration(tl.cfg.TraceLog.Serial.ReadWriteTimeout) * time.Second
	n := time.Now()
	for time.Since(n) < limit {
		b, err := tl.read()
		if err != nil {
			return "", fmt.Errorf("Timeout waiting for full serial line")
		}
		res += string(b)
		if strings.Contains(res, search) {
			return res, nil
		}
	}
	return "", fmt.Errorf("Timeout waiting for serial device")
}
func (tl *TraceLog) write(buf []byte) (int, error) {
	limit := time.Duration(tl.cfg.TraceLog.Serial.ReadWriteTimeout) * time.Second
	n := time.Now()
	c := 0
	for time.Since(n) < limit {
		c1 := make(chan bool, 1)
		go func(b byte) {
			if tl.cfg.TraceLog.Serial.Type == "tty" {
				n, err := tl.serialConn.Write([]byte{b})
				if err == nil && n == 1 {
					c1 <- true
				}
			} else {
				n, err := tl.fifoFile1.Write([]byte{b})
				if err == nil && n == 1 {
					c1 <- true
				}
			}
		}(buf[0])
		select {
		case <-c1:
			{
				buf = buf[1:]
				c++
			}
		case <-time.After(limit):
			{
				return c, fmt.Errorf("Timeout waiting for serial char")
			}
		}
		if len(buf) == 0 {
			break
		}
	}
	if time.Since(n) >= limit {
		return c, fmt.Errorf("Timeout waiting to send serial char")
	}
	return c, nil
}
func (tl *TraceLog) writeString(s string) (int, error) {
	return tl.write([]byte(s))
}