root / hw / bt-hid.c @ a6307b08
History | View | Annotate | Download (15.8 kB)
1 | 47e699dc | balrog | /*
|
---|---|---|---|
2 | 47e699dc | balrog | * QEMU Bluetooth HID Profile wrapper for USB HID.
|
3 | 47e699dc | balrog | *
|
4 | 47e699dc | balrog | * Copyright (C) 2007-2008 OpenMoko, Inc.
|
5 | 47e699dc | balrog | * Written by Andrzej Zaborowski <andrew@openedhand.com>
|
6 | 47e699dc | balrog | *
|
7 | 47e699dc | balrog | * This program is free software; you can redistribute it and/or
|
8 | 47e699dc | balrog | * modify it under the terms of the GNU General Public License as
|
9 | 47e699dc | balrog | * published by the Free Software Foundation; either version 2 or
|
10 | 47e699dc | balrog | * (at your option) version 3 of the License.
|
11 | 47e699dc | balrog | *
|
12 | 47e699dc | balrog | * This program is distributed in the hope that it will be useful,
|
13 | 47e699dc | balrog | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 | 47e699dc | balrog | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15 | 47e699dc | balrog | * GNU General Public License for more details.
|
16 | 47e699dc | balrog | *
|
17 | fad6cb1a | aurel32 | * You should have received a copy of the GNU General Public License along
|
18 | 8167ee88 | Blue Swirl | * with this program; if not, if not, see <http://www.gnu.org/licenses/>.
|
19 | 47e699dc | balrog | */
|
20 | 47e699dc | balrog | |
21 | 47e699dc | balrog | #include "qemu-common.h" |
22 | 47e699dc | balrog | #include "usb.h" |
23 | 47e699dc | balrog | #include "bt.h" |
24 | 47e699dc | balrog | |
25 | 47e699dc | balrog | enum hid_transaction_req {
|
26 | 47e699dc | balrog | BT_HANDSHAKE = 0x0,
|
27 | 47e699dc | balrog | BT_HID_CONTROL = 0x1,
|
28 | 47e699dc | balrog | BT_GET_REPORT = 0x4,
|
29 | 47e699dc | balrog | BT_SET_REPORT = 0x5,
|
30 | 47e699dc | balrog | BT_GET_PROTOCOL = 0x6,
|
31 | 47e699dc | balrog | BT_SET_PROTOCOL = 0x7,
|
32 | 47e699dc | balrog | BT_GET_IDLE = 0x8,
|
33 | 47e699dc | balrog | BT_SET_IDLE = 0x9,
|
34 | 47e699dc | balrog | BT_DATA = 0xa,
|
35 | 47e699dc | balrog | BT_DATC = 0xb,
|
36 | 47e699dc | balrog | }; |
37 | 47e699dc | balrog | |
38 | 47e699dc | balrog | enum hid_transaction_handshake {
|
39 | 47e699dc | balrog | BT_HS_SUCCESSFUL = 0x0,
|
40 | 47e699dc | balrog | BT_HS_NOT_READY = 0x1,
|
41 | 47e699dc | balrog | BT_HS_ERR_INVALID_REPORT_ID = 0x2,
|
42 | 47e699dc | balrog | BT_HS_ERR_UNSUPPORTED_REQUEST = 0x3,
|
43 | 47e699dc | balrog | BT_HS_ERR_INVALID_PARAMETER = 0x4,
|
44 | 47e699dc | balrog | BT_HS_ERR_UNKNOWN = 0xe,
|
45 | 47e699dc | balrog | BT_HS_ERR_FATAL = 0xf,
|
46 | 47e699dc | balrog | }; |
47 | 47e699dc | balrog | |
48 | 47e699dc | balrog | enum hid_transaction_control {
|
49 | 47e699dc | balrog | BT_HC_NOP = 0x0,
|
50 | 47e699dc | balrog | BT_HC_HARD_RESET = 0x1,
|
51 | 47e699dc | balrog | BT_HC_SOFT_RESET = 0x2,
|
52 | 47e699dc | balrog | BT_HC_SUSPEND = 0x3,
|
53 | 47e699dc | balrog | BT_HC_EXIT_SUSPEND = 0x4,
|
54 | 47e699dc | balrog | BT_HC_VIRTUAL_CABLE_UNPLUG = 0x5,
|
55 | 47e699dc | balrog | }; |
56 | 47e699dc | balrog | |
57 | 47e699dc | balrog | enum hid_protocol {
|
58 | 47e699dc | balrog | BT_HID_PROTO_BOOT = 0,
|
59 | 47e699dc | balrog | BT_HID_PROTO_REPORT = 1,
|
60 | 47e699dc | balrog | }; |
61 | 47e699dc | balrog | |
62 | 47e699dc | balrog | enum hid_boot_reportid {
|
63 | 47e699dc | balrog | BT_HID_BOOT_INVALID = 0,
|
64 | 47e699dc | balrog | BT_HID_BOOT_KEYBOARD, |
65 | 47e699dc | balrog | BT_HID_BOOT_MOUSE, |
66 | 47e699dc | balrog | }; |
67 | 47e699dc | balrog | |
68 | 47e699dc | balrog | enum hid_data_pkt {
|
69 | 47e699dc | balrog | BT_DATA_OTHER = 0,
|
70 | 47e699dc | balrog | BT_DATA_INPUT, |
71 | 47e699dc | balrog | BT_DATA_OUTPUT, |
72 | 47e699dc | balrog | BT_DATA_FEATURE, |
73 | 47e699dc | balrog | }; |
74 | 47e699dc | balrog | |
75 | 47e699dc | balrog | #define BT_HID_MTU 48 |
76 | 47e699dc | balrog | |
77 | 47e699dc | balrog | /* HID interface requests */
|
78 | 47e699dc | balrog | #define GET_REPORT 0xa101 |
79 | 47e699dc | balrog | #define GET_IDLE 0xa102 |
80 | 47e699dc | balrog | #define GET_PROTOCOL 0xa103 |
81 | 47e699dc | balrog | #define SET_REPORT 0x2109 |
82 | 47e699dc | balrog | #define SET_IDLE 0x210a |
83 | 47e699dc | balrog | #define SET_PROTOCOL 0x210b |
84 | 47e699dc | balrog | |
85 | 47e699dc | balrog | struct bt_hid_device_s {
|
86 | 47e699dc | balrog | struct bt_l2cap_device_s btdev;
|
87 | 47e699dc | balrog | struct bt_l2cap_conn_params_s *control;
|
88 | 47e699dc | balrog | struct bt_l2cap_conn_params_s *interrupt;
|
89 | 47e699dc | balrog | USBDevice *usbdev; |
90 | 47e699dc | balrog | |
91 | 47e699dc | balrog | int proto;
|
92 | 47e699dc | balrog | int connected;
|
93 | 47e699dc | balrog | int data_type;
|
94 | 47e699dc | balrog | int intr_state;
|
95 | 47e699dc | balrog | struct {
|
96 | 47e699dc | balrog | int len;
|
97 | 47e699dc | balrog | uint8_t buffer[1024];
|
98 | 47e699dc | balrog | } dataother, datain, dataout, feature, intrdataout; |
99 | 47e699dc | balrog | enum {
|
100 | 47e699dc | balrog | bt_state_ready, |
101 | 47e699dc | balrog | bt_state_transaction, |
102 | 47e699dc | balrog | bt_state_suspend, |
103 | 47e699dc | balrog | } state; |
104 | 47e699dc | balrog | }; |
105 | 47e699dc | balrog | |
106 | 47e699dc | balrog | static void bt_hid_reset(struct bt_hid_device_s *s) |
107 | 47e699dc | balrog | { |
108 | 47e699dc | balrog | struct bt_scatternet_s *net = s->btdev.device.net;
|
109 | 47e699dc | balrog | |
110 | 47e699dc | balrog | /* Go as far as... */
|
111 | 47e699dc | balrog | bt_l2cap_device_done(&s->btdev); |
112 | 47e699dc | balrog | bt_l2cap_device_init(&s->btdev, net); |
113 | 47e699dc | balrog | |
114 | 47e699dc | balrog | s->usbdev->handle_reset(s->usbdev); |
115 | 47e699dc | balrog | s->proto = BT_HID_PROTO_REPORT; |
116 | 47e699dc | balrog | s->state = bt_state_ready; |
117 | 47e699dc | balrog | s->dataother.len = 0;
|
118 | 47e699dc | balrog | s->datain.len = 0;
|
119 | 47e699dc | balrog | s->dataout.len = 0;
|
120 | 47e699dc | balrog | s->feature.len = 0;
|
121 | 47e699dc | balrog | s->intrdataout.len = 0;
|
122 | 47e699dc | balrog | s->intr_state = 0;
|
123 | 47e699dc | balrog | } |
124 | 47e699dc | balrog | |
125 | 47e699dc | balrog | static int bt_hid_out(struct bt_hid_device_s *s) |
126 | 47e699dc | balrog | { |
127 | 47e699dc | balrog | USBPacket p; |
128 | 47e699dc | balrog | |
129 | 47e699dc | balrog | if (s->data_type == BT_DATA_OUTPUT) {
|
130 | 47e699dc | balrog | p.pid = USB_TOKEN_OUT; |
131 | 47e699dc | balrog | p.devep = 1;
|
132 | 47e699dc | balrog | p.data = s->dataout.buffer; |
133 | 47e699dc | balrog | p.len = s->dataout.len; |
134 | 47e699dc | balrog | s->dataout.len = s->usbdev->handle_data(s->usbdev, &p); |
135 | 47e699dc | balrog | |
136 | 47e699dc | balrog | return s->dataout.len;
|
137 | 47e699dc | balrog | } |
138 | 47e699dc | balrog | |
139 | 47e699dc | balrog | if (s->data_type == BT_DATA_FEATURE) {
|
140 | 47e699dc | balrog | /* XXX:
|
141 | 47e699dc | balrog | * does this send a USB_REQ_CLEAR_FEATURE/USB_REQ_SET_FEATURE
|
142 | 47e699dc | balrog | * or a SET_REPORT? */
|
143 | 47e699dc | balrog | p.devep = 0;
|
144 | 47e699dc | balrog | } |
145 | 47e699dc | balrog | |
146 | 47e699dc | balrog | return -1; |
147 | 47e699dc | balrog | } |
148 | 47e699dc | balrog | |
149 | 47e699dc | balrog | static int bt_hid_in(struct bt_hid_device_s *s) |
150 | 47e699dc | balrog | { |
151 | 47e699dc | balrog | USBPacket p; |
152 | 47e699dc | balrog | |
153 | 47e699dc | balrog | p.pid = USB_TOKEN_IN; |
154 | 47e699dc | balrog | p.devep = 1;
|
155 | 47e699dc | balrog | p.data = s->datain.buffer; |
156 | 47e699dc | balrog | p.len = sizeof(s->datain.buffer);
|
157 | 47e699dc | balrog | s->datain.len = s->usbdev->handle_data(s->usbdev, &p); |
158 | 47e699dc | balrog | |
159 | 47e699dc | balrog | return s->datain.len;
|
160 | 47e699dc | balrog | } |
161 | 47e699dc | balrog | |
162 | 47e699dc | balrog | static void bt_hid_send_handshake(struct bt_hid_device_s *s, int result) |
163 | 47e699dc | balrog | { |
164 | 47e699dc | balrog | *s->control->sdu_out(s->control, 1) =
|
165 | 47e699dc | balrog | (BT_HANDSHAKE << 4) | result;
|
166 | 47e699dc | balrog | s->control->sdu_submit(s->control); |
167 | 47e699dc | balrog | } |
168 | 47e699dc | balrog | |
169 | 47e699dc | balrog | static void bt_hid_send_control(struct bt_hid_device_s *s, int operation) |
170 | 47e699dc | balrog | { |
171 | 47e699dc | balrog | *s->control->sdu_out(s->control, 1) =
|
172 | 47e699dc | balrog | (BT_HID_CONTROL << 4) | operation;
|
173 | 47e699dc | balrog | s->control->sdu_submit(s->control); |
174 | 47e699dc | balrog | } |
175 | 47e699dc | balrog | |
176 | 47e699dc | balrog | static void bt_hid_disconnect(struct bt_hid_device_s *s) |
177 | 47e699dc | balrog | { |
178 | 47e699dc | balrog | /* Disconnect s->control and s->interrupt */
|
179 | 47e699dc | balrog | } |
180 | 47e699dc | balrog | |
181 | 47e699dc | balrog | static void bt_hid_send_data(struct bt_l2cap_conn_params_s *ch, int type, |
182 | 47e699dc | balrog | const uint8_t *data, int len) |
183 | 47e699dc | balrog | { |
184 | 47e699dc | balrog | uint8_t *pkt, hdr = (BT_DATA << 4) | type;
|
185 | 47e699dc | balrog | int plen;
|
186 | 47e699dc | balrog | |
187 | 47e699dc | balrog | do {
|
188 | 47e699dc | balrog | plen = MIN(len, ch->remote_mtu - 1);
|
189 | 47e699dc | balrog | pkt = ch->sdu_out(ch, plen + 1);
|
190 | 47e699dc | balrog | |
191 | 47e699dc | balrog | pkt[0] = hdr;
|
192 | 47e699dc | balrog | if (plen)
|
193 | 47e699dc | balrog | memcpy(pkt + 1, data, plen);
|
194 | 47e699dc | balrog | ch->sdu_submit(ch); |
195 | 47e699dc | balrog | |
196 | 47e699dc | balrog | len -= plen; |
197 | 47e699dc | balrog | data += plen; |
198 | 47e699dc | balrog | hdr = (BT_DATC << 4) | type;
|
199 | 47e699dc | balrog | } while (plen == ch->remote_mtu - 1); |
200 | 47e699dc | balrog | } |
201 | 47e699dc | balrog | |
202 | 47e699dc | balrog | static void bt_hid_control_transaction(struct bt_hid_device_s *s, |
203 | 47e699dc | balrog | const uint8_t *data, int len) |
204 | 47e699dc | balrog | { |
205 | 47e699dc | balrog | uint8_t type, parameter; |
206 | 47e699dc | balrog | int rlen, ret = -1; |
207 | 47e699dc | balrog | if (len < 1) |
208 | 47e699dc | balrog | return;
|
209 | 47e699dc | balrog | |
210 | 47e699dc | balrog | type = data[0] >> 4; |
211 | 47e699dc | balrog | parameter = data[0] & 0xf; |
212 | 47e699dc | balrog | |
213 | 47e699dc | balrog | switch (type) {
|
214 | 47e699dc | balrog | case BT_HANDSHAKE:
|
215 | 47e699dc | balrog | case BT_DATA:
|
216 | 47e699dc | balrog | switch (parameter) {
|
217 | 47e699dc | balrog | default:
|
218 | 47e699dc | balrog | /* These are not expected to be sent this direction. */
|
219 | 47e699dc | balrog | ret = BT_HS_ERR_INVALID_PARAMETER; |
220 | 47e699dc | balrog | } |
221 | 47e699dc | balrog | break;
|
222 | 47e699dc | balrog | |
223 | 47e699dc | balrog | case BT_HID_CONTROL:
|
224 | 47e699dc | balrog | if (len != 1 || (parameter != BT_HC_VIRTUAL_CABLE_UNPLUG && |
225 | 47e699dc | balrog | s->state == bt_state_transaction)) { |
226 | 47e699dc | balrog | ret = BT_HS_ERR_INVALID_PARAMETER; |
227 | 47e699dc | balrog | break;
|
228 | 47e699dc | balrog | } |
229 | 47e699dc | balrog | switch (parameter) {
|
230 | 47e699dc | balrog | case BT_HC_NOP:
|
231 | 47e699dc | balrog | break;
|
232 | 47e699dc | balrog | case BT_HC_HARD_RESET:
|
233 | 47e699dc | balrog | case BT_HC_SOFT_RESET:
|
234 | 47e699dc | balrog | bt_hid_reset(s); |
235 | 47e699dc | balrog | break;
|
236 | 47e699dc | balrog | case BT_HC_SUSPEND:
|
237 | 47e699dc | balrog | if (s->state == bt_state_ready)
|
238 | 47e699dc | balrog | s->state = bt_state_suspend; |
239 | 47e699dc | balrog | else
|
240 | 47e699dc | balrog | ret = BT_HS_ERR_INVALID_PARAMETER; |
241 | 47e699dc | balrog | break;
|
242 | 47e699dc | balrog | case BT_HC_EXIT_SUSPEND:
|
243 | 47e699dc | balrog | if (s->state == bt_state_suspend)
|
244 | 47e699dc | balrog | s->state = bt_state_ready; |
245 | 47e699dc | balrog | else
|
246 | 47e699dc | balrog | ret = BT_HS_ERR_INVALID_PARAMETER; |
247 | 47e699dc | balrog | break;
|
248 | 47e699dc | balrog | case BT_HC_VIRTUAL_CABLE_UNPLUG:
|
249 | 47e699dc | balrog | bt_hid_disconnect(s); |
250 | 47e699dc | balrog | break;
|
251 | 47e699dc | balrog | default:
|
252 | 47e699dc | balrog | ret = BT_HS_ERR_INVALID_PARAMETER; |
253 | 47e699dc | balrog | } |
254 | 47e699dc | balrog | break;
|
255 | 47e699dc | balrog | |
256 | 47e699dc | balrog | case BT_GET_REPORT:
|
257 | 47e699dc | balrog | /* No ReportIDs declared. */
|
258 | 47e699dc | balrog | if (((parameter & 8) && len != 3) || |
259 | 47e699dc | balrog | (!(parameter & 8) && len != 1) || |
260 | 47e699dc | balrog | s->state != bt_state_ready) { |
261 | 47e699dc | balrog | ret = BT_HS_ERR_INVALID_PARAMETER; |
262 | 47e699dc | balrog | break;
|
263 | 47e699dc | balrog | } |
264 | 47e699dc | balrog | if (parameter & 8) |
265 | 47e699dc | balrog | rlen = data[2] | (data[3] << 8); |
266 | 47e699dc | balrog | else
|
267 | 47e699dc | balrog | rlen = INT_MAX; |
268 | 47e699dc | balrog | switch (parameter & 3) { |
269 | 47e699dc | balrog | case BT_DATA_OTHER:
|
270 | 47e699dc | balrog | ret = BT_HS_ERR_INVALID_PARAMETER; |
271 | 47e699dc | balrog | break;
|
272 | 47e699dc | balrog | case BT_DATA_INPUT:
|
273 | 47e699dc | balrog | /* Here we can as well poll s->usbdev */
|
274 | 47e699dc | balrog | bt_hid_send_data(s->control, BT_DATA_INPUT, |
275 | 47e699dc | balrog | s->datain.buffer, MIN(rlen, s->datain.len)); |
276 | 47e699dc | balrog | break;
|
277 | 47e699dc | balrog | case BT_DATA_OUTPUT:
|
278 | 47e699dc | balrog | bt_hid_send_data(s->control, BT_DATA_OUTPUT, |
279 | 47e699dc | balrog | s->dataout.buffer, MIN(rlen, s->dataout.len)); |
280 | 47e699dc | balrog | break;
|
281 | 47e699dc | balrog | case BT_DATA_FEATURE:
|
282 | 47e699dc | balrog | bt_hid_send_data(s->control, BT_DATA_FEATURE, |
283 | 47e699dc | balrog | s->feature.buffer, MIN(rlen, s->feature.len)); |
284 | 47e699dc | balrog | break;
|
285 | 47e699dc | balrog | } |
286 | 47e699dc | balrog | break;
|
287 | 47e699dc | balrog | |
288 | 47e699dc | balrog | case BT_SET_REPORT:
|
289 | 47e699dc | balrog | if (len < 2 || len > BT_HID_MTU || s->state != bt_state_ready || |
290 | 47e699dc | balrog | (parameter & 3) == BT_DATA_OTHER ||
|
291 | 47e699dc | balrog | (parameter & 3) == BT_DATA_INPUT) {
|
292 | 47e699dc | balrog | ret = BT_HS_ERR_INVALID_PARAMETER; |
293 | 47e699dc | balrog | break;
|
294 | 47e699dc | balrog | } |
295 | 47e699dc | balrog | s->data_type = parameter & 3;
|
296 | 47e699dc | balrog | if (s->data_type == BT_DATA_OUTPUT) {
|
297 | 47e699dc | balrog | s->dataout.len = len - 1;
|
298 | 47e699dc | balrog | memcpy(s->dataout.buffer, data + 1, s->dataout.len);
|
299 | 47e699dc | balrog | } else {
|
300 | 47e699dc | balrog | s->feature.len = len - 1;
|
301 | 47e699dc | balrog | memcpy(s->feature.buffer, data + 1, s->feature.len);
|
302 | 47e699dc | balrog | } |
303 | 47e699dc | balrog | if (len == BT_HID_MTU)
|
304 | 47e699dc | balrog | s->state = bt_state_transaction; |
305 | 47e699dc | balrog | else
|
306 | 47e699dc | balrog | bt_hid_out(s); |
307 | 47e699dc | balrog | break;
|
308 | 47e699dc | balrog | |
309 | 47e699dc | balrog | case BT_GET_PROTOCOL:
|
310 | 47e699dc | balrog | if (len != 1 || s->state == bt_state_transaction) { |
311 | 47e699dc | balrog | ret = BT_HS_ERR_INVALID_PARAMETER; |
312 | 47e699dc | balrog | break;
|
313 | 47e699dc | balrog | } |
314 | 47e699dc | balrog | *s->control->sdu_out(s->control, 1) = s->proto;
|
315 | 47e699dc | balrog | s->control->sdu_submit(s->control); |
316 | 47e699dc | balrog | break;
|
317 | 47e699dc | balrog | |
318 | 47e699dc | balrog | case BT_SET_PROTOCOL:
|
319 | 47e699dc | balrog | if (len != 1 || s->state == bt_state_transaction || |
320 | 47e699dc | balrog | (parameter != BT_HID_PROTO_BOOT && |
321 | 47e699dc | balrog | parameter != BT_HID_PROTO_REPORT)) { |
322 | 47e699dc | balrog | ret = BT_HS_ERR_INVALID_PARAMETER; |
323 | 47e699dc | balrog | break;
|
324 | 47e699dc | balrog | } |
325 | 47e699dc | balrog | s->proto = parameter; |
326 | 511d2b14 | blueswir1 | s->usbdev->handle_control(s->usbdev, SET_PROTOCOL, s->proto, 0, 0, |
327 | 511d2b14 | blueswir1 | NULL);
|
328 | 47e699dc | balrog | ret = BT_HS_SUCCESSFUL; |
329 | 47e699dc | balrog | break;
|
330 | 47e699dc | balrog | |
331 | 47e699dc | balrog | case BT_GET_IDLE:
|
332 | 47e699dc | balrog | if (len != 1 || s->state == bt_state_transaction) { |
333 | 47e699dc | balrog | ret = BT_HS_ERR_INVALID_PARAMETER; |
334 | 47e699dc | balrog | break;
|
335 | 47e699dc | balrog | } |
336 | 47e699dc | balrog | s->usbdev->handle_control(s->usbdev, GET_IDLE, 0, 0, 1, |
337 | 47e699dc | balrog | s->control->sdu_out(s->control, 1));
|
338 | 47e699dc | balrog | s->control->sdu_submit(s->control); |
339 | 47e699dc | balrog | break;
|
340 | 47e699dc | balrog | |
341 | 47e699dc | balrog | case BT_SET_IDLE:
|
342 | 47e699dc | balrog | if (len != 2 || s->state == bt_state_transaction) { |
343 | 47e699dc | balrog | ret = BT_HS_ERR_INVALID_PARAMETER; |
344 | 47e699dc | balrog | break;
|
345 | 47e699dc | balrog | } |
346 | 47e699dc | balrog | |
347 | 47e699dc | balrog | /* We don't need to know about the Idle Rate here really,
|
348 | 47e699dc | balrog | * so just pass it on to the device. */
|
349 | 47e699dc | balrog | ret = s->usbdev->handle_control(s->usbdev, |
350 | 511d2b14 | blueswir1 | SET_IDLE, data[1], 0, 0, NULL) ? |
351 | 47e699dc | balrog | BT_HS_SUCCESSFUL : BT_HS_ERR_INVALID_PARAMETER; |
352 | 47e699dc | balrog | /* XXX: Does this generate a handshake? */
|
353 | 47e699dc | balrog | break;
|
354 | 47e699dc | balrog | |
355 | 47e699dc | balrog | case BT_DATC:
|
356 | 47e699dc | balrog | if (len > BT_HID_MTU || s->state != bt_state_transaction) {
|
357 | 47e699dc | balrog | ret = BT_HS_ERR_INVALID_PARAMETER; |
358 | 47e699dc | balrog | break;
|
359 | 47e699dc | balrog | } |
360 | 47e699dc | balrog | if (s->data_type == BT_DATA_OUTPUT) {
|
361 | 47e699dc | balrog | memcpy(s->dataout.buffer + s->dataout.len, data + 1, len - 1); |
362 | 47e699dc | balrog | s->dataout.len += len - 1;
|
363 | 47e699dc | balrog | } else {
|
364 | 47e699dc | balrog | memcpy(s->feature.buffer + s->feature.len, data + 1, len - 1); |
365 | 47e699dc | balrog | s->feature.len += len - 1;
|
366 | 47e699dc | balrog | } |
367 | 47e699dc | balrog | if (len < BT_HID_MTU) {
|
368 | 47e699dc | balrog | bt_hid_out(s); |
369 | 47e699dc | balrog | s->state = bt_state_ready; |
370 | 47e699dc | balrog | } |
371 | 47e699dc | balrog | break;
|
372 | 47e699dc | balrog | |
373 | 47e699dc | balrog | default:
|
374 | 47e699dc | balrog | ret = BT_HS_ERR_UNSUPPORTED_REQUEST; |
375 | 47e699dc | balrog | } |
376 | 47e699dc | balrog | |
377 | 47e699dc | balrog | if (ret != -1) |
378 | 47e699dc | balrog | bt_hid_send_handshake(s, ret); |
379 | 47e699dc | balrog | } |
380 | 47e699dc | balrog | |
381 | 47e699dc | balrog | static void bt_hid_control_sdu(void *opaque, const uint8_t *data, int len) |
382 | 47e699dc | balrog | { |
383 | 47e699dc | balrog | struct bt_hid_device_s *hid = opaque;
|
384 | 47e699dc | balrog | |
385 | 7442511c | blueswir1 | bt_hid_control_transaction(hid, data, len); |
386 | 47e699dc | balrog | } |
387 | 47e699dc | balrog | |
388 | 47e699dc | balrog | static void bt_hid_datain(void *opaque) |
389 | 47e699dc | balrog | { |
390 | 47e699dc | balrog | struct bt_hid_device_s *hid = opaque;
|
391 | 47e699dc | balrog | |
392 | 47e699dc | balrog | /* If suspended, wake-up and send a wake-up event first. We might
|
393 | 47e699dc | balrog | * want to also inspect the input report and ignore event like
|
394 | 47e699dc | balrog | * mouse movements until a button event occurs. */
|
395 | 47e699dc | balrog | if (hid->state == bt_state_suspend) {
|
396 | 47e699dc | balrog | hid->state = bt_state_ready; |
397 | 47e699dc | balrog | } |
398 | 47e699dc | balrog | |
399 | 47e699dc | balrog | if (bt_hid_in(hid) > 0) |
400 | 47e699dc | balrog | /* TODO: when in boot-mode precede any Input reports with the ReportID
|
401 | 47e699dc | balrog | * byte, here and in GetReport/SetReport on the Control channel. */
|
402 | 47e699dc | balrog | bt_hid_send_data(hid->interrupt, BT_DATA_INPUT, |
403 | 47e699dc | balrog | hid->datain.buffer, hid->datain.len); |
404 | 47e699dc | balrog | } |
405 | 47e699dc | balrog | |
406 | 47e699dc | balrog | static void bt_hid_interrupt_sdu(void *opaque, const uint8_t *data, int len) |
407 | 47e699dc | balrog | { |
408 | 47e699dc | balrog | struct bt_hid_device_s *hid = opaque;
|
409 | 47e699dc | balrog | |
410 | 47e699dc | balrog | if (len > BT_HID_MTU || len < 1) |
411 | 47e699dc | balrog | goto bad;
|
412 | 47e699dc | balrog | if ((data[0] & 3) != BT_DATA_OUTPUT) |
413 | 47e699dc | balrog | goto bad;
|
414 | 47e699dc | balrog | if ((data[0] >> 4) == BT_DATA) { |
415 | 47e699dc | balrog | if (hid->intr_state)
|
416 | 47e699dc | balrog | goto bad;
|
417 | 47e699dc | balrog | |
418 | 47e699dc | balrog | hid->data_type = BT_DATA_OUTPUT; |
419 | 47e699dc | balrog | hid->intrdataout.len = 0;
|
420 | 47e699dc | balrog | } else if ((data[0] >> 4) == BT_DATC) { |
421 | 47e699dc | balrog | if (!hid->intr_state)
|
422 | 47e699dc | balrog | goto bad;
|
423 | 47e699dc | balrog | } else
|
424 | 47e699dc | balrog | goto bad;
|
425 | 47e699dc | balrog | |
426 | 47e699dc | balrog | memcpy(hid->intrdataout.buffer + hid->intrdataout.len, data + 1, len - 1); |
427 | 47e699dc | balrog | hid->intrdataout.len += len - 1;
|
428 | 47e699dc | balrog | hid->intr_state = (len == BT_HID_MTU); |
429 | 47e699dc | balrog | if (!hid->intr_state) {
|
430 | 47e699dc | balrog | memcpy(hid->dataout.buffer, hid->intrdataout.buffer, |
431 | 47e699dc | balrog | hid->dataout.len = hid->intrdataout.len); |
432 | 47e699dc | balrog | bt_hid_out(hid); |
433 | 47e699dc | balrog | } |
434 | 47e699dc | balrog | |
435 | 47e699dc | balrog | return;
|
436 | 47e699dc | balrog | bad:
|
437 | 47e699dc | balrog | fprintf(stderr, "%s: bad transaction on Interrupt channel.\n",
|
438 | 47e699dc | balrog | __FUNCTION__); |
439 | 47e699dc | balrog | } |
440 | 47e699dc | balrog | |
441 | 47e699dc | balrog | /* "Virtual cable" plug/unplug event. */
|
442 | 47e699dc | balrog | static void bt_hid_connected_update(struct bt_hid_device_s *hid) |
443 | 47e699dc | balrog | { |
444 | 47e699dc | balrog | int prev = hid->connected;
|
445 | 47e699dc | balrog | |
446 | 47e699dc | balrog | hid->connected = hid->control && hid->interrupt; |
447 | 47e699dc | balrog | |
448 | 47e699dc | balrog | /* Stop page-/inquiry-scanning when a host is connected. */
|
449 | 47e699dc | balrog | hid->btdev.device.page_scan = !hid->connected; |
450 | 47e699dc | balrog | hid->btdev.device.inquiry_scan = !hid->connected; |
451 | 47e699dc | balrog | |
452 | 47e699dc | balrog | if (hid->connected && !prev) {
|
453 | 47e699dc | balrog | hid->usbdev->handle_reset(hid->usbdev); |
454 | 47e699dc | balrog | hid->proto = BT_HID_PROTO_REPORT; |
455 | 47e699dc | balrog | } |
456 | 47e699dc | balrog | |
457 | 47e699dc | balrog | /* Should set HIDVirtualCable in SDP (possibly need to check that SDP
|
458 | 47e699dc | balrog | * isn't destroyed yet, in case we're being called from handle_destroy) */
|
459 | 47e699dc | balrog | } |
460 | 47e699dc | balrog | |
461 | 47e699dc | balrog | static void bt_hid_close_control(void *opaque) |
462 | 47e699dc | balrog | { |
463 | 47e699dc | balrog | struct bt_hid_device_s *hid = opaque;
|
464 | 47e699dc | balrog | |
465 | 511d2b14 | blueswir1 | hid->control = NULL;
|
466 | 47e699dc | balrog | bt_hid_connected_update(hid); |
467 | 47e699dc | balrog | } |
468 | 47e699dc | balrog | |
469 | 47e699dc | balrog | static void bt_hid_close_interrupt(void *opaque) |
470 | 47e699dc | balrog | { |
471 | 47e699dc | balrog | struct bt_hid_device_s *hid = opaque;
|
472 | 47e699dc | balrog | |
473 | 511d2b14 | blueswir1 | hid->interrupt = NULL;
|
474 | 47e699dc | balrog | bt_hid_connected_update(hid); |
475 | 47e699dc | balrog | } |
476 | 47e699dc | balrog | |
477 | 47e699dc | balrog | static int bt_hid_new_control_ch(struct bt_l2cap_device_s *dev, |
478 | 47e699dc | balrog | struct bt_l2cap_conn_params_s *params)
|
479 | 47e699dc | balrog | { |
480 | 47e699dc | balrog | struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev; |
481 | 47e699dc | balrog | |
482 | 47e699dc | balrog | if (hid->control)
|
483 | 47e699dc | balrog | return 1; |
484 | 47e699dc | balrog | |
485 | 47e699dc | balrog | hid->control = params; |
486 | 47e699dc | balrog | hid->control->opaque = hid; |
487 | 47e699dc | balrog | hid->control->close = bt_hid_close_control; |
488 | 47e699dc | balrog | hid->control->sdu_in = bt_hid_control_sdu; |
489 | 47e699dc | balrog | |
490 | 47e699dc | balrog | bt_hid_connected_update(hid); |
491 | 47e699dc | balrog | |
492 | 47e699dc | balrog | return 0; |
493 | 47e699dc | balrog | } |
494 | 47e699dc | balrog | |
495 | 47e699dc | balrog | static int bt_hid_new_interrupt_ch(struct bt_l2cap_device_s *dev, |
496 | 47e699dc | balrog | struct bt_l2cap_conn_params_s *params)
|
497 | 47e699dc | balrog | { |
498 | 47e699dc | balrog | struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev; |
499 | 47e699dc | balrog | |
500 | 47e699dc | balrog | if (hid->interrupt)
|
501 | 47e699dc | balrog | return 1; |
502 | 47e699dc | balrog | |
503 | 47e699dc | balrog | hid->interrupt = params; |
504 | 47e699dc | balrog | hid->interrupt->opaque = hid; |
505 | 47e699dc | balrog | hid->interrupt->close = bt_hid_close_interrupt; |
506 | 47e699dc | balrog | hid->interrupt->sdu_in = bt_hid_interrupt_sdu; |
507 | 47e699dc | balrog | |
508 | 47e699dc | balrog | bt_hid_connected_update(hid); |
509 | 47e699dc | balrog | |
510 | 47e699dc | balrog | return 0; |
511 | 47e699dc | balrog | } |
512 | 47e699dc | balrog | |
513 | 47e699dc | balrog | static void bt_hid_destroy(struct bt_device_s *dev) |
514 | 47e699dc | balrog | { |
515 | 47e699dc | balrog | struct bt_hid_device_s *hid = (struct bt_hid_device_s *) dev; |
516 | 47e699dc | balrog | |
517 | 47e699dc | balrog | if (hid->connected)
|
518 | 47e699dc | balrog | bt_hid_send_control(hid, BT_HC_VIRTUAL_CABLE_UNPLUG); |
519 | 47e699dc | balrog | bt_l2cap_device_done(&hid->btdev); |
520 | 47e699dc | balrog | |
521 | 47e699dc | balrog | hid->usbdev->handle_destroy(hid->usbdev); |
522 | 47e699dc | balrog | |
523 | 47e699dc | balrog | qemu_free(hid); |
524 | 47e699dc | balrog | } |
525 | 47e699dc | balrog | |
526 | 47e699dc | balrog | enum peripheral_minor_class {
|
527 | 47e699dc | balrog | class_other = 0 << 4, |
528 | 47e699dc | balrog | class_keyboard = 1 << 4, |
529 | 47e699dc | balrog | class_pointing = 2 << 4, |
530 | 47e699dc | balrog | class_combo = 3 << 4, |
531 | 47e699dc | balrog | }; |
532 | 47e699dc | balrog | |
533 | 47e699dc | balrog | static struct bt_device_s *bt_hid_init(struct bt_scatternet_s *net, |
534 | 47e699dc | balrog | USBDevice *dev, enum peripheral_minor_class minor)
|
535 | 47e699dc | balrog | { |
536 | 47e699dc | balrog | struct bt_hid_device_s *s = qemu_mallocz(sizeof(*s)); |
537 | 47e699dc | balrog | uint32_t class = |
538 | 47e699dc | balrog | /* Format type */
|
539 | 47e699dc | balrog | (0 << 0) | |
540 | 47e699dc | balrog | /* Device class */
|
541 | 47e699dc | balrog | (minor << 2) |
|
542 | 47e699dc | balrog | (5 << 8) | /* "Peripheral" */ |
543 | 47e699dc | balrog | /* Service classes */
|
544 | 47e699dc | balrog | (1 << 13) | /* Limited discoverable mode */ |
545 | 47e699dc | balrog | (1 << 19); /* Capturing device (?) */ |
546 | 47e699dc | balrog | |
547 | 47e699dc | balrog | bt_l2cap_device_init(&s->btdev, net); |
548 | 47e699dc | balrog | bt_l2cap_sdp_init(&s->btdev); |
549 | 47e699dc | balrog | bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_CTRL, |
550 | 47e699dc | balrog | BT_HID_MTU, bt_hid_new_control_ch); |
551 | 47e699dc | balrog | bt_l2cap_psm_register(&s->btdev, BT_PSM_HID_INTR, |
552 | 47e699dc | balrog | BT_HID_MTU, bt_hid_new_interrupt_ch); |
553 | 47e699dc | balrog | |
554 | 47e699dc | balrog | s->usbdev = dev; |
555 | 47e699dc | balrog | s->btdev.device.lmp_name = s->usbdev->devname; |
556 | 47e699dc | balrog | usb_hid_datain_cb(s->usbdev, s, bt_hid_datain); |
557 | 47e699dc | balrog | |
558 | 47e699dc | balrog | s->btdev.device.handle_destroy = bt_hid_destroy; |
559 | 47e699dc | balrog | |
560 | 47e699dc | balrog | s->btdev.device.class[0] = (class >> 0) & 0xff; |
561 | 47e699dc | balrog | s->btdev.device.class[1] = (class >> 8) & 0xff; |
562 | 47e699dc | balrog | s->btdev.device.class[2] = (class >> 16) & 0xff; |
563 | 47e699dc | balrog | |
564 | 47e699dc | balrog | return &s->btdev.device;
|
565 | 47e699dc | balrog | } |
566 | 47e699dc | balrog | |
567 | 47e699dc | balrog | struct bt_device_s *bt_keyboard_init(struct bt_scatternet_s *net) |
568 | 47e699dc | balrog | { |
569 | 47e699dc | balrog | return bt_hid_init(net, usb_keyboard_init(), class_keyboard);
|
570 | 47e699dc | balrog | } |