Move the shared drm ioctl abi into internal/drm
This commit is contained in:
+39
-112
@@ -9,83 +9,15 @@ import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"g.fc.run/theater/cabletest/internal/drm"
|
||||
)
|
||||
|
||||
// cabletest drives the panel through drm, so /dev/fb0 holds the kernel console
|
||||
// and is not worth reading. The displayed buffer is reachable from out here
|
||||
// instead: GETCRTC names it, and root is allowed a handle to it without being
|
||||
// drm master, so it can be read without the owning process cooperating.
|
||||
const (
|
||||
drmCardGlob = "/dev/dri/card*"
|
||||
|
||||
drmVblankRelative = 0x1
|
||||
drmVblankHighCrtcMask = 0x3e
|
||||
drmVblankHighCrtcShft = 1
|
||||
|
||||
xrgbRedShift = 16
|
||||
xrgbGreenShift = 8
|
||||
xrgbBlueShift = 0
|
||||
)
|
||||
|
||||
func drmIOWR(nr, size uintptr) uintptr { return 3<<30 | size<<16 | 0x64<<8 | nr }
|
||||
|
||||
type drmModeInfo struct {
|
||||
clock uint32
|
||||
hdisplay, hsyncStart, hsyncEnd, htotal, hskew uint16
|
||||
vdisplay, vsyncStart, vsyncEnd, vtotal, vscan uint16
|
||||
vrefresh, flags, typ uint32
|
||||
name [32]byte
|
||||
}
|
||||
|
||||
type drmModeCardRes struct {
|
||||
fbIDPtr, crtcIDPtr, connIDPtr, encIDPtr uint64
|
||||
countFBs, countCRTCs, countConns, countEncs uint32
|
||||
minWidth, maxWidth, minHeight, maxHeight uint32
|
||||
}
|
||||
|
||||
type drmModeCrtc struct {
|
||||
setConnectorsPtr uint64
|
||||
countConnectors uint32
|
||||
crtcID uint32
|
||||
fbID uint32
|
||||
x, y uint32
|
||||
gammaSize uint32
|
||||
modeValid uint32
|
||||
mode drmModeInfo
|
||||
}
|
||||
|
||||
type drmModeFBCmd struct {
|
||||
fbID, width, height, pitch, bpp, depth uint32
|
||||
handle uint32
|
||||
}
|
||||
|
||||
type drmModeMapDumb struct {
|
||||
handle, pad uint32
|
||||
offset uint64
|
||||
}
|
||||
|
||||
type drmWaitVblank struct {
|
||||
typ uint32
|
||||
sequence uint32
|
||||
signal uint64
|
||||
tvSec int64
|
||||
tvUsec int64
|
||||
}
|
||||
|
||||
var (
|
||||
drmGetResources = drmIOWR(0xa0, unsafe.Sizeof(drmModeCardRes{}))
|
||||
drmGetCrtc = drmIOWR(0xa1, unsafe.Sizeof(drmModeCrtc{}))
|
||||
drmGetFB = drmIOWR(0xad, unsafe.Sizeof(drmModeFBCmd{}))
|
||||
drmMapDumb = drmIOWR(0xb3, unsafe.Sizeof(drmModeMapDumb{}))
|
||||
drmWaitVblankIO = drmIOWR(0x3a, 24)
|
||||
)
|
||||
|
||||
func drmIoctl(fd int, req uintptr, arg unsafe.Pointer) error {
|
||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), req, uintptr(arg)); errno != 0 {
|
||||
return errno
|
||||
}
|
||||
return nil
|
||||
}
|
||||
const drmCardGlob = "/dev/dri/card*"
|
||||
|
||||
type grabber struct {
|
||||
fd int
|
||||
@@ -113,26 +45,26 @@ func openGrabber() (*grabber, error) {
|
||||
}
|
||||
|
||||
func activeCrtc(fd int) (uint32, int, error) {
|
||||
var res drmModeCardRes
|
||||
if err := drmIoctl(fd, drmGetResources, unsafe.Pointer(&res)); err != nil {
|
||||
var res drm.ModeCardRes
|
||||
if err := drm.Ioctl(fd, drm.GetResources, unsafe.Pointer(&res)); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
if res.countCRTCs == 0 {
|
||||
if res.CountCRTCs == 0 {
|
||||
return 0, 0, fmt.Errorf("card has no crtcs")
|
||||
}
|
||||
crtcs := make([]uint32, res.countCRTCs)
|
||||
res.countFBs, res.countConns, res.countEncs = 0, 0, 0
|
||||
res.fbIDPtr, res.connIDPtr, res.encIDPtr = 0, 0, 0
|
||||
res.crtcIDPtr = uint64(uintptr(unsafe.Pointer(&crtcs[0])))
|
||||
if err := drmIoctl(fd, drmGetResources, unsafe.Pointer(&res)); err != nil {
|
||||
crtcs := make([]uint32, res.CountCRTCs)
|
||||
res.CountFBs, res.CountConns, res.CountEncs = 0, 0, 0
|
||||
res.FBIDPtr, res.ConnIDPtr, res.EncIDPtr = 0, 0, 0
|
||||
res.CrtcIDPtr = uint64(uintptr(unsafe.Pointer(&crtcs[0])))
|
||||
if err := drm.Ioctl(fd, drm.GetResources, unsafe.Pointer(&res)); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
for i, id := range crtcs {
|
||||
c := drmModeCrtc{crtcID: id}
|
||||
if err := drmIoctl(fd, drmGetCrtc, unsafe.Pointer(&c)); err != nil {
|
||||
c := drm.ModeCrtc{CrtcID: id}
|
||||
if err := drm.Ioctl(fd, drm.GetCrtc, unsafe.Pointer(&c)); err != nil {
|
||||
continue
|
||||
}
|
||||
if c.fbID != 0 && c.modeValid != 0 {
|
||||
if c.FBID != 0 && c.ModeValid != 0 {
|
||||
return id, i, nil
|
||||
}
|
||||
}
|
||||
@@ -145,12 +77,12 @@ func (g *grabber) close() { unix.Close(g.fd) }
|
||||
// the flip cycle decides whether it stays ahead of the writer. Woken at a
|
||||
// blank, the buffer GETCRTC then names has just gone on screen.
|
||||
func (g *grabber) waitVblank() error {
|
||||
v := drmWaitVblank{
|
||||
typ: drmVblankRelative | uint32(g.index<<drmVblankHighCrtcShft)&drmVblankHighCrtcMask,
|
||||
sequence: 1,
|
||||
v := drm.WaitVblank{
|
||||
Type: drm.VblankRelative | uint32(g.index<<drm.VblankHighCrtcShift)&drm.VblankHighCrtcMask,
|
||||
Sequence: 1,
|
||||
}
|
||||
for {
|
||||
err := drmIoctl(g.fd, drmWaitVblankIO, unsafe.Pointer(&v))
|
||||
err := drm.Ioctl(g.fd, drm.WaitVblankIO, unsafe.Pointer(&v))
|
||||
if err == unix.EINTR {
|
||||
continue
|
||||
}
|
||||
@@ -159,35 +91,35 @@ func (g *grabber) waitVblank() error {
|
||||
}
|
||||
|
||||
func (g *grabber) mapFront() (mem []byte, pitch, pw, ph int, err error) {
|
||||
c := drmModeCrtc{crtcID: g.crtc}
|
||||
if err := drmIoctl(g.fd, drmGetCrtc, unsafe.Pointer(&c)); err != nil {
|
||||
c := drm.ModeCrtc{CrtcID: g.crtc}
|
||||
if err := drm.Ioctl(g.fd, drm.GetCrtc, unsafe.Pointer(&c)); err != nil {
|
||||
return nil, 0, 0, 0, fmt.Errorf("get crtc: %w", err)
|
||||
}
|
||||
if c.fbID == 0 {
|
||||
if c.FBID == 0 {
|
||||
return nil, 0, 0, 0, fmt.Errorf("crtc %d is not scanning out anything", g.crtc)
|
||||
}
|
||||
|
||||
// Only a drm master or root is given a handle to someone else's framebuffer.
|
||||
fb := drmModeFBCmd{fbID: c.fbID}
|
||||
if err := drmIoctl(g.fd, drmGetFB, unsafe.Pointer(&fb)); err != nil {
|
||||
return nil, 0, 0, 0, fmt.Errorf("get fb %d: %w", c.fbID, err)
|
||||
fb := drm.ModeFBCmd{FBID: c.FBID}
|
||||
if err := drm.Ioctl(g.fd, drm.GetFB, unsafe.Pointer(&fb)); err != nil {
|
||||
return nil, 0, 0, 0, fmt.Errorf("get fb %d: %w", c.FBID, err)
|
||||
}
|
||||
if fb.handle == 0 {
|
||||
return nil, 0, 0, 0, fmt.Errorf("kernel gave no handle for fb %d", c.fbID)
|
||||
if fb.Handle == 0 {
|
||||
return nil, 0, 0, 0, fmt.Errorf("kernel gave no handle for fb %d", c.FBID)
|
||||
}
|
||||
if fb.bpp != 32 {
|
||||
return nil, 0, 0, 0, fmt.Errorf("expected 32bpp, got %d", fb.bpp)
|
||||
if fb.Bpp != 32 {
|
||||
return nil, 0, 0, 0, fmt.Errorf("expected 32bpp, got %d", fb.Bpp)
|
||||
}
|
||||
|
||||
m := drmModeMapDumb{handle: fb.handle}
|
||||
if err := drmIoctl(g.fd, drmMapDumb, unsafe.Pointer(&m)); err != nil {
|
||||
return nil, 0, 0, 0, fmt.Errorf("map fb %d: %w", c.fbID, err)
|
||||
m := drm.ModeMapDumb{Handle: fb.Handle}
|
||||
if err := drm.Ioctl(g.fd, drm.MapDumb, unsafe.Pointer(&m)); err != nil {
|
||||
return nil, 0, 0, 0, fmt.Errorf("map fb %d: %w", c.FBID, err)
|
||||
}
|
||||
pitch, pw, ph = int(fb.pitch), int(fb.width), int(fb.height)
|
||||
mem, err = unix.Mmap(g.fd, int64(m.offset), pitch*ph,
|
||||
pitch, pw, ph = int(fb.Pitch), int(fb.Width), int(fb.Height)
|
||||
mem, err = unix.Mmap(g.fd, int64(m.Offset), pitch*ph,
|
||||
unix.PROT_READ, unix.MAP_SHARED)
|
||||
if err != nil {
|
||||
return nil, 0, 0, 0, fmt.Errorf("mmap fb %d: %w", c.fbID, err)
|
||||
return nil, 0, 0, 0, fmt.Errorf("mmap fb %d: %w", c.FBID, err)
|
||||
}
|
||||
return mem, pitch, pw, ph, nil
|
||||
}
|
||||
@@ -217,22 +149,17 @@ func (g *grabber) frame() (*image.NRGBA, error) {
|
||||
return decode(a, pitch, pw, ph), nil
|
||||
}
|
||||
|
||||
// The panel is landscape and every draw is turned a quarter turn on its way
|
||||
// into it, so the turn is undone here to get back what a person standing in
|
||||
// front of it sees.
|
||||
// Undoes the quarter turn drm.Offset applies, to get back what a person
|
||||
// standing in front of the panel sees.
|
||||
func decode(buf []byte, pitch, pw, ph int) *image.NRGBA {
|
||||
img := image.NewNRGBA(image.Rect(0, 0, ph, pw))
|
||||
for x := 0; x < ph; x++ {
|
||||
for y := 0; y < pw; y++ {
|
||||
o := x*pitch + (pw-1-y)*4
|
||||
o := drm.Offset(x, y, pitch, pw)
|
||||
v := uint32(buf[o]) | uint32(buf[o+1])<<8 |
|
||||
uint32(buf[o+2])<<16 | uint32(buf[o+3])<<24
|
||||
img.SetNRGBA(x, y, color.NRGBA{
|
||||
R: uint8(v >> xrgbRedShift),
|
||||
G: uint8(v >> xrgbGreenShift),
|
||||
B: uint8(v >> xrgbBlueShift),
|
||||
A: 255,
|
||||
})
|
||||
r, g, b := drm.Unpack(v)
|
||||
img.SetNRGBA(x, y, color.NRGBA{R: r, G: g, B: b, A: 255})
|
||||
}
|
||||
}
|
||||
return img
|
||||
|
||||
Reference in New Issue
Block a user