-
Notifications
You must be signed in to change notification settings - Fork 41
/
syscalls.go
110 lines (94 loc) · 2.42 KB
/
syscalls.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package v4l2
import (
"errors"
"fmt"
"io/fs"
"os"
sys "golang.org/x/sys/unix"
)
// OpenDevice offers a simpler file-open operation than the Go API's os.OpenFile (the Go API's
// operation causes some drivers to return busy). It also applies file validation prior to opening the device.
func OpenDevice(path string, flags int, mode uint32) (uintptr, error) {
fstat, err := os.Stat(path)
if err != nil {
return 0, fmt.Errorf("open device: %w", err)
}
if (fstat.Mode() | fs.ModeCharDevice) == 0 {
return 0, fmt.Errorf("device open: %s: not character device", path)
}
return openDev(path, flags, mode)
}
// openDev offers a simpler file open operation than the Go API OpenFile.
// See https://cs.opensource.google/go/go/+/refs/tags/go1.19.1:src/os/file_unix.go;l=205
func openDev(path string, flags int, mode uint32) (uintptr, error) {
var fd int
var err error
for {
fd, err = sys.Openat(sys.AT_FDCWD, path, flags, mode)
if err == nil {
break
}
if errors.Is(err, ErrorInterrupted) {
continue //retry
}
return 0, &os.PathError{Op: "open", Path: path, Err: err}
}
return uintptr(fd), nil
}
// CloseDevice closes the device.
func CloseDevice(fd uintptr) error {
return closeDev(fd)
}
func closeDev(fd uintptr) error {
return sys.Close(int(fd))
}
// ioctl is a wrapper for Syscall(SYS_IOCTL)
func ioctl(fd, req, arg uintptr) (err sys.Errno) {
for {
_, _, errno := sys.Syscall(sys.SYS_IOCTL, fd, req, arg)
switch errno {
case 0:
return 0
case sys.EINTR:
continue // retry
default:
return errno
}
}
}
// send sends a request to the kernel (via ioctl syscall)
func send(fd, req, arg uintptr) error {
errno := ioctl(fd, req, arg)
if errno == 0 {
return nil
}
parsedErr := parseErrorType(errno)
switch parsedErr {
case ErrorUnsupported, ErrorSystem, ErrorBadArgument:
return parsedErr
case ErrorTimeout, ErrorTemporary:
// TODO add code for automatic retry/recovery
return errno
default:
return errno
}
}
// WaitForRead returns a channel that can be used to be notified when
// a device's is ready to be read.
func WaitForRead(dev Device) <-chan struct{} {
sigChan := make(chan struct{})
go func(fd uintptr) {
defer close(sigChan)
var fdsRead sys.FdSet
fdsRead.Set(int(fd))
tv := sys.Timeval{Sec: 2, Usec: 0}
for {
_, errno := sys.Select(int(fd+1), &fdsRead, nil, nil, &tv)
if errno == sys.EINTR {
continue
}
sigChan <- struct{}{}
}
}(dev.Fd())
return sigChan
}