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