1/******************************************************************************
2 *
3 *  Copyright (C) 2002-2012 Broadcom Corporation
4 *
5 *  Licensed under the Apache License, Version 2.0 (the "License");
6 *  you may not use this file except in compliance with the License.
7 *  You may obtain a copy of the License at:
8 *
9 *  http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 *
17 ******************************************************************************/
18
19/******************************************************************************
20 *
21 *  This file contains the HID HOST API entry points
22 *
23 ******************************************************************************/
24
25#include <stdlib.h>
26#include <string.h>
27#include <stdio.h>
28
29#include "gki.h"
30#include "bt_types.h"
31#include "hiddefs.h"
32#include "hidh_api.h"
33#include "hidh_int.h"
34#include "btm_api.h"
35#include "btu.h"
36
37#if HID_DYNAMIC_MEMORY == FALSE
38tHID_HOST_CTB   hh_cb;
39#endif
40
41static void hidh_search_callback (UINT16 sdp_result);
42
43/*******************************************************************************
44**
45** Function         HID_HostGetSDPRecord
46**
47** Description      This function reads the device SDP record
48**
49** Returns          tHID_STATUS
50**
51*******************************************************************************/
52tHID_STATUS HID_HostGetSDPRecord ( BD_ADDR addr, tSDP_DISCOVERY_DB *p_db, UINT32 db_len,
53                                   tHID_HOST_SDP_CALLBACK *sdp_cback )
54{
55    tSDP_UUID   uuid_list;
56
57    if( hh_cb.sdp_busy )
58        return HID_ERR_SDP_BUSY;
59
60    uuid_list.len = 2;
61    uuid_list.uu.uuid16 = UUID_SERVCLASS_HUMAN_INTERFACE;
62
63    hh_cb.p_sdp_db = p_db;
64    SDP_InitDiscoveryDb (p_db, db_len, 1, &uuid_list, 0, NULL);
65
66    if (SDP_ServiceSearchRequest (addr, p_db, hidh_search_callback))
67    {
68        hh_cb.sdp_cback = sdp_cback ;
69        hh_cb.sdp_busy = TRUE;
70        return HID_SUCCESS;
71    }
72    else
73        return HID_ERR_NO_RESOURCES;
74}
75
76void hidh_get_str_attr( tSDP_DISC_REC *p_rec, UINT16 attr_id, UINT16 max_len, char *str )
77{
78    tSDP_DISC_ATTR          *p_attr;
79    UINT16                  name_len;
80
81    if ((p_attr = SDP_FindAttributeInRec(p_rec, attr_id)) != NULL)
82    {
83        if((name_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type)) < max_len )
84        {
85            memcpy( str, (char *) p_attr->attr_value.v.array, name_len );
86            str[name_len] = '\0';
87        }
88        else
89        {
90            memcpy( str, (char *) p_attr->attr_value.v.array, max_len-1 );
91            str[max_len-1] = '\0';
92        }
93    }
94    else
95        str[0] = '\0';
96}
97
98
99static void hidh_search_callback (UINT16 sdp_result)
100{
101    tSDP_DISCOVERY_DB       *p_db = hh_cb.p_sdp_db;
102    tSDP_DISC_REC           *p_rec;
103    tSDP_DISC_ATTR          *p_attr, *p_subattr1, *p_subattr2, *p_repdesc;
104    tBT_UUID                hid_uuid;
105    tHID_DEV_SDP_INFO       *p_nvi = &hh_cb.sdp_rec;
106    UINT16                  attr_mask = 0;
107
108    hid_uuid.len       = LEN_UUID_16;
109    hid_uuid.uu.uuid16 = UUID_SERVCLASS_HUMAN_INTERFACE;
110
111    hh_cb.sdp_busy = FALSE;
112
113    if (sdp_result != SDP_SUCCESS)
114    {
115        hh_cb.sdp_cback(sdp_result, 0, NULL);
116        return;
117    }
118
119    if ((p_rec = SDP_FindServiceUUIDInDb (p_db, &hid_uuid, NULL)) == NULL)
120    {
121        hh_cb.sdp_cback(HID_SDP_NO_SERV_UUID, 0, NULL);
122        return;
123    }
124
125    memset (&hh_cb.sdp_rec, 0, sizeof( tHID_DEV_SDP_INFO ));
126
127    /* First, verify the mandatory fields we care about */
128    if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_DESCRIPTOR_LIST)) == NULL)
129     || (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE)
130     || ((p_subattr1 = p_attr->attr_value.v.p_sub_attr) == NULL)
131     || (SDP_DISC_ATTR_TYPE(p_subattr1->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE)
132     || ((p_subattr2 = p_subattr1->attr_value.v.p_sub_attr) == NULL)
133     || ((p_repdesc = p_subattr2->p_next_attr) == NULL)
134     || (SDP_DISC_ATTR_TYPE(p_repdesc->attr_len_type) != TEXT_STR_DESC_TYPE))
135    {
136        hh_cb.sdp_cback(HID_SDP_MANDATORY_MISSING, 0, NULL);
137        return;
138    }
139
140    if ((p_nvi->dscp_info.dl_len = SDP_DISC_ATTR_LEN(p_repdesc->attr_len_type)) != 0)
141        p_nvi->dscp_info.dsc_list = (UINT8 *) &p_repdesc->attr_value;
142
143    if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_VIRTUAL_CABLE)) != NULL) &&
144        (p_attr->attr_value.v.u8) )
145    {
146        attr_mask |= HID_VIRTUAL_CABLE;
147    }
148
149    if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_RECONNECT_INITIATE)) != NULL) &&
150        (p_attr->attr_value.v.u8) )
151    {
152        attr_mask |= HID_RECONN_INIT;
153    }
154
155    if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_NORMALLY_CONNECTABLE)) != NULL) &&
156        (p_attr->attr_value.v.u8) )
157    {
158        attr_mask |= HID_NORMALLY_CONNECTABLE;
159    }
160
161    if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_SDP_DISABLE)) != NULL)&&
162        (p_attr->attr_value.v.u8) )
163    {
164        attr_mask |= HID_SDP_DISABLE;
165    }
166
167    if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_BATTERY_POWER)) != NULL)&&
168        (p_attr->attr_value.v.u8) )
169    {
170        attr_mask |= HID_BATTERY_POWER;
171    }
172
173    if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_REMOTE_WAKE)) != NULL)&&
174        (p_attr->attr_value.v.u8) )
175    {
176        attr_mask |= HID_REMOTE_WAKE;
177    }
178
179    hidh_get_str_attr( p_rec, ATTR_ID_SERVICE_NAME, HID_MAX_SVC_NAME_LEN, p_nvi->svc_name );
180    hidh_get_str_attr( p_rec, ATTR_ID_SERVICE_DESCRIPTION, HID_MAX_SVC_DESCR_LEN, p_nvi->svc_descr );
181    hidh_get_str_attr( p_rec, ATTR_ID_PROVIDER_NAME, HID_MAX_PROV_NAME_LEN, p_nvi->prov_name );
182
183    if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_DEVICE_RELNUM)) != NULL))
184    {
185        p_nvi->rel_num = p_attr->attr_value.v.u16;
186    }
187
188    if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_COUNTRY_CODE)) != NULL))
189    {
190        p_nvi->ctry_code = p_attr->attr_value.v.u8;
191    }
192
193    if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_DEVICE_SUBCLASS)) != NULL))
194    {
195        p_nvi->sub_class = p_attr->attr_value.v.u8;
196    }
197
198    if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_PARSER_VERSION)) != NULL))
199    {
200        p_nvi->hpars_ver = p_attr->attr_value.v.u16;
201    }
202
203    if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_LINK_SUPERVISION_TO)) != NULL))
204    {
205        attr_mask |= HID_SUP_TOUT_AVLBL;
206        p_nvi->sup_timeout = p_attr->attr_value.v.u16;
207    }
208
209    if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_SSR_HOST_MAX_LAT)) != NULL))
210    {
211        attr_mask |= HID_SSR_MAX_LATENCY;
212        p_nvi->ssr_max_latency = p_attr->attr_value.v.u16;
213    }
214    else
215        p_nvi->ssr_max_latency = HID_SSR_PARAM_INVALID;
216
217    if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_SSR_HOST_MIN_TOUT)) != NULL))
218    {
219        attr_mask |= HID_SSR_MIN_TOUT;
220        p_nvi->ssr_min_tout = p_attr->attr_value.v.u16;
221    }
222    else
223        p_nvi->ssr_min_tout = HID_SSR_PARAM_INVALID;
224
225    hh_cb.sdp_rec.p_sdp_layer_rec = p_rec;
226    hh_cb.sdp_cback(SDP_SUCCESS, attr_mask, &hh_cb.sdp_rec);
227}
228
229
230/*******************************************************************************
231**
232** Function         HID_HostInit
233**
234** Description      This function initializes the control block and trace variable
235**
236** Returns          void
237**
238*******************************************************************************/
239void HID_HostInit (void)
240{
241    memset(&hh_cb, 0, sizeof(tHID_HOST_CTB));
242
243#if defined(HID_INITIAL_TRACE_LEVEL)
244    hh_cb.trace_level = HID_INITIAL_TRACE_LEVEL;
245#else
246    hh_cb.trace_level = BT_TRACE_LEVEL_NONE;
247#endif
248}
249
250/*******************************************************************************
251**
252** Function         HID_HostSetTraceLevel
253**
254** Description      This function sets the trace level for HID Host. If called with
255**                  a value of 0xFF, it simply reads the current trace level.
256**
257** Returns          the new (current) trace level
258**
259*******************************************************************************/
260UINT8 HID_HostSetTraceLevel (UINT8 new_level)
261{
262    if (new_level != 0xFF)
263        hh_cb.trace_level = new_level;
264
265    return (hh_cb.trace_level);
266}
267
268/*******************************************************************************
269**
270** Function         HID_HostRegister
271**
272** Description      This function registers HID-Host with lower layers
273**
274** Returns          tHID_STATUS
275**
276*******************************************************************************/
277tHID_STATUS HID_HostRegister (tHID_HOST_DEV_CALLBACK *dev_cback)
278{
279    tHID_STATUS st;
280
281    if( hh_cb.reg_flag )
282        return HID_ERR_ALREADY_REGISTERED;
283
284    if( dev_cback == NULL )
285        return HID_ERR_INVALID_PARAM;
286
287    /* Register with L2CAP */
288    if( (st = hidh_conn_reg()) != HID_SUCCESS )
289    {
290        return st;
291    }
292
293    hh_cb.callback = dev_cback ;
294    hh_cb.reg_flag = TRUE;
295
296    return (HID_SUCCESS);
297}
298
299/*******************************************************************************
300**
301** Function         HID_HostDeregister
302**
303** Description      This function is called when the host is about power down.
304**
305** Returns          tHID_STATUS
306**
307*******************************************************************************/
308tHID_STATUS HID_HostDeregister(void)
309{
310    UINT8 i;
311
312    if( !hh_cb.reg_flag )
313        return (HID_ERR_NOT_REGISTERED);
314
315    for( i=0; i<HID_HOST_MAX_DEVICES; i++ )
316    {
317        HID_HostRemoveDev( i ) ;
318    }
319
320    hidh_conn_dereg();
321    hh_cb.reg_flag = FALSE;
322
323    return (HID_SUCCESS) ;
324}
325
326/*******************************************************************************
327**
328** Function         HID_HostAddDev
329**
330** Description      This is called so HID-host may manage this device.
331**
332** Returns          tHID_STATUS
333**
334*******************************************************************************/
335tHID_STATUS HID_HostAddDev ( BD_ADDR addr, UINT16 attr_mask, UINT8 *handle )
336{
337    int i;
338    /* Find an entry for this device in hh_cb.devices array */
339
340    if( !hh_cb.reg_flag )
341        return (HID_ERR_NOT_REGISTERED);
342
343    for( i=0; i<HID_HOST_MAX_DEVICES; i++)
344    {
345        if((hh_cb.devices[i].in_use) &&
346           (!memcmp(addr, hh_cb.devices[i].addr, BD_ADDR_LEN)))
347            break;
348    }
349
350    if (i== HID_HOST_MAX_DEVICES )
351    {
352        for( i=0; i<HID_HOST_MAX_DEVICES; i++)
353        {
354            if( !hh_cb.devices[i].in_use)
355                break;
356        }
357    }
358
359    if( i==HID_HOST_MAX_DEVICES )
360        return HID_ERR_NO_RESOURCES;
361
362    if (!hh_cb.devices[i].in_use)
363    {
364        hh_cb.devices[i].in_use = TRUE;
365        memcpy( hh_cb.devices[i].addr, addr, sizeof( BD_ADDR ) ) ;
366        hh_cb.devices[i].state = HID_DEV_NO_CONN;
367        hh_cb.devices[i].conn_tries = 0 ;
368    }
369
370    hh_cb.devices[i].attr_mask = attr_mask;
371
372    *handle = i;
373
374    return (HID_SUCCESS);
375}
376
377
378/*******************************************************************************
379**
380** Function         HID_HostRemoveDev
381**
382** Description      This removes the device from list devices that host has to manage.
383**
384** Returns          tHID_STATUS
385**
386*******************************************************************************/
387tHID_STATUS HID_HostRemoveDev ( UINT8 dev_handle )
388{
389    if( !hh_cb.reg_flag )
390        return (HID_ERR_NOT_REGISTERED);
391
392    if( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) )
393        return HID_ERR_INVALID_PARAM;
394
395    HID_HostCloseDev( dev_handle ) ;
396    hh_cb.devices[dev_handle].in_use = FALSE;
397    hh_cb.devices[dev_handle].conn.conn_state = HID_CONN_STATE_UNUSED;
398    hh_cb.devices[dev_handle].conn.ctrl_cid = hh_cb.devices[dev_handle].conn.intr_cid = 0;
399
400    return HID_SUCCESS;
401}
402
403/*******************************************************************************
404**
405** Function         HID_HostOpenDev
406**
407** Description      This function is called when the user wants to initiate a
408**                  connection attempt to a device.
409**
410** Returns          void
411**
412*******************************************************************************/
413tHID_STATUS HID_HostOpenDev ( UINT8 dev_handle )
414{
415    if( !hh_cb.reg_flag )
416        return (HID_ERR_NOT_REGISTERED);
417
418    if( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) )
419        return HID_ERR_INVALID_PARAM;
420
421    if( hh_cb.devices[dev_handle].state != HID_DEV_NO_CONN )
422        return HID_ERR_ALREADY_CONN;
423
424    hh_cb.devices[dev_handle].conn_tries = 1;
425    return hidh_conn_initiate( dev_handle );
426}
427
428/*******************************************************************************
429**
430** Function         HID_HostWriteDev
431**
432** Description      This function is called when the host has a report to send.
433**
434**                  report_id: is only used on GET_REPORT transaction if is specified.
435**                              only valid when it's a non-zero value.
436**
437** Returns          void
438**
439*******************************************************************************/
440tHID_STATUS HID_HostWriteDev( UINT8 dev_handle, UINT8 t_type,
441                              UINT8 param, UINT16 data, UINT8 report_id, BT_HDR *pbuf  )
442{
443    tHID_STATUS status = HID_SUCCESS;
444
445    if( !hh_cb.reg_flag )
446    {
447        HIDH_TRACE_ERROR0("HID_ERR_NOT_REGISTERED");
448        status = HID_ERR_NOT_REGISTERED;
449    }
450
451    if( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) )
452    {
453        HIDH_TRACE_ERROR0("HID_ERR_INVALID_PARAM");
454        status = HID_ERR_INVALID_PARAM;
455    }
456
457    else if( hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED )
458    {
459        HIDH_TRACE_ERROR1("HID_ERR_NO_CONNECTION dev_handle %d", dev_handle);
460        status = HID_ERR_NO_CONNECTION;
461    }
462
463    if (status != HID_SUCCESS)
464    {
465        if (pbuf)
466            GKI_freebuf ((void *)pbuf);
467    }
468    else
469        status = hidh_conn_snd_data( dev_handle, t_type, param, data, report_id, pbuf ) ;
470
471    return status;
472}
473
474/*******************************************************************************
475**
476** Function         HID_HostCloseDev
477**
478** Description      This function disconnects the device.
479**
480** Returns          void
481**
482*******************************************************************************/
483tHID_STATUS HID_HostCloseDev( UINT8 dev_handle )
484{
485    if( !hh_cb.reg_flag )
486        return (HID_ERR_NOT_REGISTERED);
487
488    if( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) )
489        return HID_ERR_INVALID_PARAM;
490
491    hh_cb.devices[dev_handle].conn_tries = HID_HOST_MAX_CONN_RETRY+1;
492    btu_stop_timer( &(hh_cb.devices[dev_handle].conn.timer_entry) ) ;
493
494    if( hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED )
495        return HID_ERR_NO_CONNECTION;
496
497    hh_cb.devices[dev_handle].conn_tries = HID_HOST_MAX_CONN_RETRY+1;
498    return hidh_conn_disconnect( dev_handle );
499}
500
501tHID_STATUS HID_HostSetSecurityLevel( char serv_name[], UINT8 sec_lvl )
502{
503    if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HID_SEC_CTRL,
504                               sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_SEC_CHN))
505    {
506        HIDH_TRACE_ERROR0 ("Security Registration 1 failed");
507        return (HID_ERR_NO_RESOURCES);
508    }
509
510    if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HID_SEC_CTRL,
511                               sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_SEC_CHN))
512    {
513        HIDH_TRACE_ERROR0 ("Security Registration 2 failed");
514        return (HID_ERR_NO_RESOURCES);
515    }
516
517    if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HID_NOSEC_CTRL,
518                               BTM_SEC_NONE, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_NOSEC_CHN))
519    {
520        HIDH_TRACE_ERROR0 ("Security Registration 3 failed");
521        return (HID_ERR_NO_RESOURCES);
522    }
523
524    if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HID_NOSEC_CTRL,
525                               BTM_SEC_NONE, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_NOSEC_CHN))
526    {
527        HIDH_TRACE_ERROR0 ("Security Registration 4 failed");
528        return (HID_ERR_NO_RESOURCES);
529    }
530
531    if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HID_INTR,
532                               BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, 0))
533    {
534        HIDH_TRACE_ERROR0 ("Security Registration 5 failed");
535        return (HID_ERR_NO_RESOURCES);
536    }
537
538    if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HID_INTR,
539                               BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, 0))
540    {
541        HIDH_TRACE_ERROR0 ("Security Registration 6 failed");
542        return (HID_ERR_NO_RESOURCES);
543    }
544
545    return( HID_SUCCESS );
546}
547