#include "compat.h"
typedef struct {
int fd;
int bus;
int addr;
int handle;
} dev_fd_map;
static dev_fd_map g_devs[OHB_MAX_DEV_FDS];
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
static volatile int g_count = 0;
int ohb_parse_dev_path(const char *path, int *bus, int *addr) {
if (!path || strncmp(path, OHB_DEV_PREFIX, sizeof(OHB_DEV_PREFIX) - 1) != 0) {
return 0;
}
return sscanf(path, "/dev/bus/usb/%d/%d", bus, addr) == 2;
}
void ohb_dev_register(int fd, int bus, int addr) {
pthread_mutex_lock(&g_lock);
for (int i = 0; i < OHB_MAX_DEV_FDS; i++) {
if (g_devs[i].fd == fd) {
g_devs[i].bus = bus;
g_devs[i].addr = addr;
g_devs[i].handle = -1;
pthread_mutex_unlock(&g_lock);
return;
}
}
for (int i = 0; i < OHB_MAX_DEV_FDS; i++) {
if (g_devs[i].fd == 0) {
g_devs[i].fd = fd;
g_devs[i].bus = bus;
g_devs[i].addr = addr;
g_devs[i].handle = -1;
g_count++;
break;
}
}
pthread_mutex_unlock(&g_lock);
}
int ohb_dev_lookup(int fd) {
if (g_count == 0) {
return -2;
}
int bus = 0, addr = 0, handle = -1;
pthread_mutex_lock(&g_lock);
for (int i = 0; i < OHB_MAX_DEV_FDS; i++) {
if (g_devs[i].fd == fd) {
bus = g_devs[i].bus;
addr = g_devs[i].addr;
handle = g_devs[i].handle;
break;
}
}
pthread_mutex_unlock(&g_lock);
if (bus == 0) {
return -2;
}
if (handle >= 0) {
return handle;
}
* 地址可能已失效(设备重新枚举过),ohb_bridge_open_ex 会用历史 VID/PID 重定位。 */
int h = -1, nb = bus, na = addr, vid = 0, pid = 0;
if (ohb_bridge_open_ex(bus, addr, &h, &nb, &na, &vid, &pid) != 0) {
return -1;
}
ohb_ident_remember(nb, na, vid, pid);
pthread_mutex_lock(&g_lock);
for (int i = 0; i < OHB_MAX_DEV_FDS; i++) {
if (g_devs[i].fd == fd) {
g_devs[i].handle = h;
g_devs[i].bus = nb;
g_devs[i].addr = na;
break;
}
}
pthread_mutex_unlock(&g_lock);
ohb_acq_set_addr(fd, nb, na);
return h;
}
int ohb_dev_addr_by_handle(int handle, int *bus, int *addr) {
int ok = 0;
if (handle < 0) {
return 0;
}
pthread_mutex_lock(&g_lock);
for (int i = 0; i < OHB_MAX_DEV_FDS; i++) {
if (g_devs[i].fd != 0 && g_devs[i].handle == handle) {
*bus = g_devs[i].bus;
*addr = g_devs[i].addr;
ok = 1;
break;
}
}
pthread_mutex_unlock(&g_lock);
return ok;
}
void ohb_dev_unregister(int fd) {
if (g_count == 0) {
return;
}
int handle = -1;
pthread_mutex_lock(&g_lock);
for (int i = 0; i < OHB_MAX_DEV_FDS; i++) {
if (g_devs[i].fd == fd) {
handle = g_devs[i].handle;
g_devs[i].fd = 0;
g_devs[i].bus = 0;
g_devs[i].addr = 0;
g_devs[i].handle = -1;
if (g_count > 0) {
g_count--;
}
break;
}
}
pthread_mutex_unlock(&g_lock);
if (handle >= 0) {
char cmd[64], reply[128];
snprintf(cmd, sizeof(cmd), "close %d", handle);
ohb_bridge_cmd(cmd, reply, sizeof(reply));
}
}
int ohb_dev_resolve_proc(int fd, int *bus, int *addr) { ohb_real_init();
char linkp[64];
char target[512];
snprintf(linkp, sizeof(linkp), "/proc/self/fd/%d", fd);
ssize_t n = g_real_readlink(linkp, target, sizeof(target) - 1);
if (n <= 0) {
return 0;
}
target[n] = '\0';
if (!ohb_parse_dev_path(target, bus, addr)) {
return 0;
}
ohb_dev_register(fd, *bus, *addr);
return 1;
}
*
* 经典 usbfs 里,对设备节点 read() 会返回完整描述符链(device + config + interface +
* endpoint)。adb 的 usb_linux 正是这么读的:
* int fd = open("/dev/bus/usb/001/003", O_RDONLY);
* desclength = read(fd, devdesc, 4096);
* 鸿蒙的 usbfs 不实现这个 read(返回 0),adb 因此判定"descriptor too small"跳过设备。
* 这里用 ArkTS 侧生成的 descriptors 文件来满足它 —— 数据本来就是我们伪造的。
*/
#define OHB_MAX_ACQ 32
typedef struct {
int fd;
int bus;
int addr;
int off;
} acq_entry;
static acq_entry g_acq[OHB_MAX_ACQ];
static pthread_mutex_t g_acq_lock = PTHREAD_MUTEX_INITIALIZER;
void ohb_acq_register(int fd, int bus, int addr) {
pthread_mutex_lock(&g_acq_lock);
for (int i = 0; i < OHB_MAX_ACQ; i++) {
if (g_acq[i].fd == fd || g_acq[i].fd == 0) {
g_acq[i].fd = fd;
g_acq[i].bus = bus;
g_acq[i].addr = addr;
g_acq[i].off = 0;
break;
}
}
pthread_mutex_unlock(&g_acq_lock);
ohb_dev_register(fd, bus, addr);
}
int ohb_acq_lookup(int fd, int *bus, int *addr) {
int found = 0;
pthread_mutex_lock(&g_acq_lock);
for (int i = 0; i < OHB_MAX_ACQ; i++) {
if (g_acq[i].fd == fd && fd != 0) {
*bus = g_acq[i].bus;
*addr = g_acq[i].addr;
found = 1;
break;
}
}
pthread_mutex_unlock(&g_acq_lock);
return found;
}
int ohb_acq_lookup_rdev(unsigned int major_no, unsigned int minor_no, int *bus, int *addr) {
if (major_no != 189) {
return 0;
}
*bus = (int)((minor_no >> 8) & 0xff);
*addr = (int)(minor_no & 0xff);
return *bus > 0 && *addr > 0;
}
int ohb_dev_known(int fd) {
if (fd <= 0) {
return 0;
}
int b = 0, a = 0;
if (ohb_acq_lookup(fd, &b, &a)) {
return 1;
}
if (g_count == 0) {
return 0;
}
int found = 0;
pthread_mutex_lock(&g_lock);
for (int i = 0; i < OHB_MAX_DEV_FDS; i++) {
if (g_devs[i].fd == fd) {
found = 1;
break;
}
}
pthread_mutex_unlock(&g_lock);
return found;
}
void ohb_acq_set_addr(int fd, int bus, int addr) { pthread_mutex_lock(&g_acq_lock);
for (int i = 0; i < OHB_MAX_ACQ; i++) {
if (g_acq[i].fd == fd && fd != 0) {
g_acq[i].bus = bus;
g_acq[i].addr = addr;
break;
}
}
pthread_mutex_unlock(&g_acq_lock);
}
*
* 设备重新枚举后 bus/devAddress 会变(实测 1-6 → 1-7 → 1-8),而程序缓存的是旧地址。
* 只靠地址已经找不到设备,但 VID/PID 不变 —— 记住历史映射,就能把旧地址翻译回"是哪台设备",
* 再让 ArkTS 侧按身份反查出新地址。对程序完全透明。 */
#define OHB_MAX_IDENT 32
typedef struct {
int bus, addr, vid, pid;
} ident_entry;
static ident_entry g_ident[OHB_MAX_IDENT];
static int g_ident_count = 0;
static pthread_mutex_t g_ident_lock = PTHREAD_MUTEX_INITIALIZER;
void ohb_ident_remember(int bus, int addr, int vid, int pid) {
if (bus <= 0 || addr <= 0 || (vid == 0 && pid == 0)) {
return;
}
pthread_mutex_lock(&g_ident_lock);
int slot = -1;
for (int i = 0; i < g_ident_count; i++) {
if (g_ident[i].bus == bus && g_ident[i].addr == addr) {
slot = i;
break;
}
}
if (slot < 0 && g_ident_count < OHB_MAX_IDENT) {
slot = g_ident_count++;
}
if (slot >= 0) {
g_ident[slot].bus = bus;
g_ident[slot].addr = addr;
g_ident[slot].vid = vid;
g_ident[slot].pid = pid;
}
pthread_mutex_unlock(&g_ident_lock);
}
int ohb_ident_lookup(int bus, int addr, int *vid, int *pid) {
int found = 0;
pthread_mutex_lock(&g_ident_lock);
for (int i = 0; i < g_ident_count; i++) {
if (g_ident[i].bus == bus && g_ident[i].addr == addr) {
*vid = g_ident[i].vid;
*pid = g_ident[i].pid;
found = 1;
break;
}
}
pthread_mutex_unlock(&g_ident_lock);
return found;
}
* 读两个小文件即可,不用访问桥接;设备一旦重新枚举,这两项就是唯一线索。 */
void ohb_ident_learn_from_tree(int bus, int addr) {
int vid = 0, pid = 0;
if (ohb_ident_lookup(bus, addr, &vid, &pid)) {
return;
}
ohb_real_init();
char path[512];
char buf[64];
for (int pass = 0; pass < 2; pass++) {
snprintf(path, sizeof(path), "%s/%d-%d/%s", OHB_FAKE_DEVICES, bus, addr,
pass == 0 ? "idVendor" : "idProduct");
int f = g_real_open(path, O_RDONLY, 0);
if (f < 0) {
return;
}
ssize_t n = g_real_read(f, buf, sizeof(buf) - 1);
g_real_close(f);
if (n <= 0) {
return;
}
buf[n] = '\0';
int v = (int)strtol(buf, NULL, 16);
if (pass == 0) {
vid = v;
} else {
pid = v;
}
}
ohb_ident_remember(bus, addr, vid, pid);
ohb_trace("ident %d-%d = %d:%d", bus, addr, vid, pid);
}
int ohb_usbfs_read(int fd, void *buf, size_t len) {
if (!buf || len == 0) {
return -1;
}
int bus = 0, addr = 0, off = -1;
pthread_mutex_lock(&g_acq_lock);
for (int i = 0; i < OHB_MAX_ACQ; i++) {
if (g_acq[i].fd == fd && fd != 0) {
bus = g_acq[i].bus;
addr = g_acq[i].addr;
off = g_acq[i].off;
break;
}
}
pthread_mutex_unlock(&g_acq_lock);
if (off < 0) {
return -1;
}
if (off > 0) {
return 0;
}
char path[512];
snprintf(path, sizeof(path), "%s/%d-%d/descriptors", OHB_FAKE_DEVICES, bus, addr);
ohb_real_init();
int f = g_real_open(path, O_RDONLY, 0);
if (f < 0) {
return 0;
}
unsigned char tmp[4096];
ssize_t n = read(f, tmp, sizeof(tmp));
g_real_close(f);
if (n <= 0) {
return 0;
}
size_t copy = (size_t)n > len ? len : (size_t)n;
memcpy(buf, tmp, copy);
pthread_mutex_lock(&g_acq_lock);
for (int i = 0; i < OHB_MAX_ACQ; i++) {
if (g_acq[i].fd == fd) {
g_acq[i].off = (int)copy;
break;
}
}
pthread_mutex_unlock(&g_acq_lock);
return (int)copy;
}
void ohb_devs_fork_prepare(void) {
pthread_mutex_lock(&g_lock);
}
void ohb_devs_fork_parent(void) {
pthread_mutex_unlock(&g_lock);
}
void ohb_devs_fork_child(void) {
pthread_mutex_init(&g_lock, NULL);
memset(g_devs, 0, sizeof(g_devs));
g_count = 0;
pthread_mutex_init(&g_acq_lock, NULL);
memset(g_acq, 0, sizeof(g_acq));
pthread_mutex_init(&g_ident_lock, NULL);
memset(g_ident, 0, sizeof(g_ident));
g_ident_count = 0;
}