1package wayland
2
3import (
4 "fmt"
5 "os"
6 "runtime"
7 "unsafe"
8
9 "golang.org/x/sys/unix"
10)
11
12// syncobjDevice is the DRM syncobj interface of a render node: explicit
13// sync timelines clients import (wp_linux_drm_syncobj_v1).
14type syncobjDevice interface {
15 // fdToHandle imports a timeline syncobj fd.
16 fdToHandle(fd int) (uint32, error)
17 // exportSyncFile returns a sync file of a timeline point's fence.
18 exportSyncFile(h uint32, point uint64) (*os.File, error)
19 // signal signals a timeline point (a release point).
20 signal(h uint32, point uint64) error
21 // signalled checks a point without blocking.
22 signalled(h uint32, point uint64) (bool, error)
23 // eventfd writes efd once the point is signalled.
24 eventfd(h uint32, point uint64, efd int) error
25 destroy(h uint32) error
26}
27
28const (
29 ioctlSyncobjDestroy = 0xC00864C0 // DRM_IOWR(0xC0, struct drm_syncobj_destroy)
30 ioctlSyncobjHandleToFD = 0xC01864C1 // DRM_IOWR(0xC1, struct drm_syncobj_handle)
31 ioctlSyncobjFDToHandle = 0xC01864C2 // DRM_IOWR(0xC2, struct drm_syncobj_handle)
32 ioctlSyncobjTimelineWait = 0xC02864CA // DRM_IOWR(0xCA, struct drm_syncobj_timeline_wait)
33 ioctlSyncobjTimelineSignal = 0xC01864CD // DRM_IOWR(0xCD, struct drm_syncobj_timeline_array)
34 ioctlSyncobjEventfd = 0xC01864CF // DRM_IOWR(0xCF, struct drm_syncobj_eventfd)
35 ioctlGetCap = 0xC010640C // DRM_IOWR(0x0C, struct drm_get_cap)
36
37 syncobjExportSyncFile = 1 << 0
38 syncobjTimeline = 1 << 1
39 capSyncobjTimeline = 0x14
40)
41
42type syncobjHandle struct {
43 handle, flags uint32
44 fd int32
45 pad uint32
46 point uint64
47}
48
49type syncobjDestroy struct{ handle, pad uint32 }
50
51type syncobjTimelineArray struct {
52 handles, points uint64
53 count, flags uint32
54}
55
56type syncobjTimelineWait struct {
57 handles, points uint64
58 timeout int64
59 count, flags uint32
60 first uint32
61 pad uint32
62}
63
64type syncobjEventfd struct {
65 handle, flags uint32
66 point uint64
67 fd int32
68 pad uint32
69}
70
71type drmCap struct{ capability, value uint64 }
72
73// renderNode is a DRM render node opened for syncobj ioctls.
74type renderNode struct{ fd int }
75
76// openSyncobj opens a render node that supports timeline syncobjs.
77func openSyncobj(path string) (*renderNode, error) {
78 fd, err := unix.Open(path, unix.O_RDWR|unix.O_CLOEXEC, 0)
79 if err != nil {
80 return nil, err
81 }
82 c := drmCap{capability: capSyncobjTimeline}
83 if ioctl(fd, ioctlGetCap, unsafe.Pointer(&c)) != nil || c.value == 0 {
84 unix.Close(fd)
85 return nil, fmt.Errorf("%s: no timeline syncobjs", path)
86 }
87 return &renderNode{fd: fd}, nil
88}
89
90func (n *renderNode) close() { unix.Close(n.fd) }
91
92func ioctl(fd int, req uintptr, arg unsafe.Pointer) error {
93 for {
94 _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), req, uintptr(arg))
95 if errno == unix.EINTR || errno == unix.EAGAIN {
96 continue
97 }
98 if errno != 0 {
99 return errno
100 }
101 return nil
102 }
103}
104
105func (n *renderNode) fdToHandle(fd int) (uint32, error) {
106 a := syncobjHandle{fd: int32(fd)}
107 if err := ioctl(n.fd, ioctlSyncobjFDToHandle, unsafe.Pointer(&a)); err != nil {
108 return 0, err
109 }
110 return a.handle, nil
111}
112
113func (n *renderNode) exportSyncFile(h uint32, point uint64) (*os.File, error) {
114 a := syncobjHandle{handle: h, flags: syncobjExportSyncFile | syncobjTimeline, fd: -1, point: point}
115 if err := ioctl(n.fd, ioctlSyncobjHandleToFD, unsafe.Pointer(&a)); err != nil {
116 return nil, err
117 }
118 return os.NewFile(uintptr(a.fd), "acquire-fence"), nil
119}
120
121func (n *renderNode) signal(h uint32, point uint64) error {
122 handles, points := [1]uint32{h}, [1]uint64{point}
123 a := syncobjTimelineArray{handles: uint64(uintptr(unsafe.Pointer(&handles))), points: uint64(uintptr(unsafe.Pointer(&points))), count: 1}
124 err := ioctl(n.fd, ioctlSyncobjTimelineSignal, unsafe.Pointer(&a))
125 runtime.KeepAlive(&handles)
126 runtime.KeepAlive(&points)
127 return err
128}
129
130func (n *renderNode) signalled(h uint32, point uint64) (bool, error) {
131 handles, points := [1]uint32{h}, [1]uint64{point}
132 a := syncobjTimelineWait{handles: uint64(uintptr(unsafe.Pointer(&handles))), points: uint64(uintptr(unsafe.Pointer(&points))), count: 1}
133 err := ioctl(n.fd, ioctlSyncobjTimelineWait, unsafe.Pointer(&a))
134 runtime.KeepAlive(&handles)
135 runtime.KeepAlive(&points)
136 if err == unix.ETIME {
137 return false, nil
138 }
139 return err == nil, err
140}
141
142func (n *renderNode) eventfd(h uint32, point uint64, efd int) error {
143 a := syncobjEventfd{handle: h, point: point, fd: int32(efd)}
144 return ioctl(n.fd, ioctlSyncobjEventfd, unsafe.Pointer(&a))
145}
146
147func (n *renderNode) destroy(h uint32) error {
148 a := syncobjDestroy{handle: h}
149 return ioctl(n.fd, ioctlSyncobjDestroy, unsafe.Pointer(&a))
150}