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 // eventfd writes efd once the point has a fence (WAIT_AVAILABLE).
22 eventfd(h uint32, point uint64, efd int) error
23 destroy(h uint32) error
24}
25
26const (
27 ioctlSyncobjDestroy = 0xC00864C0 // DRM_IOWR(0xC0, struct drm_syncobj_destroy)
28 ioctlSyncobjHandleToFD = 0xC01864C1 // DRM_IOWR(0xC1, struct drm_syncobj_handle)
29 ioctlSyncobjFDToHandle = 0xC01864C2 // DRM_IOWR(0xC2, struct drm_syncobj_handle)
30 ioctlSyncobjTimelineSignal = 0xC01864CD // DRM_IOWR(0xCD, struct drm_syncobj_timeline_array)
31 ioctlSyncobjEventfd = 0xC01864CF // DRM_IOWR(0xCF, struct drm_syncobj_eventfd)
32 ioctlGetCap = 0xC010640C // DRM_IOWR(0x0C, struct drm_get_cap)
33
34 syncobjExportSyncFile = 1 << 0
35 syncobjTimeline = 1 << 1
36 syncobjWaitAvailable = 1 << 2
37 capSyncobjTimeline = 0x14
38)
39
40type syncobjHandle struct {
41 handle, flags uint32
42 fd int32
43 pad uint32
44 point uint64
45}
46
47type syncobjDestroy struct{ handle, pad uint32 }
48
49type syncobjTimelineArray struct {
50 handles, points uint64
51 count, flags uint32
52}
53
54type syncobjEventfd struct {
55 handle, flags uint32
56 point uint64
57 fd int32
58 pad uint32
59}
60
61type drmCap struct{ capability, value uint64 }
62
63// renderNode is a DRM render node opened for syncobj ioctls.
64type renderNode struct{ fd int }
65
66// openSyncobj opens a render node that supports timeline syncobjs.
67func openSyncobj(path string) (*renderNode, error) {
68 fd, err := unix.Open(path, unix.O_RDWR|unix.O_CLOEXEC, 0)
69 if err != nil {
70 return nil, err
71 }
72 c := drmCap{capability: capSyncobjTimeline}
73 if ioctl(fd, ioctlGetCap, unsafe.Pointer(&c)) != nil || c.value == 0 {
74 unix.Close(fd)
75 return nil, fmt.Errorf("%s: no timeline syncobjs", path)
76 }
77 return &renderNode{fd: fd}, nil
78}
79
80func (n *renderNode) close() { unix.Close(n.fd) }
81
82func ioctl(fd int, req uintptr, arg unsafe.Pointer) error {
83 for {
84 _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), req, uintptr(arg))
85 if errno == unix.EINTR || errno == unix.EAGAIN {
86 continue
87 }
88 if errno != 0 {
89 return errno
90 }
91 return nil
92 }
93}
94
95func (n *renderNode) fdToHandle(fd int) (uint32, error) {
96 a := syncobjHandle{fd: int32(fd)}
97 if err := ioctl(n.fd, ioctlSyncobjFDToHandle, unsafe.Pointer(&a)); err != nil {
98 return 0, err
99 }
100 return a.handle, nil
101}
102
103func (n *renderNode) exportSyncFile(h uint32, point uint64) (*os.File, error) {
104 a := syncobjHandle{handle: h, flags: syncobjExportSyncFile | syncobjTimeline, fd: -1, point: point}
105 if err := ioctl(n.fd, ioctlSyncobjHandleToFD, unsafe.Pointer(&a)); err != nil {
106 return nil, err
107 }
108 return os.NewFile(uintptr(a.fd), "acquire-fence"), nil
109}
110
111func (n *renderNode) signal(h uint32, point uint64) error {
112 handles, points := [1]uint32{h}, [1]uint64{point}
113 a := syncobjTimelineArray{handles: uint64(uintptr(unsafe.Pointer(&handles))), points: uint64(uintptr(unsafe.Pointer(&points))), count: 1}
114 err := ioctl(n.fd, ioctlSyncobjTimelineSignal, unsafe.Pointer(&a))
115 runtime.KeepAlive(&handles)
116 runtime.KeepAlive(&points)
117 return err
118}
119
120func (n *renderNode) eventfd(h uint32, point uint64, efd int) error {
121 a := syncobjEventfd{handle: h, flags: syncobjWaitAvailable, point: point, fd: int32(efd)}
122 return ioctl(n.fd, ioctlSyncobjEventfd, unsafe.Pointer(&a))
123}
124
125func (n *renderNode) destroy(h uint32) error {
126 a := syncobjDestroy{handle: h}
127 return ioctl(n.fd, ioctlSyncobjDestroy, unsafe.Pointer(&a))
128}