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