MeowOS for HiBy R3 Pro II — 公共发布版本(历史已重置)

• 全新单提交:清除含敏感信息的旧历史(sudo 口令、内网 IP、SSH 密钥路径、热点 SSID、设备 MAC)
• 本地开发文档/测试记录/构建脚本全部移入 gitignored 的 devdocs/ 与 scripts/build/(不入库)
• 保留:MeowOS 源码(launcher/watchdog/bootmenu/meow_svcd/SDK)、项目规范文档、mips-repo
增补组件:SDK 统一日志+通知 API、meow_svcd 服务管理器、mips-repo 软件仓库
This commit is contained in:
MeowOS Dev committed 2026-08-30 01:12:34 +08:00
commit 245bee2946
136 files changed
+32730

No files matched your search

BIN
View File
Binary file not shown.
+466
View File
@@ -0,0 +1,466 @@
/* uinput_tap.c — remote touch/key injection for R3 Pro II.
*
* Modes:
* uinput_tap daemon <cmd_fifo> create a virtual goodix-style touch
* device + a virtual key device, then
* serve commands from the fifo
* uinput_tap tap <fifo> <x> <y> enqueue a tap command
* uinput_tap key <fifo> <code> enqueue a key press+release
* uinput_tap seq <fifo> <x1,y1;x2,y2;...> taps with 300ms gaps
*
* The virtual devices are created BEFORE hiby_player starts so its
* hgl_ts input scanner opens them.
*
* Static MIPS32r2 musl build:
* ~/Project/R3ProII/.tools/zig-mipsel-cc -Os -static uinput_tap.c -o uinput_tap
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <linux/input.h>
#include <linux/uinput.h>
#define DEV "/dev/uinput"
/* ---- input codes (avoid header dependency issues) ---- */
#ifndef EV_SYN
#define EV_SYN 0
#define EV_KEY 1
#define EV_REL 2
#define EV_ABS 3
#endif
#ifndef ABS_MT_SLOT
#define ABS_MT_SLOT 0x2f
#define ABS_MT_TOUCH_MAJOR 0x30
#define ABS_MT_POSITION_X 0x35
#define ABS_MT_POSITION_Y 0x36
#define ABS_MT_TRACKING_ID 0x39
#endif
#ifndef BTN_TOUCH
#define BTN_TOUCH 0x14a
#endif
#ifndef SYN_REPORT
#define SYN_REPORT 0
#endif
#ifndef KEY_VOLUMEUP
#define KEY_VOLUMEUP 115
#define KEY_VOLUMEDOWN 114
#define KEY_POWER 116
#define KEY_PLAY 207
#define KEY_NEXT 163
#define KEY_PREVIOUS 165
#endif
static int ufd = -1;
static void emit(int type, int code, int val)
{
struct input_event ev;
memset(&ev, 0, sizeof ev);
ev.type = type;
ev.code = code;
ev.value = val;
if (write(ufd, &ev, sizeof ev) != sizeof ev)
perror("write event");
}
static void syn(void)
{
emit(EV_SYN, SYN_REPORT, 0);
}
/* full tap: MT protocol B + legacy ABS_X/ABS_Y + BTN_TOUCH */
static void tap(int x, int y)
{
/* MT slot */
emit(EV_ABS, ABS_MT_SLOT, 0);
emit(EV_ABS, ABS_MT_TRACKING_ID, 7);
emit(EV_ABS, ABS_MT_POSITION_X, x);
emit(EV_ABS, ABS_MT_POSITION_Y, y);
emit(EV_ABS, ABS_MT_TOUCH_MAJOR, 6);
/* legacy */
emit(EV_ABS, ABS_X, x);
emit(EV_ABS, ABS_Y, y);
emit(EV_KEY, BTN_TOUCH, 1);
syn();
usleep(60000);
emit(EV_ABS, ABS_MT_TRACKING_ID, -1);
emit(EV_KEY, BTN_TOUCH, 0);
syn();
}
static void keytap(int code)
{
emit(EV_KEY, code, 1);
syn();
usleep(80000);
emit(EV_KEY, code, 0);
syn();
}
/* Use the legacy write()-based uinput API (struct uinput_user_dev) which is
* supported by all kernels including the Ingenic 4.4.94 SDK (UI_DEV_SETUP
* ioctl layout differs across versions -> avoid it). */
/* Ingenic kernel 4.4.94 changed UINPUT_IOCTL_BASE from 'U'(0x55) to 'f'(0x66) */
#define UINPUT_BASE 0x66
#define UI_DEV_CREATE ((0x66u << 8) | 1u)
#define UI_DEV_DESTROY ((0x66u << 8) | 2u)
#define UI_SET_EVBIT (0x40000000u | (4u << 16) | (0x66u << 8) | 100u)
#define UI_SET_KEYBIT (0x40000000u | (4u << 16) | (0x66u << 8) | 101u)
#define UI_SET_RELBIT (0x40000000u | (4u << 16) | (0x66u << 8) | 102u)
#define UI_SET_ABSBIT (0x40000000u | (4u << 16) | (0x66u << 8) | 103u)
struct uinput_user_dev_v44 {
char name[80];
struct input_id id;
int ff_effects_max;
int absmax[64];
int absmin[64];
int absfuzz[64];
int absflat[64];
};
static void set_abs(struct uinput_user_dev_v44 *u, int code, int min, int max)
{
u->absmin[code] = min;
u->absmax[code] = max;
}
static int dev_create(const char *name, unsigned short vendor,
unsigned short product, unsigned short version)
{
struct uinput_user_dev_v44 u;
memset(&u, 0, sizeof u);
strncpy(u.name, name, 79);
u.id.bustype = BUS_VIRTUAL;
u.id.vendor = vendor;
u.id.product = product;
u.id.version = version;
if (write(ufd, &u, sizeof u) != (ssize_t)sizeof u) { perror("write uinput"); return -1; }
if (ioctl(ufd, UI_DEV_CREATE) < 0) { perror("UI_DEV_CREATE"); return -1; }
return 0;
}
static int create_touch(const char *name)
{
ioctl(ufd, UI_SET_EVBIT, EV_KEY);
ioctl(ufd, UI_SET_EVBIT, EV_ABS);
ioctl(ufd, UI_SET_EVBIT, EV_SYN);
ioctl(ufd, UI_SET_KEYBIT, BTN_TOUCH);
ioctl(ufd, UI_SET_ABSBIT, ABS_X);
ioctl(ufd, UI_SET_ABSBIT, ABS_Y);
ioctl(ufd, UI_SET_ABSBIT, ABS_MT_SLOT);
ioctl(ufd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
ioctl(ufd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
ioctl(ufd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
ioctl(ufd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
struct uinput_user_dev_v44 u;
memset(&u, 0, sizeof u);
strncpy(u.name, name, 79);
u.id.bustype = BUS_VIRTUAL;
u.id.vendor = 0xdead;
u.id.product = 0xbeef;
u.id.version = 1;
set_abs(&u, ABS_X, 0, 1023);
set_abs(&u, ABS_Y, 0, 1023);
set_abs(&u, ABS_MT_SLOT, 0, 9);
set_abs(&u, ABS_MT_TOUCH_MAJOR, 0, 15);
set_abs(&u, ABS_MT_POSITION_X, 0, 1023);
set_abs(&u, ABS_MT_POSITION_Y, 0, 1023);
set_abs(&u, ABS_MT_TRACKING_ID, 0, 65535);
if (write(ufd, &u, sizeof u) != (ssize_t)sizeof u) { perror("write uinput"); return -1; }
if (ioctl(ufd, UI_DEV_CREATE) < 0) { perror("UI_DEV_CREATE"); return -1; }
return 0;
}
static int create_keys(const char *name)
{
ioctl(ufd, UI_SET_EVBIT, EV_KEY);
ioctl(ufd, UI_SET_EVBIT, EV_SYN);
ioctl(ufd, UI_SET_KEYBIT, KEY_POWER);
ioctl(ufd, UI_SET_KEYBIT, KEY_VOLUMEUP);
ioctl(ufd, UI_SET_KEYBIT, KEY_VOLUMEDOWN);
ioctl(ufd, UI_SET_KEYBIT, KEY_PLAY);
ioctl(ufd, UI_SET_KEYBIT, KEY_NEXT);
ioctl(ufd, UI_SET_KEYBIT, KEY_PREVIOUS);
struct uinput_user_dev_v44 u;
memset(&u, 0, sizeof u);
strncpy(u.name, name, 79);
u.id.bustype = BUS_VIRTUAL;
u.id.vendor = 1;
u.id.product = 1;
u.id.version = 0x100;
if (write(ufd, &u, sizeof u) != (ssize_t)sizeof u) { perror("write uinput"); return -1; }
if (ioctl(ufd, UI_DEV_CREATE) < 0) { perror("UI_DEV_CREATE"); return -1; }
return 0;
}
int main(int argc, char **argv)
{
if (argc < 2) {
fprintf(stderr, "usage: %s daemon|tap|key|seq ...\n", argv[0]);
return 1;
}
if (!strcmp(argv[1], "scan")) {
setvbuf(stdout, NULL, _IONBF, 0);
ufd = open(DEV, O_WRONLY | O_NONBLOCK);
if (ufd < 0) { perror("open uinput"); return 1; }
struct uinput_user_dev_v44 u;
memset(&u, 0, sizeof u);
strncpy(u.name, "scan-test", 79);
u.id.bustype = BUS_VIRTUAL;
u.id.vendor = 5; u.id.product = 5; u.id.version = 1;
write(ufd, &u, sizeof u);
/* scan all 16-bit cmd cores (type<<8|nr), report non-EINVAL/ENOTTY */
int found = 0;
for (int t = 0; t < 256 && found < 40; t++) {
for (int n = 0; n < 256 && found < 40; n++) {
unsigned int cmd = (t << 8) | n; /* no dir/size bits */
errno = 0;
int rc = ioctl(ufd, cmd, 0);
if (rc == 0 || (errno != EINVAL && errno != ENOTTY)) {
printf("cmd=0x%04x (t=%02x n=%02x) rc=%d errno=%d\n", cmd, t, n, rc, errno);
found++;
}
}
}
printf("done found=%d\n", found);
/* deep scan nr 0x7d/0x7e with valid pointer + size bits */
int val = 0;
for (int nr = 0x7d; nr <= 0x7e; nr++) {
for (int sz = 0; sz < 128; sz++) {
unsigned int cmd = 0x40000000u | ((unsigned int)sz << 16) | (0x66u << 8) | (unsigned int)nr;
errno = 0;
int rc = ioctl(ufd, cmd, &val);
if (rc == 0 || (errno != EINVAL && errno != ENOTTY)) {
printf("deep cmd=0x%08x nr=%02x sz=%d rc=%d errno=%d\n", cmd, nr, sz, rc, errno);
}
}
}
/* also try _IOR (0x80000000) */
for (int nr = 0x7d; nr <= 0x7e; nr++) {
for (int sz = 0; sz < 128; sz++) {
unsigned int cmd = 0x80000000u | ((unsigned int)sz << 16) | (0x66u << 8) | (unsigned int)nr;
errno = 0;
int rc = ioctl(ufd, cmd, &val);
if (rc == 0 || (errno != EINVAL && errno != ENOTTY)) {
printf("read cmd=0x%08x nr=%02x sz=%d rc=%d errno=%d\n", cmd, nr, sz, rc, errno);
}
}
}
sleep(1);
return 0;
}
if (!strcmp(argv[1], "verbose2")) {
setvbuf(stdout, NULL, _IONBF, 0);
ufd = open(DEV, O_WRONLY | O_NONBLOCK);
if (ufd < 0) { perror("open uinput"); return 1; }
printf("fd=%d\n", ufd);
int r;
r = ioctl(ufd, UI_SET_EVBIT, EV_KEY); printf("SET_EVBIT KEY rc=%d errno=%d\n", r, errno);
r = ioctl(ufd, UI_SET_EVBIT, EV_ABS); printf("SET_EVBIT ABS rc=%d errno=%d\n", r, errno);
r = ioctl(ufd, UI_SET_EVBIT, EV_SYN); printf("SET_EVBIT SYN rc=%d errno=%d\n", r, errno);
r = ioctl(ufd, UI_SET_KEYBIT, BTN_TOUCH); printf("SET_KEYBIT TOUCH rc=%d errno=%d\n", r, errno);
r = ioctl(ufd, UI_SET_ABSBIT, ABS_X); printf("SET_ABSBIT X rc=%d errno=%d\n", r, errno);
struct uinput_user_dev_v44 u;
memset(&u, 0, sizeof u);
strncpy(u.name, "v2-test", 79);
u.id.bustype = BUS_VIRTUAL;
u.id.vendor = 6; u.id.product = 6; u.id.version = 1;
set_abs(&u, ABS_X, 0, 1023);
ssize_t wr = write(ufd, &u, sizeof u);
printf("write=%zd errno=%d\n", wr, errno);
r = ioctl(ufd, UI_DEV_CREATE); printf("DEV_CREATE rc=%d errno=%d\n", r, errno);
printf("sleeping 10s...\n");
sleep(10);
return 0;
}
if (!strcmp(argv[1], "inject")) {
/* 直接 write /dev/input/event1 (goodix-ts): evdev 支持 write 注入。
* 事件序列与 daemon 的 tap() 完全一致 (MT 协议 B + legacy ABS_X/Y +
* BTN_TOUCH),与真实 goodix 驱动一致,hgl_ts 解析最可靠。 */
setvbuf(stdout, NULL, _IONBF, 0);
const char *devpath = argc > 4 ? argv[4] : "/dev/input/event1";
int fd = open(devpath, O_WRONLY);
if (fd < 0) { perror("open evdev"); return 1; }
struct input_event ev;
int x = atoi(argv[2]), y = atoi(argv[3]);
ssize_t wr;
#define W(type_, code_, val_) do { \
memset(&ev, 0, sizeof ev); \
ev.type = (type_); ev.code = (code_); ev.value = (val_); \
wr = write(fd, &ev, sizeof ev); \
printf("wr %s rc=%zd errno=%d\n", #code_, wr, errno); \
} while (0)
W(EV_ABS, ABS_MT_SLOT, 0);
W(EV_ABS, ABS_MT_TRACKING_ID, 7);
W(EV_ABS, ABS_MT_POSITION_X, x);
W(EV_ABS, ABS_MT_POSITION_Y, y);
W(EV_ABS, ABS_MT_TOUCH_MAJOR, 6);
W(EV_ABS, ABS_X, x);
W(EV_ABS, ABS_Y, y);
W(EV_KEY, BTN_TOUCH, 1);
W(EV_SYN, SYN_REPORT, 0);
usleep(80000);
W(EV_ABS, ABS_MT_TRACKING_ID, -1);
W(EV_KEY, BTN_TOUCH, 0);
W(EV_SYN, SYN_REPORT, 0);
printf("injected tap %d,%d to %s\n", x, y, devpath);
close(fd);
return 0;
}
if (!strcmp(argv[1], "alt")) {
setvbuf(stdout, NULL, _IONBF, 0);
ufd = open(DEV, O_WRONLY | O_NONBLOCK);
if (ufd < 0) { perror("open uinput"); return 1; }
struct uinput_user_dev_v44 u;
memset(&u, 0, sizeof u);
strncpy(u.name, "alt-test", 79);
u.id.bustype = BUS_VIRTUAL;
u.id.vendor = 3; u.id.product = 3; u.id.version = 1;
/* 厂商命令 nr=125/126 (base 0x66): 试带各种结构 */
int r;
r = ioctl(ufd, 0x667d, &u); printf("ioctl 0x667d(user_dev) rc=%d errno=%d\n", r, errno);
r = ioctl(ufd, 0x667e, &u); printf("ioctl 0x667e(user_dev) rc=%d errno=%d\n", r, errno);
r = ioctl(ufd, 0x6601, 0); printf("ioctl 0x6601(create) rc=%d errno=%d\n", r, errno);
if (r == 0) { printf("created? sleeping\n"); sleep(4); }
return 0;
}
if (!strcmp(argv[1], "brute")) {
ufd = open(DEV, O_WRONLY | O_NONBLOCK);
if (ufd < 0) { perror("open uinput"); return 1; }
struct uinput_user_dev_v44 u;
memset(&u, 0, sizeof u);
strncpy(u.name, "brute-test", 79);
u.id.bustype = BUS_VIRTUAL;
u.id.vendor = 7; u.id.product = 7; u.id.version = 1;
ssize_t wr = write(ufd, &u, sizeof u);
printf("write=%zd errno=%d\n", wr, errno);
/* brute UI_DEV_CREATE = _IO(base,1) = (base<<8)|1 */
for (int b = 0; b < 256; b++) {
unsigned long req = (unsigned long)((b << 8) | 1); /* brute keeps generic */
errno = 0;
int rc = ioctl(ufd, req, 0);
if (rc == 0) { printf("FOUND base=0x%02x (%c) rc=0\n", b, b>=32&&b<127?b:'?'); break; }
if (errno != EINVAL && errno != ENOTTY)
printf("base=0x%02x rc=%d errno=%d\n", b, rc, errno);
}
sleep(2);
return 0;
}
if (!strcmp(argv[1], "verbose")) {
ufd = open(DEV, O_WRONLY | O_NONBLOCK);
if (ufd < 0) { perror("open uinput"); return 1; }
printf("fd=%d\n", ufd);
int r;
r = ioctl(ufd, UI_SET_EVBIT, EV_KEY); printf("SET_EVBIT KEY rc=%d errno=%d\n", r, errno);
r = ioctl(ufd, UI_SET_EVBIT, EV_SYN); printf("SET_EVBIT SYN rc=%d errno=%d\n", r, errno);
r = ioctl(ufd, UI_SET_KEYBIT, KEY_POWER); printf("SET_KEYBIT PWR rc=%d errno=%d\n", r, errno);
struct uinput_user_dev_v44 u;
memset(&u, 0, sizeof u);
strncpy(u.name, "verbose-test", 79);
u.id.bustype = BUS_VIRTUAL;
u.id.vendor = 8; u.id.product = 8; u.id.version = 1;
ssize_t wr = write(ufd, &u, sizeof u);
printf("write rc=%zd errno=%d (size %zu)\n", wr, errno, sizeof u);
r = ioctl(ufd, UI_DEV_CREATE); printf("DEV_CREATE rc=%d errno=%d\n", r, errno);
if (r == 0) { printf("OK sleeping\n"); sleep(3); }
return 0;
}
if (!strcmp(argv[1], "minimal")) {
ufd = open(DEV, O_WRONLY | O_NONBLOCK);
if (ufd < 0) { perror("open uinput"); return 1; }
ioctl(ufd, UI_SET_EVBIT, EV_KEY);
ioctl(ufd, UI_SET_EVBIT, EV_SYN);
ioctl(ufd, UI_SET_KEYBIT, KEY_POWER);
struct uinput_user_dev_v44 u;
memset(&u, 0, sizeof u);
strncpy(u.name, "minimal-test", 79);
u.id.bustype = BUS_VIRTUAL;
u.id.vendor = 9;
u.id.product = 9;
u.id.version = 1;
ssize_t wr = write(ufd, &u, sizeof u);
printf("write=%zd (expect %zu)\n", wr, sizeof u);
int rc = ioctl(ufd, UI_DEV_CREATE);
printf("create rc=%d errno=%d\n", rc, errno);
if (rc == 0) {
printf("device created, sleeping 5s\n");
sleep(5);
}
return 0;
}
if (!strcmp(argv[1], "daemon")) {
if (argc < 3) { fprintf(stderr, "daemon <fifo>\n"); return 1; }
ufd = open(DEV, O_WRONLY | O_NONBLOCK);
if (ufd < 0) { perror("open uinput"); return 1; }
if (create_touch("goodix-ts-virt") < 0) return 1;
printf("touch device created\n");
if (create_keys("md-gpio-keys-virt") < 0) return 1;
printf("key device created\n");
fflush(stdout);
/* serve fifo */
unlink(argv[2]);
if (mkfifo(argv[2], 0600) < 0) { perror("mkfifo"); return 1; }
for (;;) {
int f = open(argv[2], O_RDONLY);
if (f < 0) { perror("open fifo"); sleep(1); continue; }
char line[128];
int n;
while ((n = read(f, line, sizeof line - 1)) > 0) {
line[n] = 0;
int x, y, code;
if (sscanf(line, "tap %d %d", &x, &y) == 2) {
printf("tap %d %d\n", x, y); fflush(stdout);
tap(x, y);
} else if (sscanf(line, "key %d", &code) == 1) {
printf("key %d\n", code); fflush(stdout);
keytap(code);
}
}
close(f);
}
} else if (!strcmp(argv[1], "tap")) {
int x = atoi(argv[3]), y = atoi(argv[4]);
int f = open(argv[2], O_WRONLY);
if (f < 0) { perror("fifo"); return 1; }
char buf[64];
int n = snprintf(buf, sizeof buf, "tap %d %d\n", x, y);
write(f, buf, n);
close(f);
return 0;
} else if (!strcmp(argv[1], "key")) {
int code = atoi(argv[3]);
int f = open(argv[2], O_WRONLY);
if (f < 0) { perror("fifo"); return 1; }
char buf[64];
int n = snprintf(buf, sizeof buf, "key %d\n", code);
write(f, buf, n);
close(f);
return 0;
} else if (!strcmp(argv[1], "seq")) {
/* seq <fifo> x1,y1;x2,y2;... */
int f = open(argv[2], O_WRONLY);
if (f < 0) { perror("fifo"); return 1; }
char *p = argv[3];
char buf[64];
while (*p) {
int x, y;
if (sscanf(p, "%d,%d", &x, &y) == 2) {
int n = snprintf(buf, sizeof buf, "tap %d %d\n", x, y);
write(f, buf, n);
usleep(350000);
}
while (*p && *p != ';') p++;
if (*p == ';') p++;
}
close(f);
return 0;
}
return 0;
}