1package term
2
3import (
4	"os"
5	"syscall"
6	"unsafe"
7
8	"github.com/pkg/term/termios"
9	"golang.org/x/sys/unix"
10)
11
12type attr unix.Termios
13
14func (a *attr) getSpeed() (int, error) {
15	// We generally only care about ospeed, since that's what would
16	// be used for padding characters, for example.
17
18	rate := termios.Cfgetospeed((*unix.Termios)(a))
19
20	switch rate {
21	case unix.B50:
22		return 50, nil
23	case unix.B75:
24		return 75, nil
25	case unix.B110:
26		return 110, nil
27	case unix.B134:
28		return 134, nil
29	case unix.B150:
30		return 150, nil
31	case unix.B200:
32		return 200, nil
33	case unix.B300:
34		return 300, nil
35	case unix.B600:
36		return 600, nil
37	case unix.B1200:
38		return 1200, nil
39	case unix.B1800:
40		return 1800, nil
41	case unix.B2400:
42		return 2400, nil
43	case unix.B4800:
44		return 4800, nil
45	case unix.B9600:
46		return 9600, nil
47	case unix.B19200:
48		return 19200, nil
49	case unix.B38400:
50		return 38400, nil
51	case unix.B57600:
52		return 57600, nil
53	case unix.B115200:
54		return 115200, nil
55	case unix.B230400:
56		return 230400, nil
57	case unix.B460800:
58		return 460800, nil
59	case unix.B921600:
60		return 921600, nil
61	default:
62		return 0, unix.EINVAL
63	}
64}
65
66func (a *attr) setSpeed(baud int) error {
67	var rate uint32
68	switch baud {
69	case 50:
70		rate = unix.B50
71	case 75:
72		rate = unix.B75
73	case 110:
74		rate = unix.B110
75	case 134:
76		rate = unix.B134
77	case 150:
78		rate = unix.B150
79	case 200:
80		rate = unix.B200
81	case 300:
82		rate = unix.B300
83	case 600:
84		rate = unix.B600
85	case 1200:
86		rate = unix.B1200
87	case 1800:
88		rate = unix.B1800
89	case 2400:
90		rate = unix.B2400
91	case 4800:
92		rate = unix.B4800
93	case 9600:
94		rate = unix.B9600
95	case 19200:
96		rate = unix.B19200
97	case 38400:
98		rate = unix.B38400
99	case 57600:
100		rate = unix.B57600
101	case 115200:
102		rate = unix.B115200
103	case 230400:
104		rate = unix.B230400
105	case 460800:
106		rate = unix.B460800
107	case 921600:
108		rate = unix.B921600
109	default:
110		return unix.EINVAL
111	}
112
113	err := termios.Cfsetispeed((*unix.Termios)(a), uintptr(rate))
114	if err != nil {
115		return err
116	}
117
118	err = termios.Cfsetospeed((*unix.Termios)(a), uintptr(rate))
119	if err != nil {
120		return err
121	}
122
123	return nil
124}
125
126// Open opens an asynchronous communications port.
127func Open(name string, options ...func(*Term) error) (*Term, error) {
128
129	// copied from https://github.com/kofemann/opensolaris/blob/master/usr/src/uts/common/sys/stropts.h#L229
130	// to avoid cgo dependency.
131	const (
132		STR    = ('S' << 8)
133		I_PUSH = (STR | 02)
134	)
135
136	fd, e := unix.Open(name, unix.O_NOCTTY|unix.O_CLOEXEC|unix.O_NDELAY|unix.O_RDWR, 0666)
137	if e != nil {
138		return nil, &os.PathError{"open", name, e}
139	}
140
141	modules := [2]string{"ptem", "ldterm"}
142	for _, mod := range modules {
143		err := unix.IoctlSetInt(fd, I_PUSH, int(uintptr(unsafe.Pointer(syscall.StringBytePtr(mod)))))
144		if err != nil {
145			return nil, err
146		}
147	}
148
149	orig, err := termios.Tcgetattr(uintptr(t.fd))
150	if err != nil {
151		return nil, err
152	}
153	t := Term{name: name, fd: fd, *orig}
154
155	if err := t.SetOption(options...); err != nil {
156		return nil, err
157	}
158
159	return &t, unix.SetNonblock(t.fd, false)
160}
161
162// Restore restores the state of the terminal captured at the point that
163// the terminal was originally opened.
164func (t *Term) Restore() error {
165	return termios.Tcsetattr(uintptr(t.fd), termios.TCSANOW, &t.orig)
166}
167