@@ -8,7 +8,7 @@ hid report descriptors在usb描述符中的关系
88
99![ report descriptors] ( ./reportid.png )
1010
11- 下面是一个三个按键的标准鼠标
11+ ## 标准三键的鼠标
1212
1313![ 3 buttons mouse] ( ./mouse.png )
1414
@@ -301,6 +301,195 @@ qemu代码里描述上述鼠标按键的结构时HIDPointerEvent
301301 0xc0, /* End Collection */
302302 };
303303
304+ ## 标准五键(两个侧键)鼠标
305+
306+ 鼠标按键用button1, button2, button3...来命名
307+
308+ qemu代码中用结构体HIDPointerEvent来描述鼠标的数据
309+ typedef struct HIDPointerEvent {
310+ int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute * /
311+ int32_t dz, buttons_state;
312+ } HIDPointerEvent;
313+
314+ 其中buttons_state每一位表示按键,这里用int32_t说明可以支持32个按键(一般支持8个按键即可uint8_t)
315+ bit1对应button1, bit2对应button2,以此类推
316+
317+ 一般操作系统中将对应按键映射成不同功能
318+
319+ button1: left (鼠标左键)
320+ button2: right (鼠标右键)
321+ button3: middle (滚轮按键)
322+ button4: side (侧边按键1)
323+ button5: extra (侧边按键2)
324+
325+ 鼠标设备上报buttonN经过系统映射后变为对应的功能按键(left,right,middle...)
326+
327+ 大部分标准5键鼠标都是按照上面的进行映射(button4,button5的映射非强制)
328+
329+ 所以在qemu模拟的鼠标中可以将button4和button5和side和extra对应以达到同大部分物理鼠标效果
330+
331+ ### 下面以spice-input和USB HID鼠标为例说明
332+
333+ spice捕获坐标后传递给qemu的input子系统来作为鼠标数据提供给虚拟鼠标设备
334+
335+ SpiceInput(spice-input.c)->InputCore(input.c)->USB HID Device(hid.c)
336+
337+ SpiceInput中通过button_mask来传入鼠标按键掩码值
338+ 下面代将button4配置为side, button5配置为extra
339+ ``` c
340+ static void spice_update_buttons (QemuSpicePointer * pointer,
341+ int wheel, uint32_t button_mask)
342+ {
343+ static uint32_t bmap[ INPUT_BUTTON__ MAX] = {
344+ [ INPUT_BUTTON_LEFT] = 0x01, /* button 1 * /
345+ [ INPUT_BUTTON_MIDDLE] = 0x04, /* button 3 * /
346+ [ INPUT_BUTTON_RIGHT] = 0x02, /* button 2 * /
347+ [ INPUT_BUTTON_WHEEL_UP] = 0x40, /* button 7 * /
348+ [ INPUT_BUTTON_WHEEL_DOWN] = 0x80, /* button 8 * /
349+ [ INPUT_BUTTON_SIDE] = 0x08, /* button 4 * /
350+ [ INPUT_BUTTON_EXTRA] = 0x10, /* button 5 * /
351+ };
352+
353+ if (wheel < 0) {
354+ button_mask |= 0x40;
355+ }
356+ if (wheel > 0) {
357+ button_mask |= 0x80;
358+ }
359+ ```
360+
361+ qemu中通过文件qapi/ui.json来生成头文件中对应的按键枚举
362+ ```c
363+ { 'enum' : 'InputButton',
364+ 'data' : [ 'left', 'middle', 'right', 'wheel-up', 'wheel-down', 'side',
365+ 'extra' ] }
366+
367+ 下面是生成的对应的枚举值
368+ typedef enum InputButton {
369+ INPUT_BUTTON_LEFT = 0,
370+ INPUT_BUTTON_MIDDLE = 1,
371+ INPUT_BUTTON_RIGHT = 2,
372+ INPUT_BUTTON_WHEEL_UP = 3,
373+ INPUT_BUTTON_WHEEL_DOWN = 4,
374+ INPUT_BUTTON_SIDE = 5,
375+ INPUT_BUTTON_EXTRA = 6,
376+ INPUT_BUTTON__MAX = 7,
377+ } InputButton;
378+ ```
379+
380+ SpiceInput调用InputCore的接口最终调入对应的设备模拟设备处理函数,这里假设是HID设备(hid_pointer_event)
381+ ``` c
382+ static void hid_pointer_event (DeviceState * dev, QemuConsole * src,
383+ InputEvent * evt)
384+ {
385+ static const int bmap[ INPUT_BUTTON__ MAX] = {
386+ [ INPUT_BUTTON_LEFT] = 0x01,
387+ [ INPUT_BUTTON_RIGHT] = 0x02,
388+ [ INPUT_BUTTON_MIDDLE] = 0x04,
389+ [ INPUT_BUTTON_SIDE] = 0x08,
390+ [ INPUT_BUTTON_EXTRA] = 0x10,
391+ };
392+
393+ ```
394+
395+ 还有一个需要注意的是hid report descriptor
396+ 其中下面的配置是同时上报8个按键
397+ ```c
398+ static const uint8_t qemu_tablet_hid_report_descriptor[] = {
399+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
400+ 0x09, 0x02, /* Usage (Mouse) */
401+ 0xa1, 0x01, /* Collection (Application) */
402+ 0x09, 0x01, /* Usage (Pointer) */
403+ 0xa1, 0x00, /* Collection (Physical) */
404+ 0x05, 0x09, /* Usage Page (Button) */
405+ 0x19, 0x01, /* Usage Minimum (1) */
406+ 0x29, 0x08, /* Usage Maximum (8) */
407+ 0x15, 0x00, /* Logical Minimum (0) */
408+ 0x25, 0x01, /* Logical Maximum (1) */
409+ 0x95, 0x08, /* Report Count (8) */
410+ 0x75, 0x01, /* Report Size (1) */
411+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
412+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
413+ 0x09, 0x30, /* Usage (X) */
414+ 0x09, 0x31, /* Usage (Y) */
415+ 0x15, 0x00, /* Logical Minimum (0) */
416+ 0x26, 0xff, 0x7f, /* Logical Maximum (0x7fff) */
417+ 0x35, 0x00, /* Physical Minimum (0) */
418+ 0x46, 0xff, 0x7f, /* Physical Maximum (0x7fff) */
419+ 0x75, 0x10, /* Report Size (16) */
420+ 0x95, 0x02, /* Report Count (2) */
421+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
422+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
423+ 0x09, 0x38, /* Usage (Wheel) */
424+ 0x15, 0x81, /* Logical Minimum (-0x7f) */
425+ 0x25, 0x7f, /* Logical Maximum (0x7f) */
426+ 0x35, 0x00, /* Physical Minimum (same as logical) */
427+ 0x45, 0x00, /* Physical Maximum (same as logical) */
428+ 0x75, 0x08, /* Report Size (8) */
429+ 0x95, 0x01, /* Report Count (1) */
430+ 0x81, 0x06, /* Input (Data, Variable, Relative) */
431+ 0xc0, /* End Collection */
432+ 0xc0, /* End Collection */
433+ };
434+
435+ /* HID描述符长度需要对应修改 */
436+ static const USBDescIface desc_iface_tablet = {
437+ ...
438+ /* HID descriptor */
439+ .data = (uint8_t[]) {
440+ 0x09, /* u8 bLength */
441+ USB_DT_HID, /* u8 bDescriptorType */
442+ 0x01, 0x00, /* u16 HID_class */
443+ 0x00, /* u8 country_code */
444+ 0x01, /* u8 num_descriptors */
445+ USB_DT_REPORT, /* u8 type: Report */
446+ sizeof(qemu_tablet_hid_report_descriptor) / sizeof(uint8_t), 0, /* u16 len */
447+ },
448+ ...
449+ ```
450+
451+ ### 调试输入数据
452+
453+ 确认SpiceInput输出的数据可以在InputCore中打开调试
454+
455+ (qemu) trace-event input_event_btn on
456+ or
457+ virsh qemu-monitor-command <domain> --hmp trace-event input_event_btn on
458+
459+ 确认模拟HID鼠标上报的数据,在下面函数中用gdb打印对应数据
460+
461+ USB数据包的处理函数usb_handle_packet调用流程如下
462+ ``` c
463+ usb_handle_packet
464+ usb_hid_handle_data
465+ hid_pointer_poll
466+
467+ int hid_pointer_poll (HIDState * hs, uint8_t * buf, int len)
468+ case HID_TABLET:
469+ if (len > l) {
470+ buf[ l++] = e->buttons_state;
471+ }
472+ if (len > l) {
473+ buf[ l++] = dx & 0xff;
474+ }
475+ if (len > l) {
476+ buf[ l++] = dx >> 8;
477+ }
478+ if (len > l) {
479+ buf[ l++] = dy & 0xff;
480+ }
481+ if (len > l) {
482+ buf[ l++] = dy >> 8;
483+ }
484+ if (len > l) {
485+ buf[ l++] = dz;
486+ }
487+ break;
488+
489+ (gdb) printf "0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x 0x%02x\n", buf[ 0] , buf[ 1] , buf[ 2] , buf[ 3] , buf[ 4] , buf[ 5]
490+ ```
491+ 其中使用USBlyzer抓到的raw data区域对应的数据为上面的buf
492+
304493## How to retrieve the report descriptors in linux
305494
306495[Ref: get usb report descriptor](https://www.slashdev.ca/2010/05/08/get-usb-report-descriptor-with-linux/)
0 commit comments