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