Fix sff panic deadlock, share the evdev probe, assorted review cleanups
This commit is contained in:
+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