Fix sff panic deadlock, share the evdev probe, assorted review cleanups
This commit is contained in:
+21
-3
@@ -46,6 +46,7 @@ func main() {
|
||||
}
|
||||
|
||||
restoreTerm := rawKeys()
|
||||
go relayStdin()
|
||||
|
||||
var err error
|
||||
if *fuzzN > 0 {
|
||||
@@ -84,6 +85,26 @@ func rawKeys() func() {
|
||||
return func() { unix.IoctlSetTermios(fd, unix.TCSETS, &saved) }
|
||||
}
|
||||
|
||||
// The child's own process group makes the tty off limits to it (reading from
|
||||
// the background raises SIGTTIN), so the harness reads and forwards to the
|
||||
// run that currently owns the console. One reader for the life of the
|
||||
// process: a copier per run would pile up under fuzz, each stealing
|
||||
// keystrokes for a run already gone.
|
||||
func relayStdin() {
|
||||
buf := make([]byte, 64)
|
||||
for {
|
||||
n, err := os.Stdin.Read(buf)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
consoleMu.Lock()
|
||||
if console != nil {
|
||||
console.Write(buf[:n])
|
||||
}
|
||||
consoleMu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func fuzz(n int, runFor time.Duration, args []string) error {
|
||||
failed := 0
|
||||
for i := 1; i <= n; i++ {
|
||||
@@ -162,13 +183,10 @@ func run(runFor time.Duration, at string, out io.Writer, args []string) error {
|
||||
// killed when go run dies, and an orphan holding the wire poisons every
|
||||
// measurement after it.
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
// The child's own process group makes the tty off limits to it (reading
|
||||
// from the background raises SIGTTIN), so the harness reads and forwards.
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go io.Copy(stdin, os.Stdin)
|
||||
consoleMu.Lock()
|
||||
console = stdin
|
||||
consoleMu.Unlock()
|
||||
|
||||
+8
-2
@@ -12,8 +12,10 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"g.fc.run/theater/cabletest/internal/drm"
|
||||
"g.fc.run/theater/cabletest/internal/evdev"
|
||||
)
|
||||
|
||||
const streamAddr = ":8080"
|
||||
@@ -115,7 +117,7 @@ func serveStream() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if touchscreen, err = openTouchscreen(); err != nil {
|
||||
if touchscreen, err = evdev.FindTouchscreen(os.O_RDWR); err != nil {
|
||||
return err
|
||||
}
|
||||
ln, err := net.Listen("tcp", streamAddr)
|
||||
@@ -158,7 +160,10 @@ func keys(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Between runs the crtc scans out the console framebuffer, which is not a
|
||||
// dumb buffer and fails to map on every frame; each error reads once, not at
|
||||
// frame rate.
|
||||
// frame rate. The pause paces the retry, since a failure ahead of the vblank
|
||||
// wait would otherwise spin hot.
|
||||
const captureErrPause = 100 * time.Millisecond
|
||||
|
||||
func captureLoop(g *grabber) {
|
||||
lastErr := ""
|
||||
for {
|
||||
@@ -168,6 +173,7 @@ func captureLoop(g *grabber) {
|
||||
lastErr = err.Error()
|
||||
fmt.Fprintf(os.Stderr, "capture: %v\n", err)
|
||||
}
|
||||
time.Sleep(captureErrPause)
|
||||
continue
|
||||
}
|
||||
lastErr = ""
|
||||
|
||||
+16
-98
@@ -2,115 +2,33 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"g.fc.run/theater/cabletest/internal/evdev"
|
||||
)
|
||||
|
||||
const (
|
||||
evSyn = 0x00
|
||||
evKey = 0x01
|
||||
evAbs = 0x03
|
||||
btnTouch = 0x14a
|
||||
absX = 0x00
|
||||
absY = 0x01
|
||||
|
||||
inputPropDirect = 0x01
|
||||
sizeofInputEvent = 24
|
||||
)
|
||||
|
||||
func evIoc(nr, size uintptr) uintptr {
|
||||
const iocRead = 2
|
||||
return iocRead<<30 | size<<16 | 'E'<<8 | nr
|
||||
}
|
||||
|
||||
type touchDev struct {
|
||||
f *os.File
|
||||
minX, maxX, minY, maxY int32
|
||||
}
|
||||
|
||||
var touchscreen *touchDev
|
||||
|
||||
// Picks the direct-input device that reports absolute X and Y — the same
|
||||
// probe cabletest runs, so both ends name the same hardware. Events written
|
||||
// to the node are fed into the input core as if the hardware produced them,
|
||||
// so cabletest sees a finger without carrying any injection code.
|
||||
func openTouchscreen() (*touchDev, error) {
|
||||
paths, err := filepath.Glob("/dev/input/event*")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, p := range paths {
|
||||
f, err := os.OpenFile(p, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
var props, absBits uint64
|
||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, f.Fd(),
|
||||
evIoc(0x09, 8), uintptr(unsafe.Pointer(&props))); errno != 0 {
|
||||
f.Close()
|
||||
continue
|
||||
}
|
||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, f.Fd(),
|
||||
evIoc(0x20+evAbs, 8), uintptr(unsafe.Pointer(&absBits))); errno != 0 {
|
||||
f.Close()
|
||||
continue
|
||||
}
|
||||
if props&(1<<inputPropDirect) == 0 ||
|
||||
absBits&(1<<absX) == 0 || absBits&(1<<absY) == 0 {
|
||||
f.Close()
|
||||
continue
|
||||
}
|
||||
t := &touchDev{f: f}
|
||||
if t.minX, t.maxX, err = axisRange(f, absX); err != nil {
|
||||
f.Close()
|
||||
return nil, fmt.Errorf("%s x range: %w", p, err)
|
||||
}
|
||||
if t.minY, t.maxY, err = axisRange(f, absY); err != nil {
|
||||
f.Close()
|
||||
return nil, fmt.Errorf("%s y range: %w", p, err)
|
||||
}
|
||||
if t.maxX <= t.minX || t.maxY <= t.minY {
|
||||
f.Close()
|
||||
return nil, fmt.Errorf("%s: degenerate axes x %d..%d y %d..%d",
|
||||
p, t.minX, t.maxX, t.minY, t.maxY)
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
return nil, fmt.Errorf("no touchscreen found among /dev/input/event*")
|
||||
}
|
||||
|
||||
func axisRange(f *os.File, axis uintptr) (min, max int32, err error) {
|
||||
var info [6]int32
|
||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, f.Fd(),
|
||||
evIoc(0x40+axis, 24), uintptr(unsafe.Pointer(&info[0]))); errno != 0 {
|
||||
return 0, 0, errno
|
||||
}
|
||||
return info[1], info[2], nil
|
||||
}
|
||||
var touchscreen *evdev.Touchscreen
|
||||
|
||||
type inputEvent struct {
|
||||
typ, code uint16
|
||||
val int32
|
||||
}
|
||||
|
||||
// Timestamps are left zero: evdev stamps injected events at delivery.
|
||||
func (t *touchDev) send(evs []inputEvent) error {
|
||||
buf := make([]byte, len(evs)*sizeofInputEvent)
|
||||
// Events written to the node are fed into the input core as if the hardware
|
||||
// produced them, so cabletest sees a finger without carrying any injection
|
||||
// code. Timestamps are left zero: evdev stamps injected events at delivery.
|
||||
func send(t *evdev.Touchscreen, evs []inputEvent) error {
|
||||
buf := make([]byte, len(evs)*evdev.EventSize)
|
||||
for i, e := range evs {
|
||||
o := i * sizeofInputEvent
|
||||
o := i * evdev.EventSize
|
||||
binary.NativeEndian.PutUint16(buf[o+16:], e.typ)
|
||||
binary.NativeEndian.PutUint16(buf[o+18:], e.code)
|
||||
binary.NativeEndian.PutUint32(buf[o+20:], uint32(e.val))
|
||||
}
|
||||
_, err := t.f.Write(buf)
|
||||
_, err := t.F.Write(buf)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -124,7 +42,7 @@ func touch(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
f := strings.Fields(string(body))
|
||||
if len(f) == 1 && f[0] == "up" {
|
||||
if err := touchscreen.send([]inputEvent{{evKey, btnTouch, 0}, {evSyn, 0, 0}}); err != nil {
|
||||
if err := send(touchscreen, []inputEvent{{evdev.Key, evdev.BtnTouch, 0}, {evdev.Syn, 0, 0}}); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
@@ -150,14 +68,14 @@ func touch(w http.ResponseWriter, r *http.Request) {
|
||||
cy = min(max(cy, 0), k.pw-1)
|
||||
xl, yl := cy, k.ph-1-cx
|
||||
t := touchscreen
|
||||
rawX := t.minX + int32(int64(xl)*int64(t.maxX-t.minX)/int64(k.pw-1))
|
||||
rawY := t.minY + int32(int64(yl)*int64(t.maxY-t.minY)/int64(k.ph-1))
|
||||
evs := []inputEvent{{evAbs, absX, rawX}, {evAbs, absY, rawY}}
|
||||
rawX := t.MinX + int32(int64(xl)*int64(t.MaxX-t.MinX)/int64(k.pw-1))
|
||||
rawY := t.MinY + int32(int64(yl)*int64(t.MaxY-t.MinY)/int64(k.ph-1))
|
||||
evs := []inputEvent{{evdev.Abs, evdev.AbsX, rawX}, {evdev.Abs, evdev.AbsY, rawY}}
|
||||
if f[0] == "down" {
|
||||
evs = append(evs, inputEvent{evKey, btnTouch, 1})
|
||||
evs = append(evs, inputEvent{evdev.Key, evdev.BtnTouch, 1})
|
||||
}
|
||||
evs = append(evs, inputEvent{evSyn, 0, 0})
|
||||
if err := t.send(evs); err != nil {
|
||||
evs = append(evs, inputEvent{evdev.Syn, 0, 0})
|
||||
if err := send(touchscreen, evs); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user