Skip to content

Commit ea15d2f

Browse files
committed
USB mouse dual report id, relative and absolute mouse
1 parent 5cca17a commit ea15d2f

File tree

4 files changed

+302
-0
lines changed

4 files changed

+302
-0
lines changed

debug/usb/README.md

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
# USB HID Class
2+
3+
[参考文章 usb hid report descriptors](https://eleccelerator.com/tutorial-about-usb-hid-report-descriptors/)
4+
5+
[Device Class Definition for Human Interface Devices(HID)](hid1_11.pdf)
6+
7+
hid report descriptors在usb描述符中的关系
8+
9+
![report descriptors](./reportid.png)
10+
11+
下面是一个三个按键的标准鼠标
12+
13+
![3 buttons mouse](./mouse.png)
14+
15+
在代码中用数据结构表示如下
16+
17+
struct mouse_report_t
18+
{
19+
uint8_t buttons;
20+
int8_t x;
21+
int8_t y;
22+
}
23+
24+
在hid report descriptors中3个按键描述如下
25+
26+
USAGE_PAGE (Button)
27+
USAGE_MINIMUM (Button 1)
28+
USAGE_MAXIMUM (Button 3)
29+
30+
每个按键用两个值来表示(0和1)
31+
32+
LOGICAL MINIMUM (0)
33+
LOGICAL MAXIMUM (1)
34+
35+
总共有3个按键
36+
37+
REPORT_COUNT (3) # 每个按键时单独上报,需要上报3次按键信息
38+
REPORT_SIZE (1) # 每次上报的大小为1bit
39+
40+
上报数据描述
41+
42+
INPUT (Data, Var, Abs)
43+
44+
所以按键的所有描述合起来就是如下
45+
46+
USAGE_PAGE (Button)
47+
USAGE_MINIMUM (Button 1)
48+
USAGE_MAXIMUM (Button 3)
49+
LOGICAL_MINIMUM (0)
50+
LOGICAL_MAXIMUM (1)
51+
REPORT_COUNT (3)
52+
REPORT_SIZE (1)
53+
INPUT (Data,Var,Abs)
54+
55+
由于上报数据需要按照8bit对齐,所以剩余的未使用的5bit数据也需要描述
56+
57+
REPORT_COUNT (1) # 只需要上报一次
58+
REPORT_SIZE (5) # 一次性上报5bit数据
59+
INPUT (Cnst,Var,Abs)
60+
61+
x轴的数据上报(y轴同理)
62+
63+
USAGE_PAGE (Generic Desktop)
64+
USAGE (X)
65+
LOGICAL_MINIMUM (-127)
66+
LOGICAL_MAXIMUM (127)
67+
REPORT_SIZE (8) # 一次上报8bit数据
68+
REPORT_COUNT (1) # 上报一次
69+
INPUT (Data,Var,Rel)
70+
71+
可以将xy轴的数据合并起来表示
72+
73+
USAGE_PAGE (Generic Desktop)
74+
USAGE (X)
75+
USAGE (Y)
76+
LOGICAL_MINIMUM (-127)
77+
LOGICAL_MAXIMUM (127)
78+
REPORT_SIZE (8) # 每次上报8bit数据
79+
REPORT_COUNT (2) # 需要上报2次
80+
INPUT (Data,Var,Rel)
81+
82+
按键合xy轴合起来如下
83+
84+
USAGE_PAGE (Button)
85+
USAGE_MINIMUM (Button 1)
86+
USAGE_MAXIMUM (Button 3)
87+
LOGICAL_MINIMUM (0)
88+
LOGICAL_MAXIMUM (1)
89+
REPORT_COUNT (3)
90+
REPORT_SIZE (1)
91+
INPUT (Data,Var,Abs)
92+
REPORT_COUNT (1)
93+
REPORT_SIZE (5)
94+
INPUT (Cnst,Var,Abs)
95+
USAGE_PAGE (Generic Desktop)
96+
USAGE (X)
97+
USAGE (Y)
98+
LOGICAL_MINIMUM (-127)
99+
LOGICAL_MAXIMUM (127)
100+
REPORT_SIZE (8)
101+
REPORT_COUNT (2)
102+
INPUT (Data,Var,Rel)
103+
104+
还需要添加一些描述信息来表明是鼠标
105+
106+
USAGE_PAGE (Generic Desktop)
107+
USAGE (Mouse)
108+
COLLECTION (Application)
109+
USAGE (Pointer)
110+
COLLECTION (Physical)
111+
112+
... # 将之前的信息填在这里
113+
114+
END COLLECTION
115+
END COLLECTION
116+
117+
所以代码中就出现了如下代码(如下代码来源qemu中的dev-hid.c)
118+
119+
static const uint8_t qemu_mouse_hid_report_descriptor[] = {
120+
0x05, 0x01, /* Usage Page (Generic Desktop) */
121+
0x09, 0x02, /* Usage (Mouse) */
122+
0xa1, 0x01, /* Collection (Application) */
123+
0x09, 0x01, /* Usage (Pointer) */
124+
0xa1, 0x00, /* Collection (Physical) */
125+
0x05, 0x09, /* Usage Page (Button) */
126+
0x19, 0x01, /* Usage Minimum (1) */
127+
0x29, 0x03, /* Usage Maximum (3) */
128+
0x15, 0x00, /* Logical Minimum (0) */
129+
0x25, 0x01, /* Logical Maximum (1) */
130+
0x95, 0x03, /* Report Count (3) */
131+
0x75, 0x01, /* Report Size (1) */
132+
0x81, 0x02, /* Input (Data, Variable, Absolute) */
133+
0x95, 0x01, /* Report Count (1) */
134+
0x75, 0x05, /* Report Size (5) */
135+
0x81, 0x01, /* Input (Constant) */
136+
0x05, 0x01, /* Usage Page (Generic Desktop) */
137+
0x09, 0x30, /* Usage (X) */
138+
0x09, 0x31, /* Usage (Y) */
139+
0x09, 0x38, /* Usage (Wheel) */
140+
0x15, 0x81, /* Logical Minimum (-0x7f) */
141+
0x25, 0x7f, /* Logical Maximum (0x7f) */
142+
0x75, 0x08, /* Report Size (8) */
143+
0x95, 0x03, /* Report Count (3) */
144+
0x81, 0x06, /* Input (Data, Variable, Relative) */
145+
0xc0, /* End Collection */
146+
0xc0, /* End Collection */
147+
}
148+
149+
qemu代码里描述上述鼠标按键的结构时HIDPointerEvent
150+
151+
typedef struct HIDPointerEvent {
152+
int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */
153+
int32_t dz, buttons_state;
154+
} HIDPointerEvent;
155+
156+
能否让一个鼠标同时支持绝对和相对坐标(通过配置两个report id)
157+
158+
需要在上报的第一个byte里填充report id就能达到效果
159+
160+
typedef struct HIDPointerEvent {
161+
int8_t report_id;
162+
int32_t xdx, ydy; /* relative iff it's a mouse, otherwise absolute */
163+
int32_t dz, buttons_state;
164+
} HIDPointerEvent;
165+
166+
在report descriptors中添加report id的描述
167+
168+
static const uint8_t qemu_mouse_hid_report_descriptor[] = {
169+
0x05, 0x01, /* Usage Page (Generic Desktop) */
170+
0x09, 0x02, /* Usage (Mouse) */
171+
0xa1, 0x01, /* Collection (Application) */
172+
0x85, 0x01, /* report id 1 */
173+
0x09, 0x01, /* Usage (Pointer) */
174+
0xa1, 0x00, /* Collection (Physical) */
175+
0x05, 0x09, /* Usage Page (Button) */
176+
0x19, 0x01, /* Usage Minimum (1) */
177+
0x29, 0x03, /* Usage Maximum (3) */
178+
0x15, 0x00, /* Logical Minimum (0) */
179+
0x25, 0x01, /* Logical Maximum (1) */
180+
0x95, 0x03, /* Report Count (3) */
181+
0x75, 0x01, /* Report Size (1) */
182+
0x81, 0x02, /* Input (Data, Variable, Absolute) */
183+
0x95, 0x01, /* Report Count (1) */
184+
0x75, 0x05, /* Report Size (5) */
185+
0x81, 0x01, /* Input (Constant) */
186+
0x05, 0x01, /* Usage Page (Generic Desktop) */
187+
0x09, 0x30, /* Usage (X) */
188+
0x09, 0x31, /* Usage (Y) */
189+
0x09, 0x38, /* Usage (Wheel) */
190+
0x15, 0x81, /* Logical Minimum (-0x7f) */
191+
0x25, 0x7f, /* Logical Maximum (0x7f) */
192+
0x75, 0x08, /* Report Size (8) */
193+
0x95, 0x03, /* Report Count (3) */
194+
0x81, 0x06, /* Input (Data, Variable, Relative) */
195+
0xc0, /* End Collection */
196+
0xc0, /* End Collection */
197+
}
198+
199+
![report id format](./rid.png)
200+
201+
其中格式解读如下(report id可以用一个或多个byte来表示)
202+
203+
report_tag(1000 01nn) + nbyte_report_id
204+
205+
如果用一个byte表示,则nn = 01, report id tag就是 1000 0101 ==> 0x85
206+
207+
则表示report id 1结果如下
208+
209+
0x85, 0x01, /* report id 1 */
210+
211+
上面的修改只是修改来report descriptors上报数据的格式需要上报report id
212+
,在实际上报的usb数据里也需要修改能上报report id,修改如下
213+
214+
鼠标坐标的处理函数是(hid_pointer_event)
215+
216+
在相对坐标和绝对坐标中分别上报不同的report id(需要和report descriptors一致,将tablet的直接全部拷贝即可)
217+
218+
hid_pointer_event
219+
case INPUT_EVENT_KIND_REL:
220+
e->report_id = 1;
221+
case INPUT_EVENT_KIND_ABS:
222+
e->report_id = 2;
223+
224+
在usb写数据到端点时,在最前面的第一个byte填充report id
225+
226+
hid_pointer_poll
227+
case HID_MOUSE:
228+
if (len > l) {
229+
buf[l++] = e->report_id;
230+
}
231+
232+
相对鼠标和绝对鼠标report descriptors
233+
234+
static const uint8_t qemu_mouse_hid_report_descriptor[] = {
235+
0x05, 0x01, /* Usage Page (Generic Desktop) */
236+
0x09, 0x02, /* Usage (Mouse) */
237+
0xa1, 0x01, /* Collection (Application) */
238+
0x85, 0x01, /* report id 1 */
239+
0x09, 0x01, /* Usage (Pointer) */
240+
0xa1, 0x00, /* Collection (Physical) */
241+
0x05, 0x09, /* Usage Page (Button) */
242+
0x19, 0x01, /* Usage Minimum (1) */
243+
0x29, 0x03, /* Usage Maximum (3) */
244+
0x15, 0x00, /* Logical Minimum (0) */
245+
0x25, 0x01, /* Logical Maximum (1) */
246+
0x95, 0x03, /* Report Count (3) */
247+
0x75, 0x01, /* Report Size (1) */
248+
0x81, 0x02, /* Input (Data, Variable, Absolute) */
249+
0x95, 0x01, /* Report Count (1) */
250+
0x75, 0x05, /* Report Size (5) */
251+
0x81, 0x01, /* Input (Constant) */
252+
0x05, 0x01, /* Usage Page (Generic Desktop) */
253+
0x09, 0x30, /* Usage (X) */
254+
0x09, 0x31, /* Usage (Y) */
255+
0x09, 0x38, /* Usage (Wheel) */
256+
0x15, 0x81, /* Logical Minimum (-0x7f) */
257+
0x25, 0x7f, /* Logical Maximum (0x7f) */
258+
0x75, 0x08, /* Report Size (8) */
259+
0x95, 0x03, /* Report Count (3) */
260+
0x81, 0x06, /* Input (Data, Variable, Relative) */
261+
0xc0, /* End Collection */
262+
0xc0, /* End Collection */
263+
264+
0x05, 0x01, /* Usage Page (Generic Desktop) */
265+
0x09, 0x02, /* Usage (Mouse) */
266+
0xa1, 0x01, /* Collection (Application) */
267+
0x85, 0x02, /* report id 2 */
268+
0x09, 0x01, /* Usage (Pointer) */
269+
0xa1, 0x00, /* Collection (Physical) */
270+
0x05, 0x09, /* Usage Page (Button) */
271+
0x19, 0x01, /* Usage Minimum (1) */
272+
0x29, 0x03, /* Usage Maximum (3) */
273+
0x15, 0x00, /* Logical Minimum (0) */
274+
0x25, 0x01, /* Logical Maximum (1) */
275+
0x95, 0x03, /* Report Count (3) */
276+
0x75, 0x01, /* Report Size (1) */
277+
0x81, 0x02, /* Input (Data, Variable, Absolute) */
278+
0x95, 0x01, /* Report Count (1) */
279+
0x75, 0x05, /* Report Size (5) */
280+
0x81, 0x01, /* Input (Constant) */
281+
0x05, 0x01, /* Usage Page (Generic Desktop) */
282+
0x09, 0x30, /* Usage (X) */
283+
0x09, 0x31, /* Usage (Y) */
284+
0x15, 0x00, /* Logical Minimum (0) */
285+
0x26, 0xff, 0x7f, /* Logical Maximum (0x7fff) */
286+
0x35, 0x00, /* Physical Minimum (0) */
287+
0x46, 0xff, 0x7f, /* Physical Maximum (0x7fff) */
288+
0x75, 0x10, /* Report Size (16) */
289+
0x95, 0x02, /* Report Count (2) */
290+
0x81, 0x02, /* Input (Data, Variable, Absolute) */
291+
0x05, 0x01, /* Usage Page (Generic Desktop) */
292+
0x09, 0x38, /* Usage (Wheel) */
293+
0x15, 0x81, /* Logical Minimum (-0x7f) */
294+
0x25, 0x7f, /* Logical Maximum (0x7f) */
295+
0x35, 0x00, /* Physical Minimum (same as logical) */
296+
0x45, 0x00, /* Physical Maximum (same as logical) */
297+
0x75, 0x08, /* Report Size (8) */
298+
0x95, 0x01, /* Report Count (1) */
299+
0x81, 0x06, /* Input (Data, Variable, Relative) */
300+
0xc0, /* End Collection */
301+
0xc0, /* End Collection */
302+
};

debug/usb/mouse.png

13 KB
Loading

debug/usb/reportid.png

36 KB
Loading

debug/usb/rid.png

127 KB
Loading

0 commit comments

Comments
 (0)