Define UHID vendorId and productId from the client

Let the client choose the USB ids, that it transmits in UHID_CREATE
requests.

PR #5623 <https://github.com/Genymobile/scrcpy/pull/5623>
This commit is contained in:
Romain Vimont 2024-12-07 12:43:49 +01:00
parent 86a68fac6c
commit 27a5934a1d
11 changed files with 52 additions and 13 deletions

View File

@ -152,8 +152,10 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, uint8_t *buf) {
return 2; return 2;
case SC_CONTROL_MSG_TYPE_UHID_CREATE: case SC_CONTROL_MSG_TYPE_UHID_CREATE:
sc_write16be(&buf[1], msg->uhid_create.id); sc_write16be(&buf[1], msg->uhid_create.id);
sc_write16be(&buf[3], msg->uhid_create.vendor_id);
sc_write16be(&buf[5], msg->uhid_create.product_id);
size_t index = 3; size_t index = 7;
index += write_string_tiny(&buf[index], msg->uhid_create.name, 127); index += write_string_tiny(&buf[index], msg->uhid_create.name, 127);
sc_write16be(&buf[index], msg->uhid_create.report_desc_size); sc_write16be(&buf[index], msg->uhid_create.report_desc_size);
@ -278,9 +280,13 @@ sc_control_msg_log(const struct sc_control_msg *msg) {
// Quote only if name is not null // Quote only if name is not null
const char *name = msg->uhid_create.name; const char *name = msg->uhid_create.name;
const char *quote = name ? "\"" : ""; const char *quote = name ? "\"" : "";
LOG_CMSG("UHID create [%" PRIu16 "] name=%s%s%s " LOG_CMSG("UHID create [%" PRIu16 "] %04" PRIx16 ":%04" PRIx16
"report_desc_size=%" PRIu16, msg->uhid_create.id, " name=%s%s%s report_desc_size=%" PRIu16,
quote, name, quote, msg->uhid_create.report_desc_size); msg->uhid_create.id,
msg->uhid_create.vendor_id,
msg->uhid_create.product_id,
quote, name, quote,
msg->uhid_create.report_desc_size);
break; break;
} }
case SC_CONTROL_MSG_TYPE_UHID_INPUT: { case SC_CONTROL_MSG_TYPE_UHID_INPUT: {

View File

@ -94,6 +94,8 @@ struct sc_control_msg {
} set_display_power; } set_display_power;
struct { struct {
uint16_t id; uint16_t id;
uint16_t vendor_id;
uint16_t product_id;
const char *name; // pointer to static data const char *name; // pointer to static data
uint16_t report_desc_size; uint16_t report_desc_size;
const uint8_t *report_desc; // pointer to static data const uint8_t *report_desc; // pointer to static data

View File

@ -7,6 +7,9 @@
/** Downcast gamepad processor to sc_gamepad_uhid */ /** Downcast gamepad processor to sc_gamepad_uhid */
#define DOWNCAST(GP) container_of(GP, struct sc_gamepad_uhid, gamepad_processor) #define DOWNCAST(GP) container_of(GP, struct sc_gamepad_uhid, gamepad_processor)
#define SC_GAMEPAD_UHID_VENDOR_ID 0
#define SC_GAMEPAD_UHID_PRODUCT_ID 0
static void static void
sc_gamepad_uhid_send_input(struct sc_gamepad_uhid *gamepad, sc_gamepad_uhid_send_input(struct sc_gamepad_uhid *gamepad,
const struct sc_hid_input *hid_input, const struct sc_hid_input *hid_input,
@ -30,6 +33,8 @@ sc_gamepad_uhid_send_open(struct sc_gamepad_uhid *gamepad,
struct sc_control_msg msg; struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_UHID_CREATE; msg.type = SC_CONTROL_MSG_TYPE_UHID_CREATE;
msg.uhid_create.id = hid_open->hid_id; msg.uhid_create.id = hid_open->hid_id;
msg.uhid_create.vendor_id = SC_GAMEPAD_UHID_VENDOR_ID;
msg.uhid_create.product_id = SC_GAMEPAD_UHID_PRODUCT_ID;
msg.uhid_create.name = hid_open->name; msg.uhid_create.name = hid_open->name;
msg.uhid_create.report_desc = hid_open->report_desc; msg.uhid_create.report_desc = hid_open->report_desc;
msg.uhid_create.report_desc_size = hid_open->report_desc_size; msg.uhid_create.report_desc_size = hid_open->report_desc_size;

View File

@ -141,6 +141,8 @@ sc_keyboard_uhid_init(struct sc_keyboard_uhid *kb,
struct sc_control_msg msg; struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_UHID_CREATE; msg.type = SC_CONTROL_MSG_TYPE_UHID_CREATE;
msg.uhid_create.id = SC_HID_ID_KEYBOARD; msg.uhid_create.id = SC_HID_ID_KEYBOARD;
msg.uhid_create.vendor_id = 0;
msg.uhid_create.product_id = 0;
msg.uhid_create.name = hid_open.name; msg.uhid_create.name = hid_open.name;
msg.uhid_create.report_desc = hid_open.report_desc; msg.uhid_create.report_desc = hid_open.report_desc;
msg.uhid_create.report_desc_size = hid_open.report_desc_size; msg.uhid_create.report_desc_size = hid_open.report_desc_size;

View File

@ -81,6 +81,8 @@ sc_mouse_uhid_init(struct sc_mouse_uhid *mouse,
struct sc_control_msg msg; struct sc_control_msg msg;
msg.type = SC_CONTROL_MSG_TYPE_UHID_CREATE; msg.type = SC_CONTROL_MSG_TYPE_UHID_CREATE;
msg.uhid_create.id = SC_HID_ID_MOUSE; msg.uhid_create.id = SC_HID_ID_MOUSE;
msg.uhid_create.vendor_id = 0;
msg.uhid_create.product_id = 0;
msg.uhid_create.name = hid_open.name; msg.uhid_create.name = hid_open.name;
msg.uhid_create.report_desc = hid_open.report_desc; msg.uhid_create.report_desc = hid_open.report_desc;
msg.uhid_create.report_desc_size = hid_open.report_desc_size; msg.uhid_create.report_desc_size = hid_open.report_desc_size;

View File

@ -329,6 +329,8 @@ static void test_serialize_uhid_create(void) {
.type = SC_CONTROL_MSG_TYPE_UHID_CREATE, .type = SC_CONTROL_MSG_TYPE_UHID_CREATE,
.uhid_create = { .uhid_create = {
.id = 42, .id = 42,
.vendor_id = 0x1234,
.product_id = 0x5678,
.name = "ABC", .name = "ABC",
.report_desc_size = sizeof(report_desc), .report_desc_size = sizeof(report_desc),
.report_desc = report_desc, .report_desc = report_desc,
@ -337,11 +339,13 @@ static void test_serialize_uhid_create(void) {
uint8_t buf[SC_CONTROL_MSG_MAX_SIZE]; uint8_t buf[SC_CONTROL_MSG_MAX_SIZE];
size_t size = sc_control_msg_serialize(&msg, buf); size_t size = sc_control_msg_serialize(&msg, buf);
assert(size == 20); assert(size == 24);
const uint8_t expected[] = { const uint8_t expected[] = {
SC_CONTROL_MSG_TYPE_UHID_CREATE, SC_CONTROL_MSG_TYPE_UHID_CREATE,
0, 42, // id 0, 42, // id
0x12, 0x34, // vendor id
0x56, 0x78, // product id
3, // name size 3, // name size
65, 66, 67, // "ABC" 65, 66, 67, // "ABC"
0, 11, // report desc size 0, 11, // report desc size

View File

@ -51,6 +51,8 @@ public final class ControlMessage {
private int id; private int id;
private byte[] data; private byte[] data;
private boolean on; private boolean on;
private int vendorId;
private int productId;
private ControlMessage() { private ControlMessage() {
} }
@ -131,10 +133,12 @@ public final class ControlMessage {
return msg; return msg;
} }
public static ControlMessage createUhidCreate(int id, String name, byte[] reportDesc) { public static ControlMessage createUhidCreate(int id, int vendorId, int productId, String name, byte[] reportDesc) {
ControlMessage msg = new ControlMessage(); ControlMessage msg = new ControlMessage();
msg.type = TYPE_UHID_CREATE; msg.type = TYPE_UHID_CREATE;
msg.id = id; msg.id = id;
msg.vendorId = vendorId;
msg.productId = productId;
msg.text = name; msg.text = name;
msg.data = reportDesc; msg.data = reportDesc;
return msg; return msg;
@ -237,4 +241,12 @@ public final class ControlMessage {
public boolean getOn() { public boolean getOn() {
return on; return on;
} }
public int getVendorId() {
return vendorId;
}
public int getProductId() {
return productId;
}
} }

View File

@ -142,9 +142,11 @@ public class ControlMessageReader {
private ControlMessage parseUhidCreate() throws IOException { private ControlMessage parseUhidCreate() throws IOException {
int id = dis.readUnsignedShort(); int id = dis.readUnsignedShort();
int vendorId = dis.readUnsignedShort();
int productId = dis.readUnsignedShort();
String name = parseString(1); String name = parseString(1);
byte[] data = parseByteArray(2); byte[] data = parseByteArray(2);
return ControlMessage.createUhidCreate(id, name, data); return ControlMessage.createUhidCreate(id, vendorId, productId, name, data);
} }
private ControlMessage parseUhidInput() throws IOException { private ControlMessage parseUhidInput() throws IOException {

View File

@ -290,7 +290,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
Device.rotateDevice(getActionDisplayId()); Device.rotateDevice(getActionDisplayId());
break; break;
case ControlMessage.TYPE_UHID_CREATE: case ControlMessage.TYPE_UHID_CREATE:
getUhidManager().open(msg.getId(), msg.getText(), msg.getData()); getUhidManager().open(msg.getId(), msg.getVendorId(), msg.getProductId(), msg.getText(), msg.getData());
break; break;
case ControlMessage.TYPE_UHID_INPUT: case ControlMessage.TYPE_UHID_INPUT:
getUhidManager().writeInput(msg.getId(), msg.getData()); getUhidManager().writeInput(msg.getId(), msg.getData());

View File

@ -48,7 +48,7 @@ public final class UhidManager {
} }
} }
public void open(int id, String name, byte[] reportDesc) throws IOException { public void open(int id, int vendorId, int productId, String name, byte[] reportDesc) throws IOException {
try { try {
FileDescriptor fd = Os.open("/dev/uhid", OsConstants.O_RDWR, 0); FileDescriptor fd = Os.open("/dev/uhid", OsConstants.O_RDWR, 0);
try { try {
@ -58,7 +58,7 @@ public final class UhidManager {
close(old); close(old);
} }
byte[] req = buildUhidCreate2Req(name, reportDesc); byte[] req = buildUhidCreate2Req(vendorId, productId, name, reportDesc);
Os.write(fd, req, 0, req.length); Os.write(fd, req, 0, req.length);
registerUhidListener(id, fd); registerUhidListener(id, fd);
@ -148,7 +148,7 @@ public final class UhidManager {
} }
} }
private static byte[] buildUhidCreate2Req(String name, byte[] reportDesc) { private static byte[] buildUhidCreate2Req(int vendorId, int productId, String name, byte[] reportDesc) {
/* /*
* struct uhid_event { * struct uhid_event {
* uint32_t type; * uint32_t type;
@ -183,8 +183,8 @@ public final class UhidManager {
buf.putShort((short) reportDesc.length); buf.putShort((short) reportDesc.length);
buf.putShort(BUS_VIRTUAL); buf.putShort(BUS_VIRTUAL);
buf.putInt(0); // vendor id buf.putInt(vendorId);
buf.putInt(0); // product id buf.putInt(productId);
buf.putInt(0); // version buf.putInt(0); // version
buf.putInt(0); // country; buf.putInt(0); // country;
buf.put(reportDesc); buf.put(reportDesc);

View File

@ -322,6 +322,8 @@ public class ControlMessageReaderTest {
DataOutputStream dos = new DataOutputStream(bos); DataOutputStream dos = new DataOutputStream(bos);
dos.writeByte(ControlMessage.TYPE_UHID_CREATE); dos.writeByte(ControlMessage.TYPE_UHID_CREATE);
dos.writeShort(42); // id dos.writeShort(42); // id
dos.writeShort(0x1234); // vendorId
dos.writeShort(0x5678); // productId
dos.writeByte(3); // name size dos.writeByte(3); // name size
dos.write("ABC".getBytes(StandardCharsets.US_ASCII)); dos.write("ABC".getBytes(StandardCharsets.US_ASCII));
byte[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; byte[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
@ -335,6 +337,8 @@ public class ControlMessageReaderTest {
ControlMessage event = reader.read(); ControlMessage event = reader.read();
Assert.assertEquals(ControlMessage.TYPE_UHID_CREATE, event.getType()); Assert.assertEquals(ControlMessage.TYPE_UHID_CREATE, event.getType());
Assert.assertEquals(42, event.getId()); Assert.assertEquals(42, event.getId());
Assert.assertEquals(0x1234, event.getVendorId());
Assert.assertEquals(0x5678, event.getProductId());
Assert.assertEquals("ABC", event.getText()); Assert.assertEquals("ABC", event.getText());
Assert.assertArrayEquals(data, event.getData()); Assert.assertArrayEquals(data, event.getData());