From: Andre Eisenbach Date: Thu, 8 Aug 2013 22:42:48 +0000 (-0700) Subject: LE: Add support for the HID-over-GATT profile (1/3) X-Git-Tag: android-x86-7.1-r1~1954^2~15^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=2e7fa68bfd9723b7ec7b6b0b128d89f31a2e06ee;p=android-x86%2Fsystem-bt.git LE: Add support for the HID-over-GATT profile (1/3) bug:8330048 Change-Id: I5727161b0a87700487bee96cdffa8fd169034204 --- diff --git a/bta/Android.mk b/bta/Android.mk index 7d1c19ece..2a2551f0d 100644 --- a/bta/Android.mk +++ b/bta/Android.mk @@ -42,6 +42,7 @@ LOCAL_SRC_FILES:= \ ./hh/bta_hh_cfg.c \ ./hh/bta_hh_act.c \ ./hh/bta_hh_api.c \ + ./hh/bta_hh_le.c \ ./hh/bta_hh_utils.c \ ./hh/bta_hh_main.c \ ./pb/bta_pbs_cfg.c \ diff --git a/bta/dm/bta_dm_act.c b/bta/dm/bta_dm_act.c index 38a382e09..06a227958 100644 --- a/bta/dm/bta_dm_act.c +++ b/bta/dm/bta_dm_act.c @@ -180,7 +180,7 @@ const UINT32 bta_service_id_to_btm_srv_id_lkup_tbl [BTA_MAX_SERVICE_ID] = BTM_SEC_SERVICE_SAP, /* BTA_SAP_SERVICE_ID */ BTM_SEC_SERVICE_AVDTP, /* BTA_A2DP_SERVICE_ID */ BTM_SEC_SERVICE_AVCTP, /* BTA_AVRCP_SERVICE_ID */ - BTM_SEC_SERVICE_HID_SEC_CTRL, /* BTA_HID_SERVICE_ID */ + BTM_SEC_SERVICE_HIDH_SEC_CTRL, /* BTA_HID_SERVICE_ID */ BTM_SEC_SERVICE_AVDTP, /* BTA_VDP_SERVICE_ID */ BTM_SEC_SERVICE_PBAP, /* BTA_PBAP_SERVICE_ID */ BTM_SEC_SERVICE_HEADSET, /* BTA_HSP_HS_SERVICE_ID */ diff --git a/bta/hh/bta_hh_act.c b/bta/hh/bta_hh_act.c index f2b91e005..1c83d071f 100644 --- a/bta/hh/bta_hh_act.c +++ b/bta/hh/bta_hh_act.c @@ -42,8 +42,8 @@ /***************************************************************************** ** Local Function prototypes *****************************************************************************/ -static void bta_hh_cback (UINT8 dev_handle, UINT8 event, UINT32 data, - BT_HDR *pdata); +static void bta_hh_cback (UINT8 dev_handle, BD_ADDR addr, UINT8 event, + UINT32 data, BT_HDR *pdata); static tBTA_HH_STATUS bta_hh_get_trans_status(UINT32 result); #if BTA_HH_DEBUG @@ -96,6 +96,13 @@ void bta_hh_api_enable(tBTA_HH_DATA *p_data) bta_hh_cb.cb_index[xx] = BTA_HH_IDX_INVALID; } +#if (BTA_HH_LE_INCLUDED == TRUE) + if (status == BTA_HH_OK) + { + bta_hh_le_enable(); + } + else +#endif /* signal BTA call back event */ (* bta_hh_cb.p_cback)(BTA_HH_ENABLE_EVT, (tBTA_HH *)&status); } @@ -160,6 +167,11 @@ void bta_hh_disc_cmpl(void) if (HID_HostDeregister()!= HID_SUCCESS) status = BTA_HH_ERR; +#if (BTA_HH_LE_INCLUDED == TRUE) + bta_hh_le_deregister(); + return; +#endif + bta_hh_cleanup_disable(status); } @@ -187,9 +199,9 @@ static void bta_hh_sdp_cback(UINT16 result, UINT16 attr_mask, attr_mask |= HID_SEC_REQUIRED; #if BTA_HH_DEBUG - APPL_TRACE_EVENT3("bta_hh_sdp_cback: p_cb: %d result 0x%02x, \ - attr_mask 0x%02x", \ - p_cb, result, attr_mask); + APPL_TRACE_EVENT4("bta_hh_sdp_cback: p_cb: %d result 0x%02x, \ + attr_mask 0x%02x, handle %x", \ + p_cb, result, attr_mask,p_cb->hid_handle); #endif /* check to see type of device is supported , and should not been added before */ @@ -235,7 +247,6 @@ static void bta_hh_sdp_cback(UINT16 result, UINT16 attr_mask, /* free disc_db when SDP is completed */ utl_freebuf((void **)&bta_hh_cb.p_disc_db); - /* send SDP_CMPL_EVT into state machine */ bta_hh_sm_execute(p_cb, BTA_HH_SDP_CMPL_EVT, (tBTA_HH_DATA *)&status); @@ -259,6 +270,7 @@ static void bta_hh_di_sdp_cback(UINT16 result) #if BTA_HH_DEBUG APPL_TRACE_EVENT2("bta_hh_di_sdp_cback: p_cb: %d result 0x%02x", p_cb, result); #endif + /* if DI record does not exist on remote device, vendor_id in tBTA_HH_DEV_DSCP_INFO will be * set to 0xffff and we will allow the connection to go through. Spec mandates that DI * record be set, but many HID devices do not set this. So for IOP purposes, we allow the @@ -328,6 +340,14 @@ void bta_hh_start_sdp(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) p_cb->mode = p_data->api_conn.mode; bta_hh_cb.p_cur = p_cb; +#if (BTA_HH_LE_INCLUDED == TRUE) + if (bta_hh_is_le_device(p_cb, p_data->api_conn.bd_addr)) + { + bta_hh_le_open_conn(p_cb, p_data->api_conn.bd_addr); + return; + } +#endif + /* if previously virtually cabled device, skip SDP */ if (p_cb->app_id) { @@ -337,7 +357,7 @@ void bta_hh_start_sdp(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) #endif if (p_cb->hid_handle == BTA_HH_INVALID_HANDLE) { - if ((HID_HostAddDev (p_cb->addr, p_cb->attr_mask, &hdl)) \ + if (HID_HostAddDev (p_cb->addr, p_cb->attr_mask, &hdl) \ == HID_SUCCESS) { /* update device CB with newly register device handle */ @@ -451,8 +471,18 @@ void bta_hh_sdp_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) if (status != BTA_HH_OK) { + /* Check if this was incoming connection request from an unknown device + **and connection failed due to missing HID Device SDP UUID + **In above condition, disconnect the link as well as remove the + **device from list of HID devices*/ + if ((status == BTA_HH_ERR_SDP) && + (p_cb->incoming_conn) &&(p_cb->app_id == 0)) + { + APPL_TRACE_DEBUG1 ("bta_hh_sdp_cmpl:SDP failed for incoming conn :hndl %d", + p_cb->incoming_hid_handle); + HID_HostRemoveDev( p_cb->incoming_hid_handle); + } conn_dat.status = status; - (* bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH *)&conn_dat); /* move state machine W4_CONN ->IDLE */ @@ -464,12 +494,10 @@ void bta_hh_sdp_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) /* clean up device control block */ bta_hh_clean_up_kdev(p_cb); } - #if BTA_HH_DEBUG bta_hh_trace_dev_db(); #endif } - return; } @@ -488,16 +516,24 @@ void bta_hh_api_disc_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) tBTA_HH_CBDATA disc_dat; tHID_STATUS status; - /* found an active connection */ - disc_dat.handle = p_data ?(UINT8)p_data->hdr.layer_specific :p_cb->hid_handle; - disc_dat.status = BTA_HH_ERR; +#if BTA_HH_LE_INCLUDED == TRUE + if (p_cb->is_le_device) + bta_hh_le_api_disc_act(p_cb); + else +#endif + { + /* found an active connection */ + disc_dat.handle = p_data ?(UINT8)p_data->hdr.layer_specific :p_cb->hid_handle; + disc_dat.status = BTA_HH_ERR; - status = HID_HostCloseDev(disc_dat.handle); + status = HID_HostCloseDev(disc_dat.handle); - if (status) - (* bta_hh_cb.p_cback)(BTA_HH_CLOSE_EVT, (tBTA_HH *)&disc_dat); + if (status) + (* bta_hh_cb.p_cback)(BTA_HH_CLOSE_EVT, (tBTA_HH *)&disc_dat); + } return; + } /******************************************************************************* ** @@ -526,8 +562,23 @@ void bta_hh_open_cmpl_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) bta_hh_co_open(p_cb->hid_handle, p_cb->sub_class, p_cb->attr_mask, p_cb->app_id); +#if (BTA_HH_LE_INCLUDED == TRUE) + conn.status = p_cb->status; + conn.le_hid = p_cb->is_le_device; + conn.scps_supported = p_cb->scps_supported; + + if (!p_cb->is_le_device) +#endif + { + /* inform role manager */ + bta_sys_conn_open( BTA_ID_HH ,p_cb->app_id, p_cb->addr); + } /* set protocol mode when not default report mode */ - if (p_cb->mode != BTA_HH_PROTO_RPT_MODE) + if ( p_cb->mode != BTA_HH_PROTO_RPT_MODE +#if (BTA_HH_LE_INCLUDED == TRUE) + && !p_cb->is_le_device +#endif + ) { if ((HID_HostWriteDev(dev_handle, HID_TRANS_SET_PROTOCOL, HID_PAR_PROTOCOL_BOOT_MODE, @@ -548,6 +599,7 @@ void bta_hh_open_cmpl_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) (* bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH *)&conn); p_cb->incoming_conn = FALSE; + p_cb->incoming_hid_handle = BTA_HH_INVALID_HANDLE; } /******************************************************************************* @@ -564,10 +616,10 @@ void bta_hh_open_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) { tBTA_HH_API_CONN conn_data; -#if BTA_HH_DEBUG UINT8 dev_handle = p_data ? (UINT8)p_data->hid_cback.hdr.layer_specific : \ p_cb->hid_handle; +#if BTA_HH_DEBUG APPL_TRACE_EVENT1 ("bta_hh_open_act: Device[%d] connected", dev_handle); #endif @@ -581,6 +633,8 @@ void bta_hh_open_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) performed, do it first */ { p_cb->incoming_conn = TRUE; + /* store the handle here in case sdp fails - need to disconnect */ + p_cb->incoming_hid_handle = dev_handle; memset(&conn_data, 0, sizeof(tBTA_HH_API_CONN)); bdcpy(conn_data.bd_addr, p_cb->addr); @@ -635,6 +689,7 @@ void bta_hh_handsk_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA * p_data) #endif memset(&hs_data, 0, sizeof(tBTA_HH_HSDATA)); + memset(&cback_data, 0, sizeof(tBTA_HH_CBDATA)); switch (p_cb->w4_evt) { @@ -753,6 +808,47 @@ void bta_hh_ctrl_dat_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA * p_data) /******************************************************************************* ** +** Function bta_hh_open_failure +** +** Description report HID open failure when at wait for connection state and receive +** device close event. +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_open_failure(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_CONN conn_dat ; + UINT32 reason = p_data->hid_cback.data; /* Reason for closing (32-bit) */ + + memset(&conn_dat, 0, sizeof(tBTA_HH_CONN)); + conn_dat.handle = p_cb->hid_handle; + conn_dat.status = (reason == HID_ERR_AUTH_FAILED) ? + BTA_HH_ERR_AUTH_FAILED : BTA_HH_ERR; + bdcpy(conn_dat.bda, p_cb->addr); + HID_HostCloseDev(p_cb->hid_handle); + + /* Report OPEN fail event */ + (*bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH *)&conn_dat); + +#if BTA_HH_DEBUG + bta_hh_trace_dev_db(); +#endif + /* clean up control block, but retain SDP info and device handle */ + p_cb->vp = FALSE; + p_cb->w4_evt = 0; + + /* if no connection is active and HH disable is signaled, disable service */ + if (bta_hh_cb.cnt_num == 0 && bta_hh_cb.w4_disable) + { + bta_hh_disc_cmpl(); + } + +} + +/******************************************************************************* +** ** Function bta_hh_close_act ** ** Description HID Host process a close event @@ -844,6 +940,13 @@ void bta_hh_close_act (tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) *******************************************************************************/ void bta_hh_get_dscp_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) { +#if (BTA_HH_LE_INCLUDED == TRUE) + if (p_cb->is_le_device) + { + bta_hh_le_get_dscp_act(p_cb); + } + else +#endif (*bta_hh_cb.p_cback)(BTA_HH_GET_DSCP_EVT, (tBTA_HH *)&p_cb->dscp_info); } @@ -873,19 +976,36 @@ void bta_hh_maint_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) /* initialize callback data */ if (p_cb->hid_handle == BTA_HH_INVALID_HANDLE) { +#if (BTA_HH_LE_INCLUDED == TRUE) + if (bta_hh_is_le_device(p_cb, p_data->api_conn.bd_addr)) + { + dev_info.handle = bta_hh_le_add_device(p_cb, p_dev_info); + dev_info.status = BTA_HH_OK; + } + else +#endif + if (HID_HostAddDev(p_dev_info->bda, p_dev_info->attr_mask, &dev_handle)\ == HID_SUCCESS) { dev_info.handle = dev_handle; dev_info.status = BTA_HH_OK; +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) /* update DI information */ bta_hh_update_di_info(p_cb, p_dev_info->dscp_info.vendor_id, p_dev_info->dscp_info.product_id, p_dev_info->dscp_info.version, + p_dev_info->dscp_info.flag); +#else + bta_hh_update_di_info(p_cb, + p_dev_info->dscp_info.vendor_id, + p_dev_info->dscp_info.product_id, + p_dev_info->dscp_info.version, 0); +#endif /* add to BTA device list */ bta_hh_add_device_to_list(p_cb, dev_handle, p_dev_info->attr_mask, @@ -912,6 +1032,15 @@ void bta_hh_maint_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) dev_info.handle = (UINT8)p_dev_info->hdr.layer_specific; bdcpy(dev_info.bda, p_cb->addr); +#if BTA_HH_LE_INCLUDED == TRUE + if (p_cb->is_le_device) + { + bta_hh_le_remove_dev_bg_conn(p_cb); + bta_hh_sm_execute(p_cb, BTA_HH_API_CLOSE_EVT, NULL); + bta_hh_clean_up_kdev(p_cb); + } + else +#endif { if(HID_HostRemoveDev( dev_info.handle ) == HID_SUCCESS) { @@ -945,6 +1074,13 @@ void bta_hh_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) UINT16 event = (p_data->api_sndcmd.t_type - BTA_HH_FST_BTE_TRANS_EVT) + BTA_HH_FST_TRANS_CB_EVT; +#if BTA_HH_LE_INCLUDED == TRUE + if (p_cb->is_le_device) + bta_hh_le_write_dev_act(p_cb, p_data); + else +#endif + { + cbdata.handle = p_cb->hid_handle; /* match up BTE/BTA report/boot mode def */ @@ -1022,7 +1158,7 @@ void bta_hh_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) } } - + } return; } @@ -1039,8 +1175,8 @@ void bta_hh_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) ** Returns void ** *******************************************************************************/ -static void bta_hh_cback (UINT8 dev_handle, UINT8 event, UINT32 data, - BT_HDR *pdata) +static void bta_hh_cback (UINT8 dev_handle, BD_ADDR addr, UINT8 event, + UINT32 data, BT_HDR *pdata) { tBTA_HH_CBACK_DATA *p_buf = NULL; UINT16 sm_event = BTA_HH_INVALID_EVT; @@ -1093,6 +1229,7 @@ static void bta_hh_cback (UINT8 dev_handle, UINT8 event, UINT32 data, p_buf->hdr.event = sm_event; p_buf->hdr.layer_specific = (UINT16)dev_handle; p_buf->data = data; + bdcpy(p_buf->addr, addr); p_buf->p_data = pdata; bta_sys_sendmsg(p_buf); diff --git a/bta/hh/bta_hh_api.c b/bta/hh/bta_hh_api.c index 2a7965e92..1e71d9114 100644 --- a/bta/hh/bta_hh_api.c +++ b/bta/hh/bta_hh_api.c @@ -305,6 +305,13 @@ void BTA_HhSendCtrl(UINT8 dev_handle, tBTA_HH_TRANS_CTRL_TYPE c_type) *******************************************************************************/ void BTA_HhSendData(UINT8 dev_handle, BD_ADDR dev_bda, BT_HDR *p_data) { +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + if (p_data->layer_specific != BTA_HH_RPTT_OUTPUT) + { + APPL_TRACE_ERROR0("ERROR! Wrong report type! Write Command only valid for output report!"); + return; + } +#endif bta_hh_snd_write_dev(dev_handle, HID_TRANS_DATA, (UINT8)p_data->layer_specific, 0, 0, p_data); } @@ -406,7 +413,37 @@ void BTA_HhRemoveDev(UINT8 dev_handle ) bta_sys_sendmsg(p_buf); } } +#if BTA_HH_LE_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTA_HhUpdateLeScanParam +** +** Description Update the scan paramteters if connected to a LE hid device as +** report host. +** +** Returns void +** +*******************************************************************************/ +void BTA_HhUpdateLeScanParam(UINT8 dev_handle, UINT16 scan_int, UINT16 scan_win) +{ + tBTA_HH_SCPP_UPDATE *p_buf; + + p_buf = (tBTA_HH_SCPP_UPDATE *)GKI_getbuf((UINT16)sizeof(tBTA_HH_SCPP_UPDATE)); + + if (p_buf != NULL) + { + memset(p_buf, 0, sizeof(tBTA_HH_SCPP_UPDATE)); + p_buf->hdr.event = BTA_HH_API_SCPP_UPDATE_EVT; + p_buf->hdr.layer_specific = (UINT16) dev_handle; + p_buf->scan_int = scan_int; + p_buf->scan_win = scan_win; + + bta_sys_sendmsg(p_buf); + } +} +#endif /*******************************************************************************/ /* Utility Function */ /*******************************************************************************/ diff --git a/bta/hh/bta_hh_int.h b/bta/hh/bta_hh_int.h index 7630a3ad3..0fa7c1764 100644 --- a/bta/hh/bta_hh_int.h +++ b/bta/hh/bta_hh_int.h @@ -30,9 +30,17 @@ #include "utl.h" #include "bta_hh_api.h" +#if BTA_HH_LE_INCLUDED == TRUE +#include "bta_gatt_api.h" +#endif + /* can be moved to bta_api.h */ #define BTA_HH_MAX_RPT_CHARS 8 +#if (BTA_GATT_INCLUDED == FALSE || BLE_INCLUDED == FALSE) +#undef BTA_HH_LE_INCLUDED +#define BTA_HH_LE_INCLUDED FALSE +#endif /* state machine events, these events are handled by the state machine */ enum @@ -49,6 +57,17 @@ enum BTA_HH_API_GET_DSCP_EVT, BTA_HH_API_MAINT_DEV_EVT, BTA_HH_OPEN_CMPL_EVT, +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + BTA_HH_GATT_CLOSE_EVT, + BTA_HH_GATT_OPEN_EVT, + BTA_HH_START_ENC_EVT, + BTA_HH_ENC_CMPL_EVT, + BTA_HH_GATT_READ_CHAR_CMPL_EVT, + BTA_HH_GATT_WRITE_CHAR_CMPL_EVT, + BTA_HH_GATT_READ_DESCR_CMPL_EVT, + BTA_HH_GATT_WRITE_DESCR_CMPL_EVT, + BTA_HH_API_SCPP_UPDATE_EVT, +#endif /* not handled by execute state machine */ BTA_HH_API_ENABLE_EVT, @@ -74,6 +93,11 @@ enum BTA_HH_IDLE_ST, BTA_HH_W4_CONN_ST, BTA_HH_CONN_ST +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + ,BTA_HH_W4_SEC +#endif + ,BTA_HH_INVALID_ST /* Used to check invalid states before executing SM function */ + }; typedef UINT8 tBTA_HH_STATE; @@ -84,6 +108,9 @@ typedef struct UINT8 t_type; UINT8 param; UINT8 rpt_id; +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + UINT8 srvc_id; +#endif UINT16 data; BT_HDR *p_data; }tBTA_HH_CMD_DATA; @@ -109,6 +136,7 @@ typedef struct typedef struct { BT_HDR hdr; + BD_ADDR addr; UINT32 data; BT_HDR *p_data; }tBTA_HH_CBACK_DATA; @@ -124,6 +152,22 @@ typedef struct tBTA_HH_DEV_DSCP_INFO dscp_info; }tBTA_HH_MAINT_DEV; +#if BTA_HH_LE_INCLUDED == TRUE +typedef struct +{ + BT_HDR hdr; + UINT16 conn_id; + tBTA_GATT_REASON reason; /* disconnect reason code, not useful when connect event is reported */ + +}tBTA_HH_LE_CLOSE; + +typedef struct +{ + BT_HDR hdr; + UINT16 scan_int; + UINT16 scan_win; +}tBTA_HH_SCPP_UPDATE; +#endif /* union of all event data types */ typedef union { @@ -134,8 +178,67 @@ typedef union tBTA_HH_CBACK_DATA hid_cback; tBTA_HH_STATUS status; tBTA_HH_MAINT_DEV api_maintdev; +#if BTA_HH_LE_INCLUDED == TRUE + tBTA_HH_LE_CLOSE le_close; + tBTA_GATTC_OPEN le_open; + tBTA_HH_SCPP_UPDATE le_scpp_update; +#endif } tBTA_HH_DATA; +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) +typedef struct +{ + UINT8 index; + BOOLEAN in_use; + UINT8 inst_id; /* share service instance ID and report instance ID, as + hi 4 for service instance ID, low 4 as charatceristic instance ID */ + tBTA_HH_RPT_TYPE rpt_type; + UINT16 uuid; + UINT16 prop; + UINT8 rpt_id; + BOOLEAN client_cfg_exist; + UINT16 client_cfg_value; +}tBTA_HH_LE_RPT; + +#ifndef BTA_HH_LE_RPT_MAX +#define BTA_HH_LE_RPT_MAX 10 +#endif + +typedef struct +{ + BOOLEAN in_use; + tBTA_HH_LE_RPT report[BTA_HH_LE_RPT_MAX]; + +#define BTA_HH_LE_PROTO_MODE_BIT 0x01 +#define BTA_HH_LE_CP_BIT 0x02 + UINT8 option_char; /* control point char exisit or not */ + + BOOLEAN expl_incl_srvc; + UINT8 incl_srvc_inst; /* assuming only one included service : battery service */ + UINT8 cur_expl_char_idx; /* currently discovering service index */ + +#define BTA_HH_LE_REMOTE_WAKE 0x01 +#define BTA_HH_LE_NORMAL_CONN 0x02 + UINT8 flag; /* HID Information flag */ + UINT8 *rpt_map; + UINT16 ext_rpt_ref; + tBTA_HH_DEV_DESCR descriptor; + +}tBTA_HH_LE_HID_SRVC; + +#ifndef BTA_HH_LE_HID_SRVC_MAX +#define BTA_HH_LE_HID_SRVC_MAX 1 +#endif + +/* convert a HID handle to the LE CB index */ +#define BTA_HH_GET_LE_CB_IDX(x) (((x) >> 4) - 1) +/* convert a GATT connection ID to HID device handle, it is the hi 4 bits of a UINT8 */ +#define BTA_HH_GET_LE_DEV_HDL(x) (UINT8)(((x) + 1) << 4) +/* check to see if th edevice handle is a LE device handle */ +#define BTA_HH_IS_LE_DEV_HDL(x) ((x) & 0xf0) +#define BTA_HH_IS_LE_DEV_HDL_VALID(x) (((x)>>4) < BTA_HH_LE_MAX_KNOWN) +#endif + /* device control block */ typedef struct { @@ -147,13 +250,39 @@ typedef struct UINT8 sub_class; /* Cod sub class */ UINT8 sec_mask; /* security mask */ UINT8 app_id; /* application ID for this connection */ - UINT8 hid_handle; /* device handle */ + UINT8 hid_handle; /* device handle : low 4 bits for regular HID: HID_HOST_MAX_DEVICES can not exceed 15; + high 4 bits for LE HID: GATT_MAX_PHY_CHANNEL can not exceed 15 */ BOOLEAN vp; /* virtually unplug flag */ BOOLEAN in_use; /* control block currently in use */ BOOLEAN incoming_conn; /* is incoming connection? */ + UINT8 incoming_hid_handle; /* temporary handle for incoming connection? */ BOOLEAN opened; /* TRUE if device successfully opened HID connection */ tBTA_HH_PROTO_MODE mode; /* protocol mode */ tBTA_HH_STATE state; /* CB state */ + +#if (BTA_HH_LE_INCLUDED == TRUE) +#define BTA_HH_LE_DISC_NONE 0x00 +#define BTA_HH_LE_DISC_HIDS 0x01 +#define BTA_HH_LE_DISC_DIS 0x02 +#define BTA_HH_LE_DISC_SCPS 0x04 + + UINT8 disc_active; + tBTA_HH_STATUS status; + BOOLEAN is_le_device; + tBTA_HH_LE_HID_SRVC hid_srvc[BTA_HH_LE_HID_SRVC_MAX]; + UINT16 conn_id; + BOOLEAN in_bg_conn; + UINT8 total_srvc; + UINT8 clt_cfg_idx; + UINT8 cur_srvc_index; /* currently discovering service index */ + BOOLEAN scps_supported; + +#define BTA_HH_LE_SCPS_NOTIFY_NONE 0 +#define BTA_HH_LE_SCPS_NOTIFY_SPT 0x01 +#define BTA_HH_LE_SCPS_NOTIFY_ENB 0x02 + UINT8 scps_notify; /* scan refresh supported/notification enabled */ +#endif + } tBTA_HH_DEV_CB; /* key board parsing control block */ @@ -179,6 +308,10 @@ typedef struct block idx, used in sdp */ UINT8 cb_index[BTA_HH_MAX_KNOWN]; /* maintain a CB index map to dev handle */ +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + UINT8 le_cb_index[BTA_HH_MAX_DEVICE]; /* maintain a CB index map to LE dev handle */ + tBTA_GATTC_IF gatt_if; +#endif tBTA_HH_CBACK *p_cback; /* Application callbacks */ tSDP_DISCOVERY_DB* p_disc_db; UINT8 trace_level; /* tracing level */ @@ -217,6 +350,7 @@ extern void bta_hh_get_dscp_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); extern void bta_hh_handsk_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); extern void bta_hh_maint_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); extern void bta_hh_open_cmpl_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_open_failure(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); /* utility functions */ extern UINT8 bta_hh_find_cb(BD_ADDR bda); @@ -244,6 +378,33 @@ extern void bta_hh_disc_cmpl(void); extern tBTA_HH_STATUS bta_hh_read_ssr_param(BD_ADDR bd_addr, UINT16 *p_max_ssr_lat, UINT16 *p_min_ssr_tout); +/* functions for LE HID */ +extern void bta_hh_le_enable(void); +extern void bta_hh_le_deregister(void); +extern BOOLEAN bta_hh_is_le_device(tBTA_HH_DEV_CB *p_cb, BD_ADDR remote_bda); +extern void bta_hh_le_open_conn(tBTA_HH_DEV_CB *p_cb, BD_ADDR remote_bda); +extern void bta_hh_le_api_disc_act(tBTA_HH_DEV_CB *p_cb); +extern void bta_hh_le_get_dscp_act(tBTA_HH_DEV_CB *p_cb); +extern void bta_hh_le_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern UINT8 bta_hh_le_add_device(tBTA_HH_DEV_CB *p_cb, tBTA_HH_MAINT_DEV *p_dev_info); +extern void bta_hh_le_remove_dev_bg_conn(tBTA_HH_DEV_CB *p_cb); +extern void bta_hh_le_open_fail(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_gatt_open(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_gatt_close(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); +extern void bta_hh_start_security(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_start_srvc_discovery(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_w4_le_read_char_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_le_read_char_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_w4_le_read_descr_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_le_read_descr_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_w4_le_write_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_le_write_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_le_write_char_descr_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_start_security(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_security_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +extern void bta_hh_le_update_scpp(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); + + #if BTA_HH_DEBUG extern void bta_hh_trace_dev_db(void); #endif diff --git a/bta/hh/bta_hh_le.c b/bta/hh/bta_hh_le.c new file mode 100644 index 000000000..41512d4f5 --- /dev/null +++ b/bta/hh/bta_hh_le.c @@ -0,0 +1,2909 @@ +/***************************************************************************** +** +** Name: bta_hh_le.c +** +** Description: This file contains the HID host over LE +** functions. +** +** Copyright (c) 2005-2011, Broadcom Corp, All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ + +#include "bta_api.h" +#include "bta_hh_int.h" + +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + +#include "bta_api.h" +#include +#include "btm_api.h" +#include "btm_ble_api.h" +#include "bta_hh_co.h" +#include "bta_gatt_api.h" +#include "srvc_api.h" + +#ifndef BTA_HH_LE_RECONN +#define BTA_HH_LE_RECONN TRUE +#endif + +#define BTA_HH_APP_ID_LE 0xff + +#define BTA_HH_LE_RPT_TYPE_VALID(x) ((x) <= BTA_LE_HID_RPT_FEATURE && (x)>=BTA_LE_HID_RPT_INPUT) + +#define BTA_HH_LE_RPT_INST_ID_MAP(s,c) (UINT8)(((s)<<4)||(c)) +#define BTA_HH_LE_RPT_GET_SRVC_INST_ID(x) (UINT8)(x >> 4) +#define BTA_HH_LE_RPT_GET_RPT_INST_ID(x) (UINT8)(x & 0x0f) + + +#define BTA_HH_LE_PROTO_BOOT_MODE 0x00 +#define BTA_HH_LE_PROTO_REPORT_MODE 0x01 + +#define BTA_HH_SCPP_INST_DEF 0 + +#define BTA_HH_LE_DISC_CHAR_NUM 8 +static const UINT16 bta_hh_le_disc_char_uuid[BTA_HH_LE_DISC_CHAR_NUM] = +{ + GATT_UUID_HID_INFORMATION, + GATT_UUID_HID_REPORT_MAP, + GATT_UUID_HID_CONTROL_POINT, + GATT_UUID_HID_REPORT, + GATT_UUID_HID_BT_KB_INPUT, + GATT_UUID_HID_BT_KB_OUTPUT, + GATT_UUID_HID_BT_MOUSE_INPUT, + GATT_UUID_HID_PROTO_MODE /* always make sure this is the last attribute to discover */ +}; + +#define BTA_LE_HID_RTP_UUID_MAX 5 +static const UINT16 bta_hh_uuid_to_rtp_type[BTA_LE_HID_RTP_UUID_MAX][2] = +{ + {GATT_UUID_HID_REPORT, BTA_HH_RPTT_INPUT}, + {GATT_UUID_HID_BT_KB_INPUT, BTA_HH_RPTT_INPUT}, + {GATT_UUID_HID_BT_KB_OUTPUT, BTA_HH_RPTT_OUTPUT}, + {GATT_UUID_HID_BT_MOUSE_INPUT, BTA_HH_RPTT_INPUT}, + {GATT_UUID_BATTERY_LEVEL, BTA_HH_RPTT_INPUT} +}; + + +static void bta_hh_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC *p_data); +static void bta_hh_le_search_hid_chars(tBTA_HH_DEV_CB *p_dev_cb); +static void bta_hh_le_search_hid_included(tBTA_HH_DEV_CB *p_dev_cb); +static void bta_hh_le_search_scps(tBTA_HH_DEV_CB *p_cb); +static void bta_hh_le_search_scps_chars(tBTA_HH_DEV_CB *p_cb); +static void bta_hh_le_register_scpp_notif(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATT_STATUS status); +static void bta_hh_le_register_scpp_notif_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATT_STATUS status); +static void bta_hh_le_add_dev_bg_conn(tBTA_HH_DEV_CB *p_cb, BOOLEAN check_bond); + +#define BTA_HH_LE_SRVC_DEF 0 + +#if BTA_HH_DEBUG == TRUE +static const char *bta_hh_le_rpt_name[4] = +{ + "UNKNOWN", + "INPUT", + "OUTPUT", + "FEATURE" +}; + +/******************************************************************************* +** +** Function bta_hh_le_hid_report_dbg +** +** Description debug function to print out all HID report available on remote +** device. +** +** Returns void +** +*******************************************************************************/ +static void bta_hh_le_hid_report_dbg(tBTA_HH_DEV_CB *p_cb) +{ + UINT8 i , j; + tBTA_HH_LE_RPT *p_rpt; + char * rpt_name; + + APPL_TRACE_DEBUG0("HID Report DB"); + for (i = 0; i < BTA_HH_LE_HID_SRVC_MAX; i ++) + { + if (p_cb->hid_srvc[i].in_use) + { + p_rpt = &p_cb->hid_srvc[i].report[0]; + + APPL_TRACE_DEBUG1("\t HID serivce inst: %d", i); + + for (j = 0; j < BTA_HH_LE_RPT_MAX; j ++, p_rpt++) + { + rpt_name = "Unknown"; + if (p_rpt->in_use) + { + if (p_rpt->uuid == GATT_UUID_HID_REPORT) + rpt_name = "Report"; + if (p_rpt->uuid == GATT_UUID_HID_BT_KB_INPUT) + rpt_name = "Boot KB Input"; + if (p_rpt->uuid == GATT_UUID_HID_BT_KB_OUTPUT) + rpt_name = "Boot KB Output"; + if (p_rpt->uuid == GATT_UUID_HID_BT_MOUSE_INPUT) + rpt_name = "Boot MI Input"; + + + APPL_TRACE_DEBUG6("\t\t [%s- 0x%04x] [Type: %s], [ReportID: %d] [inst_id: %d] [Clt_cfg: %d]", + rpt_name, + p_rpt->uuid , + ((p_rpt->rpt_type < 4) ? bta_hh_le_rpt_name[p_rpt->rpt_type] : "UNKNOWN"), + p_rpt->rpt_id, + p_rpt->inst_id, + p_rpt->client_cfg_value); + } + else + break; + } + } + else + break; + } +} +/******************************************************************************* +** +** Function bta_hh_uuid_to_str +** +** Description +** +** Returns void +** +*******************************************************************************/ +static char *bta_hh_uuid_to_str(UINT16 uuid) +{ + switch(uuid) + { + case GATT_UUID_HID_INFORMATION: + return "GATT_UUID_HID_INFORMATION"; + case GATT_UUID_HID_REPORT_MAP: + return "GATT_UUID_HID_REPORT_MAP"; + case GATT_UUID_HID_CONTROL_POINT: + return "GATT_UUID_HID_CONTROL_POINT"; + case GATT_UUID_HID_REPORT: + return "GATT_UUID_HID_REPORT"; + case GATT_UUID_HID_PROTO_MODE: + return "GATT_UUID_HID_PROTO_MODE"; + case GATT_UUID_HID_BT_KB_INPUT: + return "GATT_UUID_HID_BT_KB_INPUT"; + case GATT_UUID_HID_BT_KB_OUTPUT: + return "GATT_UUID_HID_BT_KB_OUTPUT"; + case GATT_UUID_HID_BT_MOUSE_INPUT: + return "GATT_UUID_HID_BT_MOUSE_INPUT"; + case GATT_UUID_CHAR_CLIENT_CONFIG: + return "GATT_UUID_CHAR_CLIENT_CONFIG"; + case GATT_UUID_EXT_RPT_REF_DESCR: + return "GATT_UUID_EXT_RPT_REF_DESCR"; + case GATT_UUID_RPT_REF_DESCR: + return "GATT_UUID_RPT_REF_DESCR"; + default: + return "Unknown UUID"; + } +} +#endif +/******************************************************************************* +** +** Function bta_hh_le_enable +** +** Description initialize LE HID related functionality +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_enable(void) +{ + char app_name[LEN_UUID_128 + 1]; + tBT_UUID app_uuid = {LEN_UUID_128,{0}}; + UINT8 xx; + + bta_hh_cb.gatt_if = BTA_GATTS_INVALID_IF; + + for (xx = 0; xx < BTA_HH_MAX_DEVICE; xx ++) + bta_hh_cb.le_cb_index[xx] = BTA_HH_IDX_INVALID; + + memset (app_name, 0, LEN_UUID_128 + 1); + strncpy(app_name, "BTA HH OVER LE", LEN_UUID_128); + + memcpy((void *)app_uuid.uu.uuid128, (void *)app_name, LEN_UUID_128); + + BTA_GATTC_AppRegister(&app_uuid, bta_hh_gattc_callback); + + return; +} +/******************************************************************************* +** +** Function bta_hh_le_register_cmpl +** +** Description BTA HH register with BTA GATTC completed +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_register_cmpl(tBTA_GATTC_REG *p_reg) +{ + tBTA_HH_STATUS status = BTA_HH_ERR; + + if (p_reg->status == BTA_GATT_OK) + { + bta_hh_cb.gatt_if = p_reg->client_if; + status = BTA_HH_OK; + } + else + bta_hh_cb.gatt_if = BTA_GATTS_INVALID_IF; + + /* signal BTA call back event */ + (* bta_hh_cb.p_cback)(BTA_HH_ENABLE_EVT, (tBTA_HH *)&status); +} + +/******************************************************************************* +** +** Function bta_hh_le_deregister +** +** Description De-register BTA HH from BTA GATTC +** +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_deregister(void) +{ + BTA_GATTC_AppDeregister(bta_hh_cb.gatt_if); +} + +/******************************************************************************* +** +** Function bta_hh_is_le_device +** +** Description Check to see if the remote device is a LE only device +** +** Parameters: +** +*******************************************************************************/ +BOOLEAN bta_hh_is_le_device(tBTA_HH_DEV_CB *p_cb, BD_ADDR remote_bda) +{ + p_cb->is_le_device = BTM_UseLeLink (remote_bda); + + return p_cb->is_le_device; +} +/******************************************************************************* +** +** Function bta_hh_le_hid_srvc_cached +** +** Description Check to see if LE HID service has been discovered and cached +** +** Parameters: TRUE : has cache; FALSE: none. +** +*******************************************************************************/ +BOOLEAN bta_hh_le_hid_srvc_cached(tBTA_HH_DEV_CB *p_dev_cb) +{ + if (p_dev_cb->hid_srvc[BTA_HH_LE_SRVC_DEF].in_use) + return TRUE; + else + return FALSE; +} +/******************************************************************************* +** +** Function bta_hh_le_add_hid_srvc_entry +** +** Description Add a HID service entry in the HID device control block +** +** Parameters: +** +*******************************************************************************/ +BOOLEAN bta_hh_le_add_hid_srvc_entry(tBTA_HH_DEV_CB *p_dev_cb, UINT8 idx) +{ + BOOLEAN added = FALSE; + + if (idx < BTA_HH_LE_HID_SRVC_MAX) + { + p_dev_cb->hid_srvc[idx].in_use = TRUE; + added = TRUE; + } + else + { + APPL_TRACE_ERROR0("DB full,max HID service entry!"); + } + return added; + +} +/******************************************************************************* +** +** Function bta_hh_le_open_conn +** +** Description open a GATT connection first. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_open_conn(tBTA_HH_DEV_CB *p_cb, BD_ADDR remote_bda) +{ + /* update cb_index[] map */ + p_cb->hid_handle = BTA_HH_GET_LE_DEV_HDL(p_cb->index); + memcpy(p_cb->addr, remote_bda, BD_ADDR_LEN); + bta_hh_cb.le_cb_index[BTA_HH_GET_LE_CB_IDX(p_cb->hid_handle)] = p_cb->index; + p_cb->in_use = TRUE; + + BTA_GATTC_Open(bta_hh_cb.gatt_if, remote_bda, TRUE); +} +/******************************************************************************* +** +** Function bta_hh_le_fill_16bits_gatt_id +** +** Description Utility function to fill a GATT ID strucure +** +*******************************************************************************/ +void bta_hh_le_fill_16bits_gatt_id(UINT8 inst_id, UINT16 uuid, tBTA_GATT_ID *p_output) +{ + p_output->inst_id = inst_id; + p_output->uuid.len = LEN_UUID_16; + p_output->uuid.uu.uuid16 = uuid; +} +/******************************************************************************* +** +** Function bta_hh_le_fill_16bits_srvc_id +** +** Description Utility function to fill a service ID strucure with a 16 bits +** service UUID. +** +*******************************************************************************/ +void bta_hh_le_fill_16bits_srvc_id(BOOLEAN is_pri, UINT8 inst_id, UINT16 srvc_uuid, + tBTA_GATT_SRVC_ID *p_output) +{ + memset((void *)p_output, 0, sizeof(tBTA_GATT_SRVC_ID)); + p_output->is_primary = is_pri; + bta_hh_le_fill_16bits_gatt_id(inst_id, srvc_uuid, &p_output->id); + +} +/******************************************************************************* +** +** Function bta_hh_le_fill_16bits_char_id +** +** Description Utility function to fill a char ID strucure with a 16 bits +** char UUID. +** +*******************************************************************************/ +void bta_hh_le_fill_16bits_char_id(UINT8 inst_id, UINT16 char_uuid, + tBTA_GATT_ID *p_output) +{ + memset((void *)p_output, 0, sizeof(tBTA_GATT_ID)); + bta_hh_le_fill_16bits_gatt_id(inst_id, char_uuid, p_output); +} +/******************************************************************************* +** +** Function bta_hh_le_find_dev_cb_by_conn_id +** +** Description Utility function find a device control block by connection ID. +** +*******************************************************************************/ +tBTA_HH_DEV_CB * bta_hh_le_find_dev_cb_by_conn_id(UINT16 conn_id) +{ + UINT8 i; + tBTA_HH_DEV_CB *p_dev_cb = &bta_hh_cb.kdev[0]; + + for (i = 0; i < BTA_HH_MAX_DEVICE; i ++, p_dev_cb ++) + { + if (p_dev_cb->in_use && p_dev_cb->conn_id == conn_id) + return p_dev_cb; + } + return NULL; +} +/******************************************************************************* +** +** Function bta_hh_le_find_dev_cb_by_bda +** +** Description Utility function find a device control block by BD address. +** +*******************************************************************************/ +tBTA_HH_DEV_CB * bta_hh_le_find_dev_cb_by_bda(BD_ADDR bda) +{ + UINT8 i; + tBTA_HH_DEV_CB *p_dev_cb = &bta_hh_cb.kdev[0]; + + for (i = 0; i < BTA_HH_MAX_DEVICE; i ++, p_dev_cb ++) + { + if (p_dev_cb->in_use && + memcmp(p_dev_cb->addr, bda, BD_ADDR_LEN) == 0) + return p_dev_cb; + } + return NULL; +} +UINT8 bta_hh_le_find_service_inst_by_battery_inst_id(tBTA_HH_DEV_CB *p_cb, UINT8 ba_inst_id) +{ + UINT8 i; + + for (i = 0; i < BTA_HH_LE_HID_SRVC_MAX; i ++) + { + if (p_cb->hid_srvc[i].in_use && + p_cb->hid_srvc[i].incl_srvc_inst == ba_inst_id) + { + return i; + } + } + return BTA_HH_IDX_INVALID; +} +/******************************************************************************* +** +** Function bta_hh_le_find_report_entry +** +** Description find the report entry by service instance and report UUID and +** instance ID +** +*******************************************************************************/ +tBTA_HH_LE_RPT * bta_hh_le_find_report_entry(tBTA_HH_DEV_CB *p_cb, + UINT8 srvc_inst_id, /* service instance ID */ + UINT16 rpt_uuid, + UINT8 char_inst_id) +{ + UINT8 i; + UINT8 hid_inst_id = srvc_inst_id; + tBTA_HH_LE_RPT *p_rpt; + + if (rpt_uuid == GATT_UUID_BATTERY_LEVEL) + { + hid_inst_id = bta_hh_le_find_service_inst_by_battery_inst_id(p_cb, srvc_inst_id); + + if (hid_inst_id == BTA_HH_IDX_INVALID) + return NULL; + } + + p_rpt = &p_cb->hid_srvc[hid_inst_id].report[0]; + + for (i = 0; i < BTA_HH_LE_RPT_MAX; i ++, p_rpt ++) + { + if (p_rpt->uuid == rpt_uuid && + p_rpt->inst_id == BTA_HH_LE_RPT_INST_ID_MAP(srvc_inst_id, char_inst_id)) + { + + return p_rpt; + } + } + return NULL; + +} +/******************************************************************************* +** +** Function bta_hh_le_find_rpt_by_idtype +** +** Description find a report entry by report ID and protocol mode +** +** Returns void +** +*******************************************************************************/ +tBTA_HH_LE_RPT * bta_hh_le_find_rpt_by_idtype(tBTA_HH_LE_RPT*p_head, UINT8 mode, + tBTA_HH_RPT_TYPE r_type, UINT8 rpt_id) +{ + tBTA_HH_LE_RPT *p_rpt = p_head; + UINT8 i; + +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG2("bta_hh_le_find_rpt_by_idtype: r_tpye: %d rpt_id: %d", r_type, rpt_id); +#endif + + for (i = 0 ; i < BTA_HH_LE_RPT_MAX; i ++, p_rpt++) + { + if (p_rpt->in_use && p_rpt->rpt_id == rpt_id && r_type == p_rpt->rpt_type) + { + /* return battery report w/o condition */ + if (p_rpt->uuid == GATT_UUID_BATTERY_LEVEL) + return p_rpt; + + if (mode == BTA_HH_PROTO_RPT_MODE && p_rpt->uuid == GATT_UUID_HID_REPORT) + return p_rpt; + + if ( mode ==BTA_HH_PROTO_BOOT_MODE && + (p_rpt->uuid >= GATT_UUID_HID_BT_KB_INPUT && p_rpt->uuid <= GATT_UUID_HID_BT_MOUSE_INPUT)) + return p_rpt; + } + } + return NULL; +} +/******************************************************************************* +** +** Function bta_hh_le_find_alloc_report_entry +** +** Description find or allocate a report entry in the HID service report list. +** +*******************************************************************************/ +tBTA_HH_LE_RPT * bta_hh_le_find_alloc_report_entry(tBTA_HH_DEV_CB *p_cb, + UINT8 srvc_inst_id, + UINT16 rpt_uuid, + UINT8 inst_id, + UINT8 prop) +{ + UINT8 i, hid_inst_id = srvc_inst_id; + tBTA_HH_LE_RPT *p_rpt; + + if (rpt_uuid == GATT_UUID_BATTERY_LEVEL) + { + hid_inst_id = bta_hh_le_find_service_inst_by_battery_inst_id(p_cb, srvc_inst_id); + + if (hid_inst_id == BTA_HH_IDX_INVALID) + return NULL; + } + p_rpt = &p_cb->hid_srvc[hid_inst_id].report[0]; + + for (i = 0; i < BTA_HH_LE_RPT_MAX; i ++, p_rpt ++) + { + if (!p_rpt->in_use || + (p_rpt->uuid == rpt_uuid && + p_rpt->inst_id == BTA_HH_LE_RPT_INST_ID_MAP(srvc_inst_id, inst_id))) + { + if (!p_rpt->in_use) + { + p_rpt->in_use = TRUE; + p_rpt->index = i; + p_rpt->inst_id = BTA_HH_LE_RPT_INST_ID_MAP(srvc_inst_id, inst_id); + p_rpt->prop = prop; + p_rpt->uuid = rpt_uuid; + + /* assign report type */ + for (i = 0; i < BTA_LE_HID_RTP_UUID_MAX; i ++) + { + if (bta_hh_uuid_to_rtp_type[i][0] == rpt_uuid) + { + p_rpt->rpt_type = (tBTA_HH_RPT_TYPE)bta_hh_uuid_to_rtp_type[i][1]; + + if (rpt_uuid == GATT_UUID_HID_BT_KB_INPUT || rpt_uuid == GATT_UUID_HID_BT_KB_OUTPUT) + p_rpt->rpt_id = BTA_HH_KEYBD_RPT_ID; + + if (rpt_uuid == GATT_UUID_HID_BT_MOUSE_INPUT) + p_rpt->rpt_id = BTA_HH_MOUSE_RPT_ID; + + break; + } + } + } + + return p_rpt; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function bta_hh_le_read_char_dscrpt +** +** Description read cahracteristic descriptor +** +*******************************************************************************/ +tBTA_HH_STATUS bta_hh_le_read_char_dscrpt(tBTA_HH_DEV_CB *p_cb, UINT16 srvc_uuid, UINT8 srvc_inst_id, + UINT16 char_uuid, UINT8 char_inst_id, UINT16 char_descp_uuid) +{ + tBTA_GATTC_CHAR_ID char_id; + tBT_UUID descr_uuid; + tBTA_GATTC_CHAR_DESCR_ID descr_id; + tBTA_HH_STATUS status = BTA_HH_ERR; + + bta_hh_le_fill_16bits_srvc_id(TRUE, srvc_inst_id, srvc_uuid, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(char_inst_id, char_uuid, &char_id.char_id); + + descr_uuid.len = LEN_UUID_16; + descr_uuid.uu.uuid16 = char_descp_uuid; + + /* find the report reference descriptor */ + if (BTA_GATTC_GetFirstCharDescr(p_cb->conn_id, + &char_id, + &descr_uuid, + &descr_id) == BTA_GATT_OK) + { + BTA_GATTC_ReadCharDescr(p_cb->conn_id, + &descr_id, + BTA_GATT_AUTH_REQ_NONE); + + status = BTA_HH_OK; + } + else + { +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_ERROR2("bta_hh_le_read_char_dscrpt: No such descrpt exists: %s(0x%04x)", + bta_hh_uuid_to_str(char_descp_uuid), char_descp_uuid); +#endif + } + return status; +} +/******************************************************************************* +** +** Function bta_hh_le_read_rpt_ref_descr +** +** Description read report refernece descriptors in service discovery process +** +*******************************************************************************/ +void bta_hh_le_read_rpt_ref_descr(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_LE_RPT *p_rpt) +{ + BOOLEAN started = FALSE; + UINT16 srvc_uuid, char_uuid; + + while (p_rpt != NULL) + { + if (p_rpt->rpt_type == BTA_HH_RPTT_INPUT) + { + /* is battery report */ + if (p_rpt->uuid == GATT_UUID_BATTERY_LEVEL) + { +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG0("read battery level report reference descriptor"); +#endif + srvc_uuid = UUID_SERVCLASS_BATTERY; + char_uuid = GATT_UUID_BATTERY_LEVEL; + } + else + { +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG0("read HID report reference descriptor"); +#endif + srvc_uuid = UUID_SERVCLASS_LE_HID; + char_uuid = GATT_UUID_HID_REPORT; + } + + if (bta_hh_le_read_char_dscrpt(p_dev_cb, + srvc_uuid, + BTA_HH_LE_RPT_GET_SRVC_INST_ID(p_rpt->inst_id), + char_uuid, + BTA_HH_LE_RPT_GET_RPT_INST_ID(p_rpt->inst_id), + GATT_UUID_RPT_REF_DESCR) + == BTA_HH_OK) + { + started = TRUE; + break; + } + } + else + break; + + if (p_rpt->index == BTA_HH_LE_RPT_MAX - 1) + break; + + p_rpt ++; + } + + + /* if no report reference descriptor */ + if (!started) + { + /* explore next char */ + bta_hh_le_search_hid_chars(p_dev_cb); + } +} +/******************************************************************************* +** +** Function bta_hh_le_save_rpt_ref +** +** Description save report reference information and move to next one. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_save_rpt_ref(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_LE_RPT *p_rpt, + tBTA_GATTC_READ *p_data) +{ + UINT8 *pp; + + /* if the length of the descriptor value is right, parse it */ + if (p_data->status == BTA_GATT_OK && + p_data->p_value && p_data->p_value->unformat.len == 2) + { + pp = p_data->p_value->unformat.p_value; + + STREAM_TO_UINT8(p_rpt->rpt_id, pp); + STREAM_TO_UINT8(p_rpt->rpt_type, pp); + + if (p_rpt->rpt_type > BTA_HH_RPTT_FEATURE) /* invalid report type */ + p_rpt->rpt_type = BTA_HH_RPTT_RESRV; + +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG1("report ID: %d", p_rpt->rpt_id); +#endif + } + + if (p_rpt->index < BTA_HH_LE_RPT_MAX) + p_rpt ++; + + /* read next report reference descriptor */ + bta_hh_le_read_rpt_ref_descr(p_dev_cb, p_rpt); + +} +/******************************************************************************* +** +** Function bta_hh_le_save_rpt_ref +** +** Description save report reference information and move to next one. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_save_ext_rpt_ref(tBTA_HH_DEV_CB *p_dev_cb, + tBTA_GATTC_READ *p_data) +{ + UINT8 *pp; + + /* if the length of the descriptor value is right, parse it + assume it's a 16 bits UUID */ + if (p_data->status == BTA_GATT_OK && + p_data->p_value && p_data->p_value->unformat.len == 2) + { + pp = p_data->p_value->unformat.p_value; + STREAM_TO_UINT16(p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].ext_rpt_ref, pp); + +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG1("External Report Reference UUID 0x%04x", + p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].ext_rpt_ref); +#endif + } + bta_hh_le_search_hid_chars(p_dev_cb); + +} +/******************************************************************************* +** +** Function bta_hh_le_register_input_notif +** +** Description Register for all notifications for the report applicable +** for the protocol mode. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_register_input_notif(tBTA_HH_DEV_CB *p_dev_cb, UINT8 srvc_inst, + UINT8 proto_mode, BOOLEAN register_ba) +{ + tBTA_HH_LE_RPT *p_rpt = &p_dev_cb->hid_srvc[srvc_inst].report[0]; + tBTA_GATTC_CHAR_ID char_id; + UINT8 i; + UINT16 srvc_uuid; + +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG1("bta_hh_le_register_input_notif mode: %d", proto_mode); +#endif + + for (i = 0; i < BTA_HH_LE_RPT_MAX; i ++, p_rpt ++) + { + if (p_rpt->rpt_type == BTA_HH_RPTT_INPUT) + { + if (p_rpt->uuid == GATT_UUID_BATTERY_LEVEL) + srvc_uuid = UUID_SERVCLASS_BATTERY; + else + srvc_uuid = UUID_SERVCLASS_LE_HID; + + bta_hh_le_fill_16bits_srvc_id(TRUE, BTA_HH_LE_RPT_GET_SRVC_INST_ID(p_rpt->inst_id), srvc_uuid, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(BTA_HH_LE_RPT_GET_RPT_INST_ID(p_rpt->inst_id), p_rpt->uuid, &char_id.char_id); + + if (register_ba && p_rpt->uuid == GATT_UUID_BATTERY_LEVEL) + { + BTA_GATTC_RegisterForNotifications(bta_hh_cb.gatt_if, + p_dev_cb->addr, + &char_id); + } + /* boot mode, deregister report input notification */ + else if (proto_mode == BTA_HH_PROTO_BOOT_MODE) + { + if (p_rpt->uuid == GATT_UUID_HID_REPORT && + p_rpt->client_cfg_value == BTA_GATT_CLT_CONFIG_NOTIFICATION) + { + APPL_TRACE_DEBUG1("---> Deregister Report ID: %d", p_rpt->rpt_id); + BTA_GATTC_DeregisterForNotifications(bta_hh_cb.gatt_if, + p_dev_cb->addr, + &char_id); + } + /* register boot reports notification */ + else if (p_rpt->uuid == GATT_UUID_HID_BT_KB_INPUT || + p_rpt->uuid == GATT_UUID_HID_BT_MOUSE_INPUT) + { + APPL_TRACE_DEBUG1("<--- Register Boot Report ID: %d", p_rpt->rpt_id); + BTA_GATTC_RegisterForNotifications(bta_hh_cb.gatt_if, + p_dev_cb->addr, + &char_id); + } + } + else if (proto_mode == BTA_HH_PROTO_RPT_MODE) + { + if ((p_rpt->uuid == GATT_UUID_HID_BT_KB_INPUT || + p_rpt->uuid == GATT_UUID_HID_BT_MOUSE_INPUT) && + p_rpt->client_cfg_value == BTA_GATT_CLT_CONFIG_NOTIFICATION) + { + + APPL_TRACE_DEBUG1("---> Deregister Boot Report ID: %d", p_rpt->rpt_id); + BTA_GATTC_DeregisterForNotifications(bta_hh_cb.gatt_if, + p_dev_cb->addr, + &char_id); + } + else if (p_rpt->uuid == GATT_UUID_HID_REPORT && + p_rpt->client_cfg_value == BTA_GATT_CLT_CONFIG_NOTIFICATION) + { + APPL_TRACE_DEBUG1("<--- Register Report ID: %d", p_rpt->rpt_id); + BTA_GATTC_RegisterForNotifications(bta_hh_cb.gatt_if, + p_dev_cb->addr, + &char_id); + } + } + /* + else unknow protocol mode */ + } + } +} + +/******************************************************************************* +** +** Function bta_hh_le_open_cmpl +** +** Description HID over GATT connection sucessfully opened +** +*******************************************************************************/ +void bta_hh_le_open_cmpl(tBTA_HH_DEV_CB *p_cb) +{ + if ( p_cb->disc_active == BTA_HH_LE_DISC_NONE) + { +#if BTA_HH_DEBUG + bta_hh_le_hid_report_dbg(p_cb); +#endif + bta_hh_le_register_input_notif(p_cb, 0, p_cb->mode, TRUE); + bta_hh_sm_execute(p_cb, BTA_HH_OPEN_CMPL_EVT, NULL); +#if (BTA_HH_LE_RECONN == TRUE) + if (p_cb->status == BTA_HH_OK) + { + bta_hh_le_add_dev_bg_conn(p_cb, TRUE); + } +#endif + } +} +/******************************************************************************* +** +** Function bta_hh_le_write_char_clt_cfg +** +** Description Utility function to find and write client configuration of +** a characteristic +** +*******************************************************************************/ +BOOLEAN bta_hh_le_write_char_clt_cfg(tBTA_HH_DEV_CB *p_cb, + UINT8 srvc_inst_id, UINT16 srvc_uuid16, + UINT8 char_inst_id, UINT16 char_uuid16, + UINT16 clt_cfg_value) +{ + tBTA_GATTC_CHAR_ID char_id; + tBT_UUID descr_cond; + tBTA_GATTC_CHAR_DESCR_ID descr_id; + tBTA_GATT_UNFMT value; + UINT8 buf[2], *pp = buf; + + bta_hh_le_fill_16bits_srvc_id(TRUE, srvc_inst_id, srvc_uuid16, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(char_inst_id, char_uuid16, &char_id.char_id); + + descr_cond.len = LEN_UUID_16; + descr_cond.uu.uuid16 = GATT_UUID_CHAR_CLIENT_CONFIG; + + value.len = 2; + value.p_value = buf; + + UINT16_TO_STREAM(pp, clt_cfg_value); + + if (BTA_GATTC_GetFirstCharDescr(p_cb->conn_id, + &char_id, + &descr_cond, + &descr_id) == BTA_GATT_OK) + { + BTA_GATTC_WriteCharDescr(p_cb->conn_id, + &descr_id, + BTA_GATTC_TYPE_WRITE_NO_RSP, + &value, + BTA_GATT_AUTH_REQ_NONE); + + return TRUE; + } + return FALSE; +} +/******************************************************************************* +** +** Function bta_hh_le_write_rpt_clt_cfg +** +** Description write client configuration. This is only for input report +** enable all input notification upon connection open. +** +*******************************************************************************/ +BOOLEAN bta_hh_le_write_rpt_clt_cfg(tBTA_HH_DEV_CB *p_cb, UINT8 srvc_inst_id) +{ + UINT8 i; + tBTA_HH_LE_RPT *p_rpt = &p_cb->hid_srvc[srvc_inst_id].report[p_cb->clt_cfg_idx]; + UINT16 srvc_uuid; + + for (i = p_cb->clt_cfg_idx; i < BTA_HH_LE_RPT_MAX && p_rpt->in_use; i ++, p_rpt ++) + { + /* enable notification for all input report, regardless mode */ + if (p_rpt->rpt_type == BTA_HH_RPTT_INPUT) + + { + if (p_rpt->uuid == GATT_UUID_BATTERY_LEVEL) + srvc_uuid = UUID_SERVCLASS_BATTERY; + else + srvc_uuid = UUID_SERVCLASS_LE_HID; + + if (bta_hh_le_write_char_clt_cfg(p_cb, + BTA_HH_LE_RPT_GET_SRVC_INST_ID(p_rpt->inst_id), + srvc_uuid, + BTA_HH_LE_RPT_GET_RPT_INST_ID(p_rpt->inst_id), + p_rpt->uuid, + BTA_GATT_CLT_CONFIG_NOTIFICATION)) + { + p_cb->clt_cfg_idx = i; + return TRUE; + } + } + + } + p_cb->clt_cfg_idx = 0; + + /* client configuration is completed, send open callback */ + if (p_cb->state == BTA_HH_W4_CONN_ST) + { + p_cb->disc_active &= ~BTA_HH_LE_DISC_HIDS; + + /* discover scan parameter profile is act as report host */ + bta_hh_le_search_scps(p_cb); + } + return FALSE; +} + +/******************************************************************************* +** +** Function bta_hh_le_set_protocol_mode +** +** Description Set remote device protocol mode. +** +*******************************************************************************/ +BOOLEAN bta_hh_le_set_protocol_mode(tBTA_HH_DEV_CB *p_cb, tBTA_HH_PROTO_MODE mode) +{ + tBTA_GATTC_CHAR_ID char_id; + tBTA_HH_CBDATA cback_data ; + BOOLEAN exec = TRUE; + + APPL_TRACE_DEBUG1("bta_hh_le_set_protocol_mode attempt mode: %s", + (mode == BTA_HH_PROTO_RPT_MODE)? "Report": "Boot"); + + cback_data.handle = p_cb->hid_handle; + /* boot mode is not supported in the remote device */ + if ((p_cb->hid_srvc[BTA_HH_LE_SRVC_DEF].option_char & BTA_HH_LE_PROTO_MODE_BIT) == 0) + { + exec = FALSE; + p_cb->mode = BTA_HH_PROTO_RPT_MODE; + + if (mode == BTA_HH_PROTO_BOOT_MODE) + { + APPL_TRACE_ERROR0("Set Boot Mode failed!! No PROTO_MODE Char!"); + cback_data.status = BTA_HH_ERR; + } + else + { + /* if set to report mode, need to de-register all input report notification */ + bta_hh_le_register_input_notif(p_cb, 0, p_cb->mode, FALSE); + cback_data.status = BTA_HH_OK; + } + if (p_cb->state == BTA_HH_W4_CONN_ST) + { + p_cb->status = (cback_data.status == BTA_HH_OK)? BTA_HH_OK: BTA_HH_ERR_PROTO; + } + else + (* bta_hh_cb.p_cback)(BTA_HH_SET_PROTO_EVT, (tBTA_HH *)&cback_data); + } + else + { + bta_hh_le_fill_16bits_srvc_id(TRUE, 0, UUID_SERVCLASS_LE_HID, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(0, GATT_UUID_HID_PROTO_MODE, &char_id.char_id); + + p_cb->mode = mode; + mode = (mode == BTA_HH_PROTO_BOOT_MODE)? BTA_HH_LE_PROTO_BOOT_MODE : BTA_HH_LE_PROTO_REPORT_MODE; + + BTA_GATTC_WriteCharValue(p_cb->conn_id, + &char_id, + BTA_GATTC_TYPE_WRITE_NO_RSP, + 1, + &mode, + BTA_GATT_AUTH_REQ_NONE); + } + return exec; +} + +/******************************************************************************* +** +** Function bta_hh_le_get_protocol_mode +** +** Description Get remote device protocol mode. +** +*******************************************************************************/ +void bta_hh_le_get_protocol_mode(tBTA_HH_DEV_CB *p_cb) +{ + tBTA_GATTC_CHAR_ID char_id; + tBTA_HH_HSDATA hs_data; + UINT8 i; + + p_cb->w4_evt = BTA_HH_GET_PROTO_EVT; + + for (i = 0; i< BTA_HH_LE_HID_SRVC_MAX; i ++) + { + if (p_cb->hid_srvc[i].in_use && + p_cb->hid_srvc[i].option_char & BTA_HH_LE_PROTO_MODE_BIT) + { + bta_hh_le_fill_16bits_srvc_id(TRUE, 0, UUID_SERVCLASS_LE_HID, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(0, GATT_UUID_HID_PROTO_MODE, &char_id.char_id); + + BTA_GATTC_ReadCharacteristic(p_cb->conn_id, + &char_id, + BTA_GATT_AUTH_REQ_NONE); + break; + } + } + /* no service support protocol_mode, by default report mode */ + if (i == BTA_HH_LE_HID_SRVC_MAX) + { + hs_data.status = BTA_HH_OK; + hs_data.handle = p_cb->hid_handle; + hs_data.rsp_data.proto_mode = BTA_HH_PROTO_RPT_MODE; + p_cb->w4_evt = 0; + (* bta_hh_cb.p_cback)(BTA_HH_GET_PROTO_EVT, (tBTA_HH *)&hs_data); + } + +} +/******************************************************************************* +** +** Function bta_hh_le_expl_rpt +** +** Description explore all report characteristic +** +*******************************************************************************/ +void bta_hh_le_expl_rpt(tBTA_HH_DEV_CB *p_dev_cb, + tBTA_GATTC_CHAR_ID *p_char_id, + tBT_UUID *p_char_cond, + tBTA_GATT_CHAR_PROP prop) +{ + tBTA_GATTC_CHAR_ID char_result; + + do + { + if (bta_hh_le_find_alloc_report_entry(p_dev_cb, + p_dev_cb->cur_srvc_index, + GATT_UUID_HID_REPORT, + p_char_id->char_id.inst_id, + prop) == NULL) + { + APPL_TRACE_ERROR0("Add report entry failed !!!") + break; + } + + APPL_TRACE_DEBUG0("Find more REPORT"); + + if (BTA_GATTC_GetNextChar(p_dev_cb->conn_id, + p_char_id, + p_char_cond, + &char_result, + &prop) != BTA_GATT_OK) + break; + + p_char_id = &char_result; + } + while (1); + + APPL_TRACE_ERROR0("all report searched"); + bta_hh_le_read_rpt_ref_descr(p_dev_cb, + &p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].report[0]); + + + return ; +} +/******************************************************************************* +** +** Function bta_hh_le_expl_boot_rpt +** +** Description explore boot report +** +*******************************************************************************/ +void bta_hh_le_expl_boot_rpt(tBTA_HH_DEV_CB *p_dev_cb, UINT16 char_uuid, + tBTA_GATT_CHAR_PROP prop) +{ + if (bta_hh_le_find_alloc_report_entry(p_dev_cb, + p_dev_cb->cur_srvc_index, + char_uuid, + 0, + prop) == NULL) + + { + APPL_TRACE_ERROR0("Add report entry failed !!!") + } + + return; +} +/******************************************************************************* +** +** Function bta_hh_le_dis_cback +** +** Description DIS read complete callback +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_dis_cback(BD_ADDR addr, tDIS_VALUE *p_dis_value) +{ + tBTA_HH_DEV_CB *p_cb = bta_hh_le_find_dev_cb_by_bda(addr); + + + if (p_cb == NULL || p_dis_value == NULL) + { + APPL_TRACE_ERROR0("received unexpected/error DIS callback"); + return; + } + + p_cb->disc_active &= ~BTA_HH_LE_DISC_DIS; + /* plug in the PnP info for this device */ + if (p_dis_value->attr_mask & DIS_ATTR_PNP_ID_BIT) + { +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG3("Plug in PnP info: product_id = %02x, vendor_id = %04x, version = %04x", + p_dis_value->pnp_id.product_id, + p_dis_value->pnp_id.vendor_id, + p_dis_value->pnp_id.product_version); +#endif + p_cb->dscp_info.product_id = p_dis_value->pnp_id.product_id; + p_cb->dscp_info.vendor_id = p_dis_value->pnp_id.vendor_id; + p_cb->dscp_info.version = p_dis_value->pnp_id.product_version; + } + bta_hh_le_open_cmpl(p_cb); +} +/******************************************************************************* +** +** Function bta_hh_le_pri_service_discovery +** +** Description Initialize GATT discovery on the remote LE HID device by opening +** a GATT connection first. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_pri_service_discovery(tBTA_HH_DEV_CB *p_cb) +{ + tBT_UUID pri_srvc; + + p_cb->disc_active |= (BTA_HH_LE_DISC_HIDS|BTA_HH_LE_DISC_DIS); + + /* read DIS info */ + if (!DIS_ReadDISInfo(p_cb->addr, bta_hh_le_dis_cback)) + { + APPL_TRACE_ERROR0("read DIS failed"); + p_cb->disc_active &= ~BTA_HH_LE_DISC_DIS; + } + + /* in parallel */ + /* start primary service discovery for HID service */ + pri_srvc.len = LEN_UUID_16; + pri_srvc.uu.uuid16 = UUID_SERVCLASS_LE_HID; + BTA_GATTC_ServiceSearchRequest(p_cb->conn_id, &pri_srvc); + return; +} +/******************************************************************************* +** +** Function bta_hh_le_encrypt_cback +** +** Description link encryption complete callback for bond verification. +** +** Returns None +** +*******************************************************************************/ +void bta_hh_le_encrypt_cback(BD_ADDR bd_addr, void *p_ref_data, tBTM_STATUS result) +{ + UINT8 idx = bta_hh_find_cb(bd_addr); + tBTA_HH_DEV_CB *p_dev_cb; + + APPL_TRACE_ERROR0("bta_hh_le_encrypt_cback"); + + if (idx != BTA_HH_IDX_INVALID) + p_dev_cb = &bta_hh_cb.kdev[idx]; + else + { + APPL_TRACE_ERROR0("unexpected encryption callback, ignore"); + return; + } + p_dev_cb->status = (result == BTM_SUCCESS) ? BTA_HH_OK : BTA_HH_ERR_SEC; + + bta_hh_sm_execute(p_dev_cb, BTA_HH_ENC_CMPL_EVT, NULL); +} +/******************************************************************************* +** +** Function bta_hh_security_cmpl +** +** Description Security check completed, start the service discovery +** if no cache available, otherwise report connection open completed +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_security_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf) +{ + if (p_cb->status == BTA_HH_OK) + { + /* discovery has been done for HID service */ + if (p_cb->app_id != 0 && bta_hh_le_hid_srvc_cached(p_cb)) + { + /* configure protocol mode */ + if (bta_hh_le_set_protocol_mode(p_cb, p_cb->mode) == FALSE) + { + APPL_TRACE_ERROR0("bta_hh_security_cmpl"); + bta_hh_le_open_cmpl(p_cb); + } + } + /* start primary service discovery for HID service */ + else + { + bta_hh_le_pri_service_discovery(p_cb); + } + } + else + bta_hh_le_api_disc_act(p_cb); + +} +/******************************************************************************* +** +** Function bta_hh_start_security +** +** Description start the security check of the established connection +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_start_security(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf) +{ + UINT8 sec_flag=0; + + /* verify bond */ + BTM_GetSecurityFlags(p_cb->addr, &sec_flag); + + /* if link has been encrypted */ + if (sec_flag & BTM_SEC_FLAG_ENCRYPTED) + { + bta_hh_sm_execute(p_cb, BTA_HH_ENC_CMPL_EVT, NULL); + } + /* if bonded and link not encrypted */ + else if (sec_flag & BTM_SEC_FLAG_LKEY_KNOWN) + { + sec_flag = BTM_BLE_SEC_ENCRYPT; + p_cb->status = BTA_HH_ERR_AUTH_FAILED; + BTM_SetEncryption(p_cb->addr, bta_hh_le_encrypt_cback, &sec_flag); + } + /* unbonded device, report security error here */ + else if (p_cb->sec_mask != BTA_SEC_NONE) + { + sec_flag = BTM_BLE_SEC_ENCRYPT_NO_MITM; + p_cb->status = BTA_HH_ERR_AUTH_FAILED; + BTM_SetEncryption(p_cb->addr, bta_hh_le_encrypt_cback, &sec_flag); + } + /* otherwise let it go through */ + else + { + bta_hh_sm_execute(p_cb, BTA_HH_ENC_CMPL_EVT, NULL); + } + + +} +/******************************************************************************* +** +** Function bta_hh_gatt_open +** +** Description process GATT open event. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_gatt_open(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf) +{ + tBTA_GATTC_OPEN *p_data = &p_buf->le_open; + UINT8 *p2; + tHID_STATUS status = BTA_HH_ERR; + + /* if received invalid callback data , ignore it */ + if (p_cb == NULL || p_data == NULL) + return; + + p2 = p_data->remote_bda; + + APPL_TRACE_DEBUG3("bta_hh_gatt_open BTA_GATTC_OPEN_EVT bda= [%08x%04x] status =%d", + ((p2[0])<<24)+((p2[1])<<16)+((p2[2])<<8)+(p2[3]), + ((p2[4])<<8)+ p2[5],p_data->status); + + if (p_data->status == BTA_GATT_OK) + { + + p_cb->in_use = TRUE; + p_cb->conn_id = p_data->conn_id; + p_cb->hid_handle = BTA_HH_GET_LE_DEV_HDL(p_cb->index); + + bta_hh_cb.le_cb_index[BTA_HH_GET_LE_CB_IDX(p_cb->hid_handle)] = p_cb->index; + +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG3("hid_handle = %2x conn_id = %04x cb_index = %d", p_cb->hid_handle, p_cb->conn_id, p_cb->index); +#endif + + bta_hh_sm_execute(p_cb, BTA_HH_START_ENC_EVT, NULL); + + } + else /* open failure */ + { + bta_hh_sm_execute(p_cb, BTA_HH_SDP_CMPL_EVT, (tBTA_HH_DATA *)&status); + } + +} +/******************************************************************************* +** +** Function bta_hh_le_close +** +** Description This function process the GATT close event and post it as a +** BTA HH internal event +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_close(tBTA_GATTC_CLOSE * p_data) +{ + tBTA_HH_DEV_CB *p_dev_cb = bta_hh_le_find_dev_cb_by_bda(p_data->remote_bda); + tBTA_HH_LE_CLOSE *p_buf = NULL; + UINT16 sm_event = BTA_HH_GATT_CLOSE_EVT; + + if (p_dev_cb != NULL && + (p_buf = (tBTA_HH_LE_CLOSE *)GKI_getbuf(sizeof(tBTA_HH_LE_CLOSE))) != NULL) + { + p_buf->hdr.event = sm_event; + p_buf->hdr.layer_specific = (UINT16)p_dev_cb->hid_handle; + p_buf->conn_id = p_data->conn_id; + p_buf->reason = p_data->reason; + + p_dev_cb->conn_id = BTA_GATT_INVALID_CONN_ID; + + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* +** +** Function bta_hh_le_search_result +** +** Description This function process the GATT service search result. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_search_result(tBTA_GATTC_SRVC_RES *p_srvc_result) +{ + tBTA_HH_DEV_CB *p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_srvc_result->conn_id); + + if (p_dev_cb != NULL) + { + switch (p_srvc_result->service_uuid.id.uuid.uu.uuid16) + { + case UUID_SERVCLASS_LE_HID: + if (p_srvc_result->service_uuid.is_primary) + { + /* found HID primamry service */ + /* TODO: proceed to find battery and device info */ + if (bta_hh_le_add_hid_srvc_entry(p_dev_cb, p_dev_cb->total_srvc)) + p_dev_cb->total_srvc ++; + APPL_TRACE_DEBUG1("num of hid service: %d", p_dev_cb->total_srvc); + } + break; + + case UUID_SERVCLASS_SCAN_PARAM : /* scan parameter service */ + bta_hh_le_search_scps_chars(p_dev_cb); + break; + } + + } + +} + + +/******************************************************************************* +** +** Function bta_hh_le_gatt_disc_cmpl +** +** Description Check to see if the remote device is a LE only device +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_gatt_disc_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_STATUS status) +{ + APPL_TRACE_DEBUG0("bta_hh_le_gatt_disc_cmpl "); + + /* if open sucessful or protocol mode not desired, keep the connection open but inform app */ + if (status == BTA_HH_OK || status == BTA_HH_ERR_PROTO) + { + /* assign a special APP ID temp, since device type unknown */ + p_cb->app_id = BTA_HH_APP_ID_LE; + + /* set report notification configuration */ + p_cb->clt_cfg_idx = 0; + bta_hh_le_write_rpt_clt_cfg(p_cb, BTA_HH_LE_SRVC_DEF); + } + else /* error, close the GATT connection */ + { + /* close GATT connection if it's on */ + bta_hh_le_api_disc_act(p_cb); + } +} + +/******************************************************************************* +** +** Function bta_hh_le_srvc_expl_srvc +** +** Description This function discover the next avaible HID service. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_srvc_expl_srvc(tBTA_HH_DEV_CB *p_dev_cb) +{ +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG2("bta_hh_le_srvc_expl_srvc cur_srvc_index = %d in_use = %d", + p_dev_cb->cur_srvc_index, + p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].in_use); +#endif + + if (p_dev_cb->cur_srvc_index < BTA_HH_LE_HID_SRVC_MAX && + p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].in_use) + { + if (!p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].expl_incl_srvc) + /* explore included service first */ + { + bta_hh_le_search_hid_included(p_dev_cb); + } + else + { + /* explore characterisc */ + p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx = 0; + bta_hh_le_search_hid_chars(p_dev_cb); + } + } + else /* all service discvery finished */ + { + bta_hh_le_gatt_disc_cmpl(p_dev_cb, p_dev_cb->status); + } +} +/******************************************************************************* +** +** Function bta_hh_le_srvc_search_cmpl +** +** Description This function process the GATT service search complete. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_srvc_search_cmpl(tBTA_GATTC_SEARCH_CMPL *p_data) +{ + tBTA_HH_DEV_CB *p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_data->conn_id); + + /* service search exception or no HID service is supported on remote */ + if (p_dev_cb == NULL) + return; + + if(p_data->status != BTA_GATT_OK || p_dev_cb->total_srvc == 0) + { + p_dev_cb->status = BTA_HH_ERR_SDP; + /* close the connection and report service discovery complete with error */ + bta_hh_le_api_disc_act(p_dev_cb); + } + /* GATT service discovery sucessfully finished */ + else + { + if (p_dev_cb->disc_active & BTA_HH_LE_DISC_SCPS) + { + p_dev_cb->disc_active &= ~BTA_HH_LE_DISC_SCPS; + bta_hh_le_open_cmpl(p_dev_cb); + } + else /* discover HID service */ + { + p_dev_cb->cur_srvc_index = 0; + bta_hh_le_srvc_expl_srvc(p_dev_cb); + } +} +} + +/******************************************************************************* +** +** Function bta_hh_le_search_hid_included +** +** Description This function search the included service within the HID service. +** +** Parameters: +** +*******************************************************************************/ +static void bta_hh_le_search_hid_included(tBTA_HH_DEV_CB *p_dev_cb) +{ + tBT_UUID srvc_cond, char_cond; + tBTA_GATTC_INCL_SVC_ID inc_srvc_result; + tBTA_GATT_SRVC_ID srvc_id; + tBTA_GATTC_CHAR_ID char_result; + tBTA_GATT_CHAR_PROP prop = 0; + + bta_hh_le_fill_16bits_srvc_id(TRUE, p_dev_cb->cur_srvc_index, UUID_SERVCLASS_LE_HID, &srvc_id); + + srvc_cond.len = LEN_UUID_16; + srvc_cond.uu.uuid16 = UUID_SERVCLASS_BATTERY; + + if (BTA_GATTC_GetFirstIncludedService(p_dev_cb->conn_id, + &srvc_id, + &srvc_cond, + &inc_srvc_result) == BTA_GATT_OK) + { + /* read include service UUID */ + + p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].incl_srvc_inst = inc_srvc_result.incl_svc_id.id.inst_id; + + char_cond.len = LEN_UUID_16; + char_cond.uu.uuid16 = GATT_UUID_BATTERY_LEVEL; + + /* find the battery characteristic */ + if (BTA_GATTC_GetFirstChar( p_dev_cb->conn_id, + &inc_srvc_result.incl_svc_id, + &char_cond, + &char_result, + &prop) == BTA_GATT_OK) + { + if (bta_hh_le_find_alloc_report_entry(p_dev_cb, + char_result.srvc_id.id.inst_id, + GATT_UUID_BATTERY_LEVEL, + char_result.char_id.inst_id, + prop) == NULL) + { + APPL_TRACE_ERROR0("Add battery report entry failed !!!") + } + + /* read the battery characteristic */ + BTA_GATTC_ReadCharacteristic(p_dev_cb->conn_id, + &char_result, + BTA_GATT_AUTH_REQ_NONE); + + return; + + } + else + { + APPL_TRACE_ERROR0("Remote device does not have battery level"); + } + } + + p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].expl_incl_srvc = TRUE; + + bta_hh_le_srvc_expl_srvc(p_dev_cb); + +} +/******************************************************************************* +** +** Function bta_hh_read_battery_level_cmpl +** +** Description This function process the battery level read +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_read_battery_level_cmpl(UINT8 status, tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATTC_READ *p_data) +{ + p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].expl_incl_srvc = TRUE; + bta_hh_le_srvc_expl_srvc(p_dev_cb); +} +/******************************************************************************* +** +** Function bta_hh_le_search_hid_chars +** +** Description This function discover all characteristics a service and +** all descriptors available. +** +** Parameters: +** +*******************************************************************************/ +static void bta_hh_le_search_hid_chars(tBTA_HH_DEV_CB *p_dev_cb) +{ + tBT_UUID char_cond; + tBTA_GATTC_CHAR_ID char_result; + tBTA_GATT_CHAR_PROP prop; + BOOLEAN next = TRUE; + UINT16 char_uuid = 0; + tBTA_GATT_SRVC_ID srvc_id; + + if (p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx == BTA_HH_LE_DISC_CHAR_NUM || + (p_dev_cb->status != BTA_HH_OK && p_dev_cb->status != BTA_HH_ERR_PROTO)) + { + p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx = 0; + /* explore next service */ + p_dev_cb->cur_srvc_index ++; + bta_hh_le_srvc_expl_srvc(p_dev_cb); + return; + } + + p_dev_cb->hid_srvc[ p_dev_cb->cur_srvc_index].cur_expl_char_idx ++; + char_uuid = bta_hh_le_disc_char_uuid[p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx - 1]; + + char_cond.len = LEN_UUID_16; + char_cond.uu.uuid16 = char_uuid; + + bta_hh_le_fill_16bits_srvc_id(TRUE, p_dev_cb->cur_srvc_index, UUID_SERVCLASS_LE_HID, &srvc_id); + +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG2("bta_hh_le_search_hid_chars: looking for %s(0x%04x)", + bta_hh_uuid_to_str(char_uuid), char_uuid); +#endif + + if (BTA_GATTC_GetFirstChar( p_dev_cb->conn_id, + &srvc_id, + &char_cond, + &char_result, + &prop) == BTA_GATT_OK) + { + switch (char_uuid) + { + case GATT_UUID_HID_CONTROL_POINT: + p_dev_cb->hid_srvc[char_result.srvc_id.id.inst_id].option_char |= BTA_HH_LE_CP_BIT; + next = TRUE; + break; + case GATT_UUID_HID_INFORMATION: + case GATT_UUID_HID_REPORT_MAP: + /* read the char value */ + BTA_GATTC_ReadCharacteristic(p_dev_cb->conn_id, + &char_result, + BTA_GATT_AUTH_REQ_NONE); + next = FALSE; + break; + + case GATT_UUID_HID_PROTO_MODE: + p_dev_cb->hid_srvc[char_result.srvc_id.id.inst_id].option_char |= BTA_HH_LE_PROTO_MODE_BIT; + next = !bta_hh_le_set_protocol_mode(p_dev_cb, p_dev_cb->mode); + break; + + case GATT_UUID_HID_REPORT: + bta_hh_le_expl_rpt(p_dev_cb, &char_result, &char_cond, prop); + next = FALSE; + break; + + /* found boot mode report types */ + case GATT_UUID_HID_BT_KB_OUTPUT: + case GATT_UUID_HID_BT_MOUSE_INPUT: + case GATT_UUID_HID_BT_KB_INPUT: + bta_hh_le_expl_boot_rpt(p_dev_cb, char_uuid, prop); + break; + } + } + else + { + if (char_uuid == GATT_UUID_HID_PROTO_MODE) + next = !bta_hh_le_set_protocol_mode(p_dev_cb, p_dev_cb->mode); + + } + + if (next == TRUE) + { + bta_hh_le_search_hid_chars(p_dev_cb); + } +} +/******************************************************************************* +** +** Function bta_hh_le_save_rpt_map +** +** Description save the report map into the control block. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_save_rpt_map(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATTC_READ *p_data) +{ + UINT8 *pp ; + tBTA_HH_LE_HID_SRVC *p_srvc = &p_dev_cb->hid_srvc[p_data->srvc_id.id.inst_id]; + + pp = p_data->p_value->unformat.p_value; + + /* save report descriptor */ + if (p_srvc->rpt_map != NULL) + GKI_freebuf((void*)p_srvc->rpt_map); + + if (p_data->p_value->unformat.len > 0) + p_srvc->rpt_map = (UINT8 *)GKI_getbuf(p_data->p_value->unformat.len); + + if (p_srvc->rpt_map != NULL) + { + STREAM_TO_ARRAY(p_srvc->rpt_map, pp, p_data->p_value->unformat.len); + p_srvc->descriptor.dl_len = p_data->p_value->unformat.len; + p_srvc->descriptor.dsc_list = p_dev_cb->hid_srvc[p_data->srvc_id.id.inst_id].rpt_map; + } + + if (bta_hh_le_read_char_dscrpt(p_dev_cb, + UUID_SERVCLASS_LE_HID, + p_data->srvc_id.id.inst_id, + GATT_UUID_HID_REPORT_MAP, + p_data->char_id.inst_id, + GATT_UUID_EXT_RPT_REF_DESCR) != BTA_HH_OK) + { + bta_hh_le_search_hid_chars(p_dev_cb); + } + + +} +/******************************************************************************* +** +** Function bta_hh_le_proc_get_rpt_cmpl +** +** Description Process the Read report complete, send GET_REPORT_EVT to application +** with the report data. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_proc_get_rpt_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATTC_READ *p_data) +{ + BT_HDR *p_buf = NULL; + tBTA_HH_LE_RPT *p_rpt; + tBTA_HH_HSDATA hs_data; + UINT8 *pp ; + + if (p_dev_cb->w4_evt != BTA_HH_GET_RPT_EVT) + { + APPL_TRACE_ERROR1("Unexpected READ cmpl, w4_evt = %d", p_dev_cb->w4_evt); + return; + } + + hs_data.status = BTA_HH_ERR; + hs_data.handle = p_dev_cb->hid_handle; + + if (p_data->status == BTA_GATT_OK) + { + p_rpt = bta_hh_le_find_report_entry(p_dev_cb, + p_data->srvc_id.id.inst_id,//BTA_HH_LE_SRVC_DEF, + p_data->char_id.uuid.uu.uuid16, + p_data->char_id.inst_id); + + if (p_rpt != NULL && + p_data->p_value != NULL && + (p_buf = (BT_HDR *)GKI_getbuf((UINT16)(sizeof(BT_HDR) +p_data->p_value->unformat.len + 1))) != NULL) + { + /* pack data send to app */ + hs_data.status = BTA_HH_OK; + p_buf->len = p_data->p_value->unformat.len + 1; + p_buf->layer_specific = 0; + + /* attach report ID as the first byte of the report before sending it to USB HID driver */ + pp = (UINT8*)(p_buf + 1); + UINT8_TO_STREAM(pp, p_rpt->rpt_id); + memcpy(pp, p_data->p_value->unformat.p_value, p_data->p_value->unformat.len); + + hs_data.rsp_data.p_rpt_data =p_buf; + } + } + + p_dev_cb->w4_evt = 0; + (* bta_hh_cb.p_cback)(BTA_HH_GET_RPT_EVT, (tBTA_HH *)&hs_data); + + utl_freebuf((void **)&p_buf); + +} +/******************************************************************************* +** +** Function bta_hh_le_proc_read_proto_mode +** +** Description Process the Read protocol mode, send GET_PROTO_EVT to application +** with the protocol mode. +** +*******************************************************************************/ +void bta_hh_le_proc_read_proto_mode(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATTC_READ *p_data) +{ + tBTA_HH_HSDATA hs_data; + + hs_data.status = BTA_HH_ERR; + hs_data.handle = p_dev_cb->hid_handle; + hs_data.rsp_data.proto_mode = p_dev_cb->mode; + + if (p_data->status == BTA_GATT_OK && p_data->p_value) + { + hs_data.status = BTA_HH_OK; + /* match up BTE/BTA report/boot mode def*/ + hs_data.rsp_data.proto_mode = *(p_data->p_value->unformat.p_value); + /* LE repot mode is the opposite value of BR/EDR report mode, flip it here */ + if (hs_data.rsp_data.proto_mode == 0) + hs_data.rsp_data.proto_mode = BTA_HH_PROTO_BOOT_MODE; + else + hs_data.rsp_data.proto_mode = BTA_HH_PROTO_RPT_MODE; + + p_dev_cb->mode = hs_data.rsp_data.proto_mode; + } +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG1("LE GET_PROTOCOL Mode = [%s]", + (hs_data.rsp_data.proto_mode == BTA_HH_PROTO_RPT_MODE)? "Report" : "Boot"); +#endif + + p_dev_cb->w4_evt = 0; + (* bta_hh_cb.p_cback)(BTA_HH_GET_PROTO_EVT, (tBTA_HH *)&hs_data); + +} +/******************************************************************************* +** +** Function bta_hh_w4_le_read_char_cmpl +** +** Description process the GATT read complete in W4_CONN state. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_w4_le_read_char_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) +{ + tBTA_GATTC_READ * p_data = (tBTA_GATTC_READ *)p_buf; + UINT8 *pp ; + + if (p_data->char_id.uuid.uu.uuid16 == GATT_UUID_BATTERY_LEVEL) + { + bta_hh_read_battery_level_cmpl(p_data->status, p_dev_cb, p_data); + } + else + { + if (p_data->status == BTA_GATT_OK && p_data->p_value) + { + pp = p_data->p_value->unformat.p_value; + + switch (p_data->char_id.uuid.uu.uuid16) + { + /* save device information */ + case GATT_UUID_HID_INFORMATION: + STREAM_TO_UINT16(p_dev_cb->dscp_info.version, pp); + STREAM_TO_UINT8(p_dev_cb->dscp_info.ctry_code, pp); + STREAM_TO_UINT8(p_dev_cb->dscp_info.flag, pp); + break; + + case GATT_UUID_HID_REPORT_MAP: + bta_hh_le_save_rpt_map(p_dev_cb, p_data); + return; + + default: +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_ERROR2("Unexpected read %s(0x%04x)", + bta_hh_uuid_to_str(p_data->char_id.uuid.uu.uuid16), + p_data->char_id.uuid.uu.uuid16); +#endif + break; + } + } + else + { +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_ERROR3("read uuid %s[0x%04x] error: %d", + bta_hh_uuid_to_str(p_data->char_id.uuid.uu.uuid16), + p_data->char_id.uuid.uu.uuid16, + p_data->status); +#else + APPL_TRACE_ERROR2("read uuid [0x%04x] error: %d", p_data->char_id.uuid.uu.uuid16, p_data->status); +#endif + } + bta_hh_le_search_hid_chars(p_dev_cb); + } + +} +/******************************************************************************* +** +** Function bta_hh_le_read_char_cmpl +** +** Description a characteristic value is received. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_read_char_cmpl (tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) +{ + tBTA_GATTC_READ * p_data = (tBTA_GATTC_READ *)p_buf; + + switch (p_data->char_id.uuid.uu.uuid16) + { + /* GET_REPORT */ + case GATT_UUID_HID_REPORT: + case GATT_UUID_HID_BT_KB_INPUT: + case GATT_UUID_HID_BT_KB_OUTPUT: + case GATT_UUID_HID_BT_MOUSE_INPUT: + case GATT_UUID_BATTERY_LEVEL: /* read battery level */ + bta_hh_le_proc_get_rpt_cmpl(p_dev_cb, p_data); + break; + + case GATT_UUID_HID_PROTO_MODE: + bta_hh_le_proc_read_proto_mode(p_dev_cb, p_data); + break; + + default: + APPL_TRACE_ERROR1("Unexpected Read UUID: 0x%04x", p_data->char_id.uuid.uu.uuid16); + break; + } + +} +/******************************************************************************* +** +** Function bta_hh_le_read_descr_cmpl +** +** Description read characteristic descriptor is completed in CONN st. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_read_descr_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) +{ + tBTA_HH_LE_RPT *p_rpt; + tBTA_GATTC_READ * p_data = (tBTA_GATTC_READ *)p_buf; + UINT8 *pp; + + /* if a report client configuration */ + if (p_data->descr_type.uuid.uu.uuid16 == GATT_UUID_CHAR_CLIENT_CONFIG) + { + if ((p_rpt = bta_hh_le_find_report_entry(p_dev_cb, + BTA_HH_LE_SRVC_DEF, + p_data->char_id.uuid.uu.uuid16, + p_data->char_id.inst_id)) != NULL) + { + pp = p_data->p_value->unformat.p_value; + STREAM_TO_UINT16(p_rpt->client_cfg_value, pp); + + APPL_TRACE_DEBUG1("Read Client Configuration: 0x%04x", p_rpt->client_cfg_value); + } + } +} + +void bta_hh_le_read_battery_level_descr_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATTC_READ * p_data) +{ + tBTA_HH_LE_RPT *p_rpt; + UINT16 descr_uuid = p_data->descr_type.uuid.uu.uuid16; + + /* read report reference descriptor for battery level is completed */ + if (descr_uuid == GATT_UUID_RPT_REF_DESCR) + { + if ((p_rpt = bta_hh_le_find_report_entry(p_dev_cb, + p_data->srvc_id.id.inst_id, + GATT_UUID_BATTERY_LEVEL, + p_data->char_id.inst_id)) == NULL) + { + bta_hh_le_search_hid_chars(p_dev_cb); + } + else + bta_hh_le_save_rpt_ref(p_dev_cb, p_rpt, p_data); + + } +} +/******************************************************************************* +** +** Function bta_hh_w4_le_read_descr_cmpl +** +** Description read characteristic descriptor is completed in W4_CONN st. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_w4_le_read_descr_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) +{ + tBTA_HH_LE_RPT *p_rpt; + tBTA_GATTC_READ * p_data = (tBTA_GATTC_READ *)p_buf; + UINT16 char_uuid16; + + if (p_data == NULL) + return; + + char_uuid16 = p_data->char_id.uuid.uu.uuid16; + +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG2("bta_hh_w4_le_read_descr_cmpl uuid: %s(0x%04x)", + bta_hh_uuid_to_str(p_data->descr_type.uuid.uu.uuid16), + p_data->descr_type.uuid.uu.uuid16); +#endif + switch (char_uuid16) + { + case GATT_UUID_HID_REPORT: + if ((p_rpt = bta_hh_le_find_report_entry(p_dev_cb, + p_data->srvc_id.id.inst_id, + GATT_UUID_HID_REPORT, + p_data->char_id.inst_id)) == NULL) + { + bta_hh_le_search_hid_chars(p_dev_cb); + } + else + bta_hh_le_save_rpt_ref(p_dev_cb, p_rpt, p_data); + break; + + case GATT_UUID_HID_REPORT_MAP: + bta_hh_le_save_ext_rpt_ref(p_dev_cb, p_data); + break; + + case GATT_UUID_BATTERY_LEVEL: + bta_hh_le_read_battery_level_descr_cmpl(p_dev_cb, p_data); + break; + + default: + APPL_TRACE_ERROR1("unknown descriptor read complete for uuid: 0x%04x", char_uuid16); + break; + } +} + +/******************************************************************************* +** +** Function bta_hh_w4_le_write_cmpl +** +** Description Write charactersitic complete event at W4_CONN st. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_w4_le_write_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) +{ + tBTA_GATTC_WRITE *p_data = (tBTA_GATTC_WRITE *)p_buf; + + if (p_data == NULL) + return; + + if (p_data->char_id.uuid.uu.uuid16 == GATT_UUID_HID_PROTO_MODE) + { + p_dev_cb->status = (p_data->status == BTA_GATT_OK) ? BTA_HH_OK : BTA_HH_ERR_PROTO; + + if ((p_dev_cb->disc_active & BTA_HH_LE_DISC_HIDS) != 0) + { + bta_hh_le_search_hid_chars(p_dev_cb); + } + else + { + bta_hh_le_open_cmpl(p_dev_cb); + } + } +} +/******************************************************************************* +** +** Function bta_hh_le_write_cmpl +** +** Description Write charactersitic complete event at CONN st. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_write_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) +{ + tBTA_GATTC_WRITE *p_data = (tBTA_GATTC_WRITE *)p_buf; + tBTA_HH_CBDATA cback_data ; + UINT16 cb_evt = p_dev_cb->w4_evt; + + if (p_data == NULL || cb_evt == 0) + return; + +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG1("bta_hh_le_write_cmpl w4_evt: %d", p_dev_cb->w4_evt); +#endif + switch (p_data->char_id.uuid.uu.uuid16) + { + /* Set protocol finished */ + case GATT_UUID_HID_PROTO_MODE: + cback_data.handle = p_dev_cb->hid_handle; + if (p_data->status == BTA_GATT_OK) + { + bta_hh_le_register_input_notif(p_dev_cb, p_data->srvc_id.id.inst_id, p_dev_cb->mode, FALSE); + cback_data.status = BTA_HH_OK; + } + else + cback_data.status = BTA_HH_ERR; + p_dev_cb->w4_evt = 0; + (* bta_hh_cb.p_cback)(cb_evt, (tBTA_HH *)&cback_data); + break; + + /* Set Report finished */ + case GATT_UUID_HID_REPORT: + case GATT_UUID_HID_BT_KB_INPUT: + case GATT_UUID_HID_BT_MOUSE_INPUT: + case GATT_UUID_HID_BT_KB_OUTPUT: + cback_data.handle = p_dev_cb->hid_handle; + cback_data.status = (p_data->status == BTA_GATT_OK)? BTA_HH_OK : BTA_HH_ERR; + p_dev_cb->w4_evt = 0; + (* bta_hh_cb.p_cback)(cb_evt, (tBTA_HH *)&cback_data); + break; + + case GATT_UUID_SCAN_INT_WINDOW: + bta_hh_le_register_scpp_notif(p_dev_cb, p_data->status); + break; + + + default: + break; + } + +} +/******************************************************************************* +** +** Function bta_hh_le_write_char_descr_cmpl +** +** Description Write charactersitic descriptor complete event +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_write_char_descr_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) +{ + tBTA_GATTC_WRITE *p_data = (tBTA_GATTC_WRITE *)p_buf; + UINT8 srvc_inst_id, hid_inst_id; + + /* only write client configuration possible */ + if (p_data && p_data->descr_type.uuid.uu.uuid16 == GATT_UUID_CHAR_CLIENT_CONFIG) + { + srvc_inst_id = p_data->srvc_id.id.inst_id; + hid_inst_id = srvc_inst_id; + switch (p_data->char_id.uuid.uu.uuid16) + { + case GATT_UUID_BATTERY_LEVEL: /* battery level clt cfg registered */ + hid_inst_id = bta_hh_le_find_service_inst_by_battery_inst_id(p_dev_cb, srvc_inst_id); + /* fall through */ + case GATT_UUID_HID_BT_KB_INPUT: + case GATT_UUID_HID_BT_MOUSE_INPUT: + case GATT_UUID_HID_REPORT: + if (p_data->status == BTA_GATT_OK) + p_dev_cb->hid_srvc[hid_inst_id].report[p_dev_cb->clt_cfg_idx ++].client_cfg_value = + BTA_GATT_CLT_CONFIG_NOTIFICATION; + + bta_hh_le_write_rpt_clt_cfg(p_dev_cb, hid_inst_id); + + break; + + case GATT_UUID_SCAN_REFRESH: + bta_hh_le_register_scpp_notif_cmpl(p_dev_cb, p_data->status); + break; + default: + APPL_TRACE_ERROR1("Unknown char ID clt cfg: 0x%04x", p_data->char_id.uuid.uu.uuid16); + } + } + else + { +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_ERROR2("Unexpected write to %s(0x%04x)", + bta_hh_uuid_to_str(p_data->descr_type.uuid.uu.uuid16), + p_data->descr_type.uuid.uu.uuid16); +#else + APPL_TRACE_ERROR1("Unexpected write to (0x%04x)", p_data->descr_type.uuid.uu.uuid16); +#endif + } + +} +/******************************************************************************* +** +** Function bta_hh_le_input_rpt_notify +** +** Description process the notificaton event, most likely for input report. +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_input_rpt_notify(tBTA_GATTC_NOTIFY *p_data) +{ + tBTA_HH_DEV_CB *p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_data->conn_id); + UINT8 app_id; + UINT8 *p_buf; + tBTA_HH_LE_RPT *p_rpt; + + if (p_dev_cb == NULL) + { + APPL_TRACE_ERROR0("notification received from Unknown device"); + return; + } + + app_id= p_dev_cb->app_id; + + p_rpt = bta_hh_le_find_report_entry(p_dev_cb, + BTA_HH_LE_SRVC_DEF, + p_data->char_id.char_id.uuid.uu.uuid16, + p_data->char_id.char_id.inst_id); + if (p_rpt == NULL) + { + APPL_TRACE_ERROR0("notification received for Unknown Report"); + return; + } + + if (p_data->char_id.char_id.uuid.uu.uuid16 == GATT_UUID_HID_BT_MOUSE_INPUT) + app_id = BTA_HH_APP_ID_MI; + else if (p_data->char_id.char_id.uuid.uu.uuid16 == GATT_UUID_HID_BT_KB_INPUT) + app_id = BTA_HH_APP_ID_KB; + + /* need to append report ID to the head of data */ + if ((p_buf = (UINT8 *)GKI_getbuf((UINT16)(p_data->len + 1))) == NULL) + { + APPL_TRACE_ERROR0("No resources to send report data"); + return; + } + + APPL_TRACE_ERROR1("Notification received on report ID: %d", p_rpt->rpt_id); + + p_buf[0] = p_rpt->rpt_id; + memcpy(&p_buf[1], p_data->value, p_data->len); + p_data->len ++; + + bta_hh_co_data((UINT8)p_dev_cb->hid_handle, + p_buf, + p_data->len, + p_dev_cb->mode, + 0 , /* no sub class*/ + p_dev_cb->dscp_info.ctry_code, + p_dev_cb->addr, + app_id); + + GKI_freebuf(p_buf); +} + + +/******************************************************************************* +** +** Function bta_hh_gatt_open_fail +** +** Description action function to process the open fail +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_open_fail(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_CONN conn_dat ; + tBTA_HH_LE_HID_SRVC *p_hid_srvc = &p_cb->hid_srvc[0]; + UINT8 i; + + p_cb->disc_active = BTA_HH_LE_DISC_NONE; + /* Failure in opening connection or GATT discovery failure */ + conn_dat.handle = p_cb->hid_handle; + memcpy(conn_dat.bda, p_cb->addr, BD_ADDR_LEN); + conn_dat.le_hid = TRUE; + conn_dat.scps_supported = p_cb->scps_supported; + + if (p_cb->status == BTA_HH_OK) + conn_dat.status = (p_data->le_close.reason == BTA_GATT_CONN_UNKNOWN) ? p_cb->status : BTA_HH_ERR; + else + conn_dat.status = p_cb->status; + + for (i = 0; i < BTA_HH_LE_HID_SRVC_MAX; i ++, p_hid_srvc ++) + { + utl_freebuf((void **)&p_hid_srvc->rpt_map); + memset(p_hid_srvc, 0, sizeof(tBTA_HH_LE_HID_SRVC)); + } + + /* Report OPEN fail event */ + (*bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH *)&conn_dat); + +} +/******************************************************************************* +** +** Function bta_hh_gatt_close +** +** Description action function to process the GATT close int he state machine. +** +** Returns void +** +*******************************************************************************/ +void bta_hh_gatt_close(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + tBTA_HH_CBDATA disc_dat = {BTA_HH_OK, 0}; + + /* finaliza device driver */ + bta_hh_co_close(p_cb->hid_handle, p_cb->app_id); + /* update total conn number */ + bta_hh_cb.cnt_num --; + + disc_dat.handle = p_cb->hid_handle; + disc_dat.status = p_cb->status; + + (*bta_hh_cb.p_cback)(BTA_HH_CLOSE_EVT, (tBTA_HH *)&disc_dat); + + /* if no connection is active and HH disable is signaled, disable service */ + if (bta_hh_cb.cnt_num == 0 && bta_hh_cb.w4_disable) + { + bta_hh_disc_cmpl(); + } + else + { +#if (BTA_HH_LE_RECONN == TRUE) + if (p_data->le_close.reason == BTA_GATT_CONN_TIMEOUT) + { + bta_hh_le_add_dev_bg_conn(p_cb, FALSE); + } +#endif + } + + return; + +} +/******************************************************************************* +** +** Function bta_hh_le_api_disc_act +** +** Description initaite a Close API to a remote HID device +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_api_disc_act(tBTA_HH_DEV_CB *p_cb) +{ + if (p_cb->conn_id != BTA_GATT_INVALID_CONN_ID) + BTA_GATTC_Close(p_cb->conn_id); +} + +/******************************************************************************* +** +** Function bta_hh_le_get_rpt +** +** Description GET_REPORT on a LE HID Report +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_get_rpt(tBTA_HH_DEV_CB *p_cb, UINT8 srvc_inst, tBTA_HH_RPT_TYPE r_type, UINT8 rpt_id) +{ + tBTA_HH_LE_RPT *p_rpt = bta_hh_le_find_rpt_by_idtype(p_cb->hid_srvc[srvc_inst].report, p_cb->mode, r_type, rpt_id); + tBTA_GATTC_CHAR_ID char_id; + UINT16 srvc_uuid = UUID_SERVCLASS_LE_HID; + + if (p_rpt == NULL) + { + APPL_TRACE_ERROR0("bta_hh_le_get_rpt: no matching report"); + return; + } + if (p_rpt->uuid == GATT_UUID_BATTERY_LEVEL) + srvc_uuid = UUID_SERVCLASS_BATTERY; + + p_cb->w4_evt = BTA_HH_GET_RPT_EVT; + + bta_hh_le_fill_16bits_srvc_id(TRUE, srvc_inst, srvc_uuid, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(p_rpt->inst_id, p_rpt->uuid, &char_id.char_id); + + BTA_GATTC_ReadCharacteristic(p_cb->conn_id, + &char_id, + BTA_GATT_AUTH_REQ_NONE); +} + +/******************************************************************************* +** +** Function bta_hh_le_write_rpt +** +** Description SET_REPORT/or DATA output on a LE HID Report +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_write_rpt(tBTA_HH_DEV_CB *p_cb, UINT8 srvc_inst, + tBTA_GATTC_WRITE_TYPE write_type, + tBTA_HH_RPT_TYPE r_type, + BT_HDR *p_buf, UINT16 w4_evt ) +{ + tBTA_HH_LE_RPT *p_rpt; + tBTA_GATTC_CHAR_ID char_id; + UINT8 *p_value, rpt_id; + + if (p_buf == NULL || p_buf->len == 0) + { + APPL_TRACE_ERROR0("bta_hh_le_write_rpt: Illegal data"); + return; + } + + /* strip report ID from the data */ + p_value = (UINT8 *)(p_buf + 1) + p_buf->offset; + STREAM_TO_UINT8(rpt_id, p_value); + p_buf->len -= 1; + + p_rpt = bta_hh_le_find_rpt_by_idtype(p_cb->hid_srvc[srvc_inst].report, p_cb->mode, r_type, rpt_id); + + if (p_rpt == NULL) + { + APPL_TRACE_ERROR0("bta_hh_le_write_rpt: no matching report"); + return; + } + + APPL_TRACE_ERROR2("bta_hh_le_write_rpt: ReportID: 0x%02x Data Len: %d", rpt_id, p_buf->len); + + p_cb->w4_evt = w4_evt; + + bta_hh_le_fill_16bits_srvc_id(TRUE, srvc_inst, UUID_SERVCLASS_LE_HID, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(p_rpt->inst_id, p_rpt->uuid, &char_id.char_id); + + BTA_GATTC_WriteCharValue(p_cb->conn_id, + &char_id, + write_type, /* default to use write request */ + p_buf->len, + p_value, + BTA_GATT_AUTH_REQ_NONE); + +} +/******************************************************************************* +** +** Function bta_hh_le_suspend +** +** Description send LE suspend or exit suspend mode to remote device. +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_suspend(tBTA_HH_DEV_CB *p_cb, tBTA_HH_TRANS_CTRL_TYPE ctrl_type) +{ + UINT8 i; + tBTA_GATTC_CHAR_ID char_id; + + ctrl_type -= BTA_HH_CTRL_SUSPEND; + + for (i = 0; i < BTA_HH_LE_HID_SRVC_MAX; i ++) + { + bta_hh_le_fill_16bits_srvc_id(TRUE, i, UUID_SERVCLASS_LE_HID, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(0, GATT_UUID_HID_CONTROL_POINT, &char_id.char_id); + + BTA_GATTC_WriteCharValue(p_cb->conn_id, + &char_id, + BTA_GATTC_TYPE_WRITE_NO_RSP, /* default to use write request */ + 1, + &ctrl_type, + BTA_GATT_AUTH_REQ_NONE); + } +} + +/******************************************************************************* +** +** Function bta_hh_le_write_dev_act +** +** Description Write LE device action. can be SET/GET/DATA transaction. +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) +{ + switch(p_data->api_sndcmd.t_type) + { + case HID_TRANS_SET_PROTOCOL: + p_cb->w4_evt = BTA_HH_SET_PROTO_EVT; + bta_hh_le_set_protocol_mode(p_cb, p_data->api_sndcmd.param); + break; + + case HID_TRANS_GET_PROTOCOL: + bta_hh_le_get_protocol_mode(p_cb); + break; + + case HID_TRANS_GET_REPORT: + bta_hh_le_get_rpt(p_cb, + BTA_HH_LE_SRVC_DEF, + p_data->api_sndcmd.param, + p_data->api_sndcmd.rpt_id); + break; + + case HID_TRANS_SET_REPORT: + bta_hh_le_write_rpt(p_cb, + BTA_HH_LE_SRVC_DEF, + BTA_GATTC_TYPE_WRITE, + p_data->api_sndcmd.param, + p_data->api_sndcmd.p_data, + BTA_HH_SET_RPT_EVT); + break; + + case HID_TRANS_DATA: /* output report */ + + bta_hh_le_write_rpt(p_cb, + BTA_HH_LE_SRVC_DEF, + BTA_GATTC_TYPE_WRITE_NO_RSP, + p_data->api_sndcmd.param, + p_data->api_sndcmd.p_data, + BTA_HH_DATA_EVT); + break; + + case HID_TRANS_CONTROL: + /* no handshake event will be generated */ + /* if VC_UNPLUG is issued, set flag */ + if (p_data->api_sndcmd.param == BTA_HH_CTRL_SUSPEND || + p_data->api_sndcmd.param == BTA_HH_CTRL_EXIT_SUSPEND) + { + bta_hh_le_suspend(p_cb, p_data->api_sndcmd.param); + } + break; + + default: + APPL_TRACE_ERROR1("unsupported trsanction for LE HID device: %d", p_data->api_sndcmd.t_type); + break; + } + +} +/******************************************************************************* +** +** Function bta_hh_le_get_dscp_act +** +** Description Send ReportDescriptor to application for all HID services. +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_get_dscp_act(tBTA_HH_DEV_CB *p_cb) +{ + UINT8 i; + + for (i = 0 ;i < BTA_HH_LE_HID_SRVC_MAX; i ++) + { + if (p_cb->hid_srvc[i].in_use) + { + p_cb->dscp_info.descriptor.dl_len = p_cb->hid_srvc[i].descriptor.dl_len; + p_cb->dscp_info.descriptor.dsc_list = p_cb->hid_srvc[i].descriptor.dsc_list; + + (*bta_hh_cb.p_cback)(BTA_HH_GET_DSCP_EVT, (tBTA_HH *)&p_cb->dscp_info); + } + else + break; + } +} + +/******************************************************************************* +** +** Function bta_hh_le_add_dev_bg_conn +** +** Description Remove a LE HID device from back ground connection procedure. +** +** Returns void +** +*******************************************************************************/ +static void bta_hh_le_add_dev_bg_conn(tBTA_HH_DEV_CB *p_cb, BOOLEAN check_bond) +{ + UINT8 sec_flag=0; + BOOLEAN to_add = TRUE; + + if (check_bond) + { + /* start reconnection if remote is a bonded device */ + /* verify bond */ + BTM_GetSecurityFlags(p_cb->addr, &sec_flag); + + if ((sec_flag & BTM_SEC_FLAG_LKEY_KNOWN) == 0) + to_add = FALSE; + } + + if (/*p_cb->dscp_info.flag & BTA_HH_LE_NORMAL_CONN &&*/ + !p_cb->in_bg_conn && to_add) + { + /* add device into BG connection to accept remote initiated connection */ + BTA_GATTC_Open(bta_hh_cb.gatt_if, p_cb->addr, FALSE); + p_cb->in_bg_conn = TRUE; + + BTA_DmBleSetBgConnType(BTA_DM_BLE_CONN_AUTO, NULL); + } + return; +} + +/******************************************************************************* +** +** Function bta_hh_le_add_device +** +** Description Add a LE HID device as a known device, and also add the address +** into back ground connection WL for incoming connection. +** +** Returns void +** +*******************************************************************************/ +UINT8 bta_hh_le_add_device(tBTA_HH_DEV_CB *p_cb, tBTA_HH_MAINT_DEV *p_dev_info) +{ + + /* update DI information */ + bta_hh_update_di_info(p_cb, + p_dev_info->dscp_info.vendor_id, + p_dev_info->dscp_info.product_id, + p_dev_info->dscp_info.version, + p_dev_info->dscp_info.flag); + + /* add to BTA device list */ + bta_hh_add_device_to_list(p_cb, p_cb->hid_handle, + p_dev_info->attr_mask, + &p_dev_info->dscp_info.descriptor, + p_dev_info->sub_class, + p_dev_info->dscp_info.ssr_max_latency, + p_dev_info->dscp_info.ssr_min_tout, + p_dev_info->app_id); + + bta_hh_le_add_dev_bg_conn(p_cb, FALSE); + + return p_cb->hid_handle; +} + +/******************************************************************************* +** +** Function bta_hh_le_remove_dev_bg_conn +** +** Description Remove a LE HID device from back ground connection procedure. +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_remove_dev_bg_conn(tBTA_HH_DEV_CB *p_dev_cb) +{ + if (p_dev_cb->in_bg_conn) + { + p_dev_cb->in_bg_conn = FALSE; + + BTA_GATTC_CancelOpen(bta_hh_cb.gatt_if, p_dev_cb->addr, FALSE); + } +} +/******************************************************************************* +** +** Function bta_hh_le_update_scpp +** +** Description action function to update the scan parameters on remote HID +** device +** +** Parameters: +** +*******************************************************************************/ +void bta_hh_le_update_scpp(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) +{ + tBTA_GATTC_CHAR_ID char_id; + UINT8 value[4], *p = value; + tBTA_HH_CBDATA cback_data ; + + if (!p_dev_cb->is_le_device || + p_dev_cb->mode != BTA_HH_PROTO_RPT_MODE || + p_dev_cb->scps_supported == FALSE) + { + APPL_TRACE_ERROR0("Can not set ScPP scan paramter as boot host, or remote does not support ScPP "); + + cback_data.handle = p_dev_cb->hid_handle; + cback_data.status = BTA_HH_ERR; + (* bta_hh_cb.p_cback)(BTA_HH_UPDATE_SCPP_EVT, (tBTA_HH *)&cback_data); + + return; + } + + p_dev_cb->w4_evt = BTA_HH_UPDATE_SCPP_EVT; + + UINT16_TO_STREAM(p, p_buf->le_scpp_update.scan_int); + UINT16_TO_STREAM(p, p_buf->le_scpp_update.scan_win); + + bta_hh_le_fill_16bits_srvc_id(TRUE, BTA_HH_SCPP_INST_DEF, UUID_SERVCLASS_SCAN_PARAM, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(BTA_HH_SCPP_INST_DEF, GATT_UUID_SCAN_INT_WINDOW, &char_id.char_id); + + BTA_GATTC_WriteCharValue(p_dev_cb->conn_id, + &char_id, + BTA_GATTC_TYPE_WRITE_NO_RSP, + 2, + value, + BTA_GATT_AUTH_REQ_NONE); + +} +/******************************************************************************* +** +** Function bta_hh_gattc_callback +** +** Description This is GATT client callback function used in BTA HH. +** +** Parameters: +** +*******************************************************************************/ +static void bta_hh_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC *p_data) +{ + tBTA_HH_DEV_CB *p_dev_cb; + UINT16 evt; +#if BTA_HH_DEBUG + APPL_TRACE_DEBUG1("bta_hh_gattc_callback event = %d", event); +#endif + if (p_data == NULL) + return; + + switch (event) + { + case BTA_GATTC_REG_EVT: /* 0 */ + bta_hh_le_register_cmpl(&p_data->reg_oper); + break; + + case BTA_GATTC_DEREG_EVT: /* 1 */ + bta_hh_cleanup_disable(p_data->reg_oper.status); + break; + + case BTA_GATTC_OPEN_EVT: /* 2 */ + p_dev_cb = bta_hh_le_find_dev_cb_by_bda(p_data->open.remote_bda); + bta_hh_sm_execute(p_dev_cb, BTA_HH_GATT_OPEN_EVT, (tBTA_HH_DATA *)&p_data->open); + break; + + case BTA_GATTC_READ_CHAR_EVT: /* 3 */ + case BTA_GATTC_READ_DESCR_EVT: /* 8 */ + p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_data->read.conn_id); + if (event == BTA_GATTC_READ_CHAR_EVT) + evt = BTA_HH_GATT_READ_CHAR_CMPL_EVT; + else + evt = BTA_HH_GATT_READ_DESCR_CMPL_EVT; + + bta_hh_sm_execute(p_dev_cb, evt, (tBTA_HH_DATA *)&p_data->read); + break; + + case BTA_GATTC_WRITE_DESCR_EVT: /* 9 */ + case BTA_GATTC_WRITE_CHAR_EVT: /* 4 */ + p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_data->write.conn_id); + if (event == BTA_GATTC_WRITE_CHAR_EVT) + evt = BTA_HH_GATT_WRITE_CHAR_CMPL_EVT; + else + evt = BTA_HH_GATT_WRITE_DESCR_CMPL_EVT; + + bta_hh_sm_execute(p_dev_cb, evt, (tBTA_HH_DATA *)&p_data->write); + break; + + case BTA_GATTC_CLOSE_EVT: /* 5 */ + bta_hh_le_close(&p_data->close); + break; + + case BTA_GATTC_SEARCH_CMPL_EVT: /* 6 */ + bta_hh_le_srvc_search_cmpl(&p_data->search_cmpl); + break; + + case BTA_GATTC_SEARCH_RES_EVT: /* 7 */ + bta_hh_le_search_result(&p_data->srvc_res); + break; + + + + case BTA_GATTC_NOTIF_EVT: /* 10 */ + bta_hh_le_input_rpt_notify(&p_data->notify); + break; + default: + break; + } +} + +/******************************************************************************* +** +** Function bta_hh_le_hid_read_rpt_clt_cfg +** +** Description a test command to read report descriptor client configuration +** +** Returns void +** +*******************************************************************************/ +void bta_hh_le_hid_read_rpt_clt_cfg(BD_ADDR bd_addr, UINT8 rpt_id) +{ + tBTA_HH_DEV_CB *p_cb = NULL; + tBTA_HH_LE_RPT *p_rpt ; + UINT8 index = BTA_HH_IDX_INVALID; + + index = bta_hh_find_cb(bd_addr); + if ((index = bta_hh_find_cb(bd_addr))== BTA_HH_IDX_INVALID) + { + APPL_TRACE_ERROR0("unknown device"); + return; + } + + p_cb = &bta_hh_cb.kdev[index]; + + p_rpt = bta_hh_le_find_rpt_by_idtype(p_cb->hid_srvc[BTA_HH_LE_SRVC_DEF].report, p_cb->mode, BTA_HH_RPTT_INPUT, rpt_id); + + if (p_rpt == NULL) + { + APPL_TRACE_ERROR0("bta_hh_le_write_rpt: no matching report"); + return; + } + + bta_hh_le_read_char_dscrpt(p_cb, + UUID_SERVCLASS_LE_HID, + BTA_HH_LE_SRVC_DEF, + p_rpt->uuid, + p_rpt->inst_id, + GATT_UUID_CHAR_CLIENT_CONFIG); + + + + return; +} +/******************************************************************************* +** +** Function bta_hh_le_search_scps +** +** Description discovery scan parameter service if act as report host, otherwise +** finish LE connection. +** +** Parameters: +** +*******************************************************************************/ +static void bta_hh_le_search_scps(tBTA_HH_DEV_CB *p_cb) +{ + tBT_UUID pri_srvc; + + if ( p_cb->mode == BTA_HH_PROTO_RPT_MODE) + { + p_cb->disc_active |= BTA_HH_LE_DISC_SCPS; + /* start service discovery for Scan Parameter service */ + pri_srvc.len = LEN_UUID_16; + pri_srvc.uu.uuid16 = UUID_SERVCLASS_SCAN_PARAM; + + BTA_GATTC_ServiceSearchRequest(p_cb->conn_id, &pri_srvc); + } + else + bta_hh_le_open_cmpl(p_cb); +} +/******************************************************************************* +** +** Function bta_hh_le_search_scps_chars +** +** Description find ScPS optional characteristics scan refresh +** +** Parameters: +** +*******************************************************************************/ +static void bta_hh_le_search_scps_chars(tBTA_HH_DEV_CB *p_cb) +{ + tBTA_GATT_SRVC_ID srvc_id; + tBT_UUID char_cond; + tBTA_GATTC_CHAR_ID char_result; + tBTA_GATT_CHAR_PROP prop; + + p_cb->scps_supported = TRUE; + bta_hh_le_fill_16bits_srvc_id(TRUE, 0, UUID_SERVCLASS_SCAN_PARAM, &srvc_id); + + char_cond.len = LEN_UUID_16; + char_cond.uu.uuid16 = GATT_UUID_SCAN_REFRESH; + + /* look for scan refresh */ + if (BTA_GATTC_GetFirstChar( p_cb->conn_id, + &srvc_id, + &char_cond, + &char_result, + &prop) == BTA_GATT_OK) + { + if (prop & BTA_GATT_CHAR_PROP_BIT_NOTIFY) + p_cb->scps_notify |= BTA_HH_LE_SCPS_NOTIFY_SPT; + else + p_cb->scps_notify = BTA_HH_LE_SCPS_NOTIFY_NONE; + + } +} + +/******************************************************************************* +** +** Function bta_hh_le_register_scpp_notif +** +** Description register scan parameter refresh notitication complete +** +** +** Parameters: +** +*******************************************************************************/ +static void bta_hh_le_register_scpp_notif(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATT_STATUS status) +{ + UINT8 sec_flag=0; + tBTA_GATTC_CHAR_ID char_id; + + /* if write scan parameter sucessful */ + /* if bonded and notification is not enabled, configure the client configuration */ + if (status == BTA_GATT_OK && + (p_dev_cb->scps_notify & BTA_HH_LE_SCPS_NOTIFY_SPT) != 0 && + (p_dev_cb->scps_notify & BTA_HH_LE_SCPS_NOTIFY_ENB) == 0) + { + BTM_GetSecurityFlags(p_dev_cb->addr, &sec_flag); + if ((sec_flag & BTM_SEC_FLAG_LKEY_KNOWN)) + { + if (bta_hh_le_write_char_clt_cfg (p_dev_cb, + BTA_HH_SCPP_INST_DEF, + UUID_SERVCLASS_SCAN_PARAM, + BTA_HH_SCPP_INST_DEF, + GATT_UUID_SCAN_REFRESH, + BTA_GATT_CLT_CONFIG_NOTIFICATION)) + { + bta_hh_le_fill_16bits_srvc_id(TRUE, BTA_HH_SCPP_INST_DEF, UUID_SERVCLASS_SCAN_PARAM, &char_id.srvc_id); + bta_hh_le_fill_16bits_char_id(BTA_HH_SCPP_INST_DEF, GATT_UUID_SCAN_REFRESH, &char_id.char_id); + + BTA_GATTC_RegisterForNotifications(bta_hh_cb.gatt_if, + p_dev_cb->addr, + &char_id); + return; + } + } + } + bta_hh_le_register_scpp_notif_cmpl(p_dev_cb, status); +} +/******************************************************************************* +** +** Function bta_hh_le_register_scpp_notif_cmpl +** +** Description action function to register scan parameter refresh notitication +** +** Parameters: +** +*******************************************************************************/ +static void bta_hh_le_register_scpp_notif_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATT_STATUS status) +{ + tBTA_HH_CBDATA cback_data ; + UINT16 cb_evt = p_dev_cb->w4_evt; + + if (status == BTA_GATT_OK) + p_dev_cb->scps_notify = (BTA_HH_LE_SCPS_NOTIFY_ENB | BTA_HH_LE_SCPS_NOTIFY_SPT); + + cback_data.handle = p_dev_cb->hid_handle; + cback_data.status = (status == BTA_GATT_OK)? BTA_HH_OK : BTA_HH_ERR; + p_dev_cb->w4_evt = 0; + (* bta_hh_cb.p_cback)(cb_evt, (tBTA_HH *)&cback_data); + + +} +#endif + + + + diff --git a/bta/hh/bta_hh_main.c b/bta/hh/bta_hh_main.c index 1c034698d..377f3a5bf 100644 --- a/bta/hh/bta_hh_main.c +++ b/bta/hh/bta_hh_main.c @@ -51,6 +51,23 @@ enum BTA_HH_GET_DSCP_ACT, BTA_HH_MAINT_DEV_ACT, BTA_HH_OPEN_CMPL_ACT, + BTA_HH_OPEN_FAILURE, +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + BTA_HH_GATT_CLOSE, + BTA_HH_LE_OPEN_FAIL, + BTA_HH_GATT_OPEN, + BTA_HH_W4_LE_READ_CHAR, + BTA_HH_LE_READ_CHAR, + BTA_HH_W4_LE_READ_DESCR, + BTA_HH_LE_READ_DESCR, + BTA_HH_W4_LE_WRITE, + BTA_HH_LE_WRITE, + BTA_HH_WRITE_DESCR, + BTA_HH_START_SEC, + BTA_HH_SEC_CMPL, + BTA_HH_LE_UPDATE_SCPP, + +#endif BTA_HH_NUM_ACTIONS }; @@ -73,7 +90,23 @@ const tBTA_HH_ACTION bta_hh_action[] = bta_hh_write_dev_act, bta_hh_get_dscp_act, bta_hh_maint_dev_act, - bta_hh_open_cmpl_act + bta_hh_open_cmpl_act, + bta_hh_open_failure +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + ,bta_hh_gatt_close + ,bta_hh_le_open_fail + ,bta_hh_gatt_open + ,bta_hh_w4_le_read_char_cmpl + ,bta_hh_le_read_char_cmpl + ,bta_hh_w4_le_read_descr_cmpl + ,bta_hh_le_read_descr_cmpl + ,bta_hh_w4_le_write_cmpl + ,bta_hh_le_write_cmpl + ,bta_hh_le_write_char_descr_cmpl + ,bta_hh_start_security + ,bta_hh_security_cmpl + ,bta_hh_le_update_scpp +#endif }; /* state table information */ @@ -97,6 +130,18 @@ const UINT8 bta_hh_st_idle[][BTA_HH_NUM_COLS] = /* BTA_HH_API_GET_DSCP_EVT */ {BTA_HH_IGNORE, BTA_HH_IDLE_ST }, /* BTA_HH_API_MAINT_DEV_EVT */ {BTA_HH_MAINT_DEV_ACT, BTA_HH_IDLE_ST }, /* BTA_HH_OPEN_CMPL_EVT */ {BTA_HH_OPEN_CMPL_ACT, BTA_HH_CONN_ST } +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) +/* BTA_HH_GATT_CLOSE_EVT */ ,{BTA_HH_IGNORE, BTA_HH_IDLE_ST } +/* BTA_HH_GATT_OPEN_EVT */ ,{BTA_HH_GATT_OPEN, BTA_HH_W4_CONN_ST } +/* BTA_HH_START_ENC_EVT */ ,{BTA_HH_IGNORE, BTA_HH_IDLE_ST } +/* BTA_HH_ENC_CMPL_EVT */ ,{BTA_HH_IGNORE, BTA_HH_IDLE_ST } +/* READ_CHAR_CMPL_EVT */ ,{BTA_HH_IGNORE, BTA_HH_IDLE_ST } +/* BTA_HH_GATT_WRITE_CMPL_EVT*/ ,{BTA_HH_IGNORE, BTA_HH_IDLE_ST } +/* READ_DESCR_CMPL_EVT */ ,{BTA_HH_IGNORE, BTA_HH_IDLE_ST } +/* WRITE_DESCR_CMPL_EVT */ ,{BTA_HH_IGNORE, BTA_HH_IDLE_ST } +/* SCPP_UPDATE_EVT */ ,{BTA_HH_IGNORE, BTA_HH_IDLE_ST } +#endif + }; @@ -106,15 +151,26 @@ const UINT8 bta_hh_st_w4_conn[][BTA_HH_NUM_COLS] = /* BTA_HH_API_OPEN_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_CONN_ST }, /* BTA_HH_API_CLOSE_EVT */ {BTA_HH_IGNORE, BTA_HH_IDLE_ST }, /* BTA_HH_INT_OPEN_EVT */ {BTA_HH_OPEN_ACT, BTA_HH_W4_CONN_ST }, -/* BTA_HH_INT_CLOSE_EVT */ {BTA_HH_CLOSE_ACT, BTA_HH_IDLE_ST }, +/* BTA_HH_INT_CLOSE_EVT */ {BTA_HH_OPEN_FAILURE, BTA_HH_IDLE_ST }, /* BTA_HH_INT_DATA_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_CONN_ST }, /* BTA_HH_INT_CTRL_DATA */ {BTA_HH_IGNORE, BTA_HH_W4_CONN_ST }, /* BTA_HH_INT_HANDSK_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_CONN_ST }, /* BTA_HH_SDP_CMPL_EVT */ {BTA_HH_SDP_CMPL, BTA_HH_W4_CONN_ST }, -/* BTA_HH_API_WRITE_DEV_EVT */ {BTA_HH_IGNORE , BTA_HH_W4_CONN_ST }, +/* BTA_HH_API_WRITE_DEV_EVT */ {BTA_HH_WRITE_DEV_ACT, BTA_HH_W4_CONN_ST }, /* BTA_HH_API_GET_DSCP_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_CONN_ST }, /* BTA_HH_API_MAINT_DEV_EVT */ {BTA_HH_MAINT_DEV_ACT, BTA_HH_IDLE_ST }, /* BTA_HH_OPEN_CMPL_EVT */ {BTA_HH_OPEN_CMPL_ACT, BTA_HH_CONN_ST } +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) +/* BTA_HH_GATT_CLOSE_EVT */ ,{BTA_HH_LE_OPEN_FAIL, BTA_HH_IDLE_ST } +/* BTA_HH_GATT_OPEN_EVT */ ,{BTA_HH_GATT_OPEN, BTA_HH_W4_CONN_ST } +/* BTA_HH_START_ENC_EVT */ ,{BTA_HH_START_SEC, BTA_HH_W4_SEC } +/* BTA_HH_ENC_CMPL_EVT */ ,{BTA_HH_IGNORE, BTA_HH_W4_CONN_ST } +/* READ_CHAR_CMPL_EVT */ ,{BTA_HH_W4_LE_READ_CHAR, BTA_HH_W4_CONN_ST } +/* BTA_HH_GATT_WRITE_CMPL_EVT*/ ,{BTA_HH_W4_LE_WRITE, BTA_HH_W4_CONN_ST } +/* READ_DESCR_CMPL_EVT */ ,{BTA_HH_W4_LE_READ_DESCR, BTA_HH_W4_CONN_ST } +/* WRITE_DESCR_CMPL_EVT */ ,{BTA_HH_WRITE_DESCR, BTA_HH_W4_CONN_ST } +/* SCPP_UPDATE_EVT */ ,{BTA_HH_IGNORE, BTA_HH_W4_CONN_ST } +#endif }; @@ -133,7 +189,45 @@ const UINT8 bta_hh_st_connected[][BTA_HH_NUM_COLS] = /* BTA_HH_API_GET_DSCP_EVT */ {BTA_HH_GET_DSCP_ACT, BTA_HH_CONN_ST }, /* BTA_HH_API_MAINT_DEV_EVT */ {BTA_HH_MAINT_DEV_ACT, BTA_HH_CONN_ST }, /* BTA_HH_OPEN_CMPL_EVT */ {BTA_HH_IGNORE, BTA_HH_CONN_ST } +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) +/* BTA_HH_GATT_CLOSE_EVT */ ,{BTA_HH_GATT_CLOSE, BTA_HH_IDLE_ST } +/* BTA_HH_GATT_OPEN_EVT */ ,{BTA_HH_IGNORE, BTA_HH_CONN_ST } +/* BTA_HH_START_ENC_EVT */ ,{BTA_HH_IGNORE, BTA_HH_CONN_ST } +/* BTA_HH_ENC_CMPL_EVT */ ,{BTA_HH_IGNORE, BTA_HH_CONN_ST } +/* READ_CHAR_CMPL_EVT */ ,{BTA_HH_LE_READ_CHAR, BTA_HH_CONN_ST } +/* WRITE_CHAR_CMPL_EVT*/ ,{BTA_HH_LE_WRITE, BTA_HH_CONN_ST } +/* READ_DESCR_CMPL_EVT */ ,{BTA_HH_LE_READ_DESCR, BTA_HH_CONN_ST } /* do not currently read any descr when connection up */ +/* WRITE_DESCR_CMPL_EVT */ ,{BTA_HH_WRITE_DESCR, BTA_HH_CONN_ST } /* do not currently write any descr when connection up */ +/* SCPP_UPDATE_EVT */ ,{BTA_HH_LE_UPDATE_SCPP, BTA_HH_CONN_ST } +#endif +}; +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) +const UINT8 bta_hh_st_w4_sec[][BTA_HH_NUM_COLS] = +{ +/* Event Action Next state */ +/* BTA_HH_API_OPEN_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, +/* BTA_HH_API_CLOSE_EVT */ {BTA_HH_API_DISC_ACT, BTA_HH_W4_SEC }, +/* BTA_HH_INT_OPEN_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, +/* BTA_HH_INT_CLOSE_EVT */ {BTA_HH_OPEN_FAILURE, BTA_HH_IDLE_ST }, +/* BTA_HH_INT_DATA_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, +/* BTA_HH_INT_CTRL_DATA */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, +/* BTA_HH_INT_HANDSK_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, +/* BTA_HH_SDP_CMPL_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, +/* BTA_HH_API_WRITE_DEV_EVT */ {BTA_HH_IGNORE , BTA_HH_W4_SEC }, +/* BTA_HH_API_GET_DSCP_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, +/* BTA_HH_API_MAINT_DEV_EVT */ {BTA_HH_MAINT_DEV_ACT, BTA_HH_W4_SEC }, +/* BTA_HH_OPEN_CMPL_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, +/* BTA_HH_GATT_CLOSE_EVT */ {BTA_HH_LE_OPEN_FAIL, BTA_HH_IDLE_ST }, +/* BTA_HH_GATT_OPEN_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, +/* BTA_HH_START_ENC_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, +/* BTA_HH_ENC_CMPL_EVT */ {BTA_HH_SEC_CMPL, BTA_HH_W4_CONN_ST }, +/* READ_CHAR_CMPL_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, +/* BTA_HH_GATT_WRITE_CMPL_EVT*/ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, +/* READ_DESCR_CMPL_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC }, +/* WRITE_DESCR_CMPL_EVT */ {BTA_HH_IGNORE, BTA_HH_W4_SEC } +/* SCPP_UPDATE_EVT */ ,{BTA_HH_IGNORE, BTA_HH_W4_SEC } }; +#endif /* type for state table */ typedef const UINT8 (*tBTA_HH_ST_TBL)[BTA_HH_NUM_COLS]; @@ -144,6 +238,9 @@ const tBTA_HH_ST_TBL bta_hh_st_tbl[] = bta_hh_st_idle, bta_hh_st_w4_conn, bta_hh_st_connected +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + ,bta_hh_st_w4_sec +#endif }; /***************************************************************************** @@ -218,6 +315,10 @@ void bta_hh_sm_execute(tBTA_HH_DEV_CB *p_cb, UINT16 event, tBTA_HH_DATA * p_data case BTA_HH_API_WRITE_DEV_EVT: cback_event = (p_data->api_sndcmd.t_type - BTA_HH_FST_BTE_TRANS_EVT) + BTA_HH_FST_TRANS_CB_EVT; + if (p_data->api_sndcmd.p_data != NULL) + { + GKI_freebuf(p_data->api_sndcmd.p_data); + } if (p_data->api_sndcmd.t_type == HID_TRANS_SET_PROTOCOL || p_data->api_sndcmd.t_type == HID_TRANS_SET_REPORT || p_data->api_sndcmd.t_type == HID_TRANS_SET_IDLE) @@ -268,6 +369,12 @@ void bta_hh_sm_execute(tBTA_HH_DEV_CB *p_cb, UINT16 event, tBTA_HH_DATA * p_data bta_hh_evt_code(debug_event)); #endif + if (in_state >= BTA_HH_INVALID_ST) + { + APPL_TRACE_ERROR2("bta_hh_sm_execute: Invalid state State = 0x%x, Event = %d", + in_state,event); + return; + } state_table = bta_hh_st_tbl[p_cb->state - 1]; event &= 0xff; @@ -351,6 +458,10 @@ BOOLEAN bta_hh_hdl_event(BT_HDR *p_msg) // btla-specific -- } } + else if (p_msg->event == BTA_HH_INT_OPEN_EVT) + { + index = bta_hh_find_cb(((tBTA_HH_CBACK_DATA *)p_msg)->addr); + } else index = bta_hh_dev_handle_to_cb_idx((UINT8)p_msg->layer_specific); @@ -412,6 +523,24 @@ static char *bta_hh_evt_code(tBTA_HH_INT_EVT evt_code) return "BTA_HH_API_GET_DSCP_EVT"; case BTA_HH_OPEN_CMPL_EVT: return "BTA_HH_OPEN_CMPL_EVT"; +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + case BTA_HH_GATT_CLOSE_EVT: + return "BTA_HH_GATT_CLOSE_EVT"; + case BTA_HH_GATT_OPEN_EVT: + return "BTA_HH_GATT_OPEN_EVT"; + case BTA_HH_START_ENC_EVT: + return "BTA_HH_START_ENC_EVT"; + case BTA_HH_ENC_CMPL_EVT: + return "BTA_HH_ENC_CMPL_EVT"; + case BTA_HH_GATT_READ_CHAR_CMPL_EVT: + return "BTA_HH_GATT_READ_CHAR_CMPL_EVT"; + case BTA_HH_GATT_WRITE_CHAR_CMPL_EVT: + return "BTA_HH_GATT_WRITE_CHAR_CMPL_EVT"; + case BTA_HH_GATT_READ_DESCR_CMPL_EVT: + return "BTA_HH_GATT_READ_DESCR_CMPL_EVT"; + case BTA_HH_GATT_WRITE_DESCR_CMPL_EVT: + return "BTA_HH_GATT_WRITE_DESCR_CMPL_EVT"; +#endif default: return "unknown HID Host event code"; } @@ -438,6 +567,10 @@ static char *bta_hh_state_code(tBTA_HH_STATE state_code) return "BTA_HH_W4_CONN_ST"; case BTA_HH_CONN_ST: return "BTA_HH_CONN_ST"; +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + case BTA_HH_W4_SEC: + return "BTA_HH_W4_SEC"; +#endif default: return "unknown HID Host state"; } diff --git a/bta/hh/bta_hh_utils.c b/bta/hh/bta_hh_utils.c index bea0f2b81..0c675139b 100644 --- a/bta/hh/bta_hh_utils.c +++ b/bta/hh/bta_hh_utils.c @@ -123,6 +123,11 @@ void bta_hh_clean_up_kdev(tBTA_HH_DEV_CB *p_cb) if (p_cb->hid_handle != BTA_HH_INVALID_HANDLE ) { +#if BTA_HH_LE_INCLUDED == TRUE + if (p_cb->is_le_device) + bta_hh_cb.le_cb_index[BTA_HH_GET_LE_CB_IDX(p_cb->hid_handle)] = BTA_HH_IDX_INVALID; + else +#endif bta_hh_cb.cb_index[p_cb->hid_handle] = BTA_HH_IDX_INVALID; } @@ -158,6 +163,9 @@ void bta_hh_update_di_info(tBTA_HH_DEV_CB *p_cb, UINT16 vendor_id, UINT16 produc p_cb->dscp_info.vendor_id = vendor_id; p_cb->dscp_info.product_id = product_id; p_cb->dscp_info.version = version; +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + p_cb->dscp_info.flag = flag; +#endif } /******************************************************************************* ** @@ -483,6 +491,17 @@ UINT8 bta_hh_dev_handle_to_cb_idx(UINT8 dev_handle) { UINT8 index = BTA_HH_IDX_INVALID; +#if BTA_HH_LE_INCLUDED == TRUE + if (BTA_HH_IS_LE_DEV_HDL(dev_handle)) + { + if (BTA_HH_IS_LE_DEV_HDL_VALID(dev_handle)) + index = bta_hh_cb.le_cb_index[BTA_HH_GET_LE_CB_IDX(dev_handle)]; +#if BTA_HH_DEBUG == TRUE + APPL_TRACE_DEBUG2("bta_hh_dev_handle_to_cb_idx dev_handle = %d index = %d", dev_handle, index); +#endif + } + else +#endif /* regular HID device checking */ if (dev_handle < BTA_HH_MAX_KNOWN ) index = bta_hh_cb.cb_index[dev_handle]; diff --git a/bta/include/bta_hh_api.h b/bta/include/bta_hh_api.h index 2cd29f406..893cac5cc 100644 --- a/bta/include/bta_hh_api.h +++ b/bta/include/bta_hh_api.h @@ -21,11 +21,15 @@ #include "bta_api.h" #include "hidh_api.h" +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) +#include "gatt_api.h" +#endif + /***************************************************************************** ** Constants and Type Definitions *****************************************************************************/ #ifndef BTA_HH_DEBUG -#define BTA_HH_DEBUG FALSE +#define BTA_HH_DEBUG TRUE #endif #ifndef BTA_HH_SSR_MAX_LATENCY_DEF @@ -47,12 +51,13 @@ #define BTA_HH_SET_PROTO_EVT 7 /* BTA_HhSetProtoMode callback */ #define BTA_HH_GET_IDLE_EVT 8 /* BTA_HhGetIdle comes callback */ #define BTA_HH_SET_IDLE_EVT 9 /* BTA_HhSetIdle finish callback */ -#define BTA_HH_GET_DSCP_EVT 10 /* Get report descripotor */ +#define BTA_HH_GET_DSCP_EVT 10 /* Get report descriptor */ #define BTA_HH_ADD_DEV_EVT 11 /* Add Device callback */ #define BTA_HH_RMV_DEV_EVT 12 /* remove device finished */ #define BTA_HH_VC_UNPLUG_EVT 13 /* virtually unplugged */ #define BTA_HH_DATA_EVT 15 #define BTA_HH_API_ERR_EVT 16 /* API error is caught */ +#define BTA_HH_UPDATE_SCPP_EVT 17 /* update scan paramter complete */ typedef UINT16 tBTA_HH_EVT; @@ -71,8 +76,14 @@ typedef UINT16 tBTA_HH_EVT; /* HID_HOST_MAX_DEVICES can not exceed 15 for th design of BTA HH */ #define BTA_HH_IDX_INVALID 0xff #define BTA_HH_MAX_KNOWN HID_HOST_MAX_DEVICES -#define BTA_HH_MAX_DEVICE HID_HOST_MAX_DEVICES +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) +/* GATT_MAX_PHY_CHANNEL can not exceed 14 for the design of BTA HH */ +#define BTA_HH_LE_MAX_KNOWN GATT_MAX_PHY_CHANNEL +#define BTA_HH_MAX_DEVICE (HID_HOST_MAX_DEVICES + GATT_MAX_PHY_CHANNEL) +#else +#define BTA_HH_MAX_DEVICE HID_HOST_MAX_DEVICES +#endif /* invalid device handle */ #define BTA_HH_INVALID_HANDLE 0xff @@ -115,6 +126,7 @@ enum BTA_HH_ERR_SDP, /* SDP error */ BTA_HH_ERR_PROTO, /* SET_Protocol error, only used in BTA_HH_OPEN_EVT callback */ + BTA_HH_ERR_DB_FULL, /* device database full error, used in BTA_HH_OPEN_EVT/BTA_HH_ADD_DEV_EVT */ BTA_HH_ERR_TOD_UNSPT, /* type of device not supported */ @@ -136,7 +148,6 @@ typedef UINT8 tBTA_HH_STATUS; #define BTA_HH_SEC_REQUIRED HID_SEC_REQUIRED typedef UINT16 tBTA_HH_ATTR_MASK; - /* supported type of device and corresponding application ID */ typedef struct { @@ -191,6 +202,12 @@ typedef struct UINT16 ssr_max_latency; /* SSR max latency, BTA_HH_SSR_PARAM_INVALID if unknown */ UINT16 ssr_min_tout; /* SSR min timeout, BTA_HH_SSR_PARAM_INVALID if unknown */ UINT8 ctry_code; /*Country Code.*/ +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) +#define BTA_HH_LE_REMOTE_WAKE 0x01 +#define BTA_HH_LE_NORMAL_CONN 0x02 + + UINT8 flag; +#endif tBTA_HH_DEV_DESCR descriptor; }tBTA_HH_DEV_DSCP_INFO; @@ -200,6 +217,11 @@ typedef struct BD_ADDR bda; /* HID device bd address */ tBTA_HH_STATUS status; /* operation status */ UINT8 handle; /* device handle */ +#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) + BOOLEAN le_hid; /* is LE devices? */ + BOOLEAN scps_supported; /* scan parameter service supported */ +#endif + } tBTA_HH_CONN; typedef tBTA_HH_CONN tBTA_HH_DEV_INFO; @@ -275,7 +297,8 @@ typedef union tBTA_HH_CBDATA dev_status; /* BTA_HH_CLOSE_EVT, BTA_HH_SET_PROTO_EVT BTA_HH_SET_RPT_EVT - BTA_HH_SET_IDLE_EVT */ + BTA_HH_SET_IDLE_EVT + BTA_HH_UPDATE_SCPP_EVT */ tBTA_HH_STATUS status; /* BTA_HH_ENABLE_EVT */ tBTA_HH_DEV_DSCP_INFO dscp_info; /* BTA_HH_GET_DSCP_EVT */ @@ -469,7 +492,6 @@ BTA_API extern void BTA_HhSendData(UINT8 dev_handle, BD_ADDR dev_bda, BT_HDR *p BTA_API extern void BTA_HhGetDscpInfo(UINT8 dev_handle); /******************************************************************************* -** ** Function BTA_HhAddDev ** ** Description Add a virtually cabled device into HID-Host device list @@ -511,6 +533,19 @@ BTA_API extern void BTA_HhRemoveDev(UINT8 dev_handle ); BTA_API extern void BTA_HhParseBootRpt(tBTA_HH_BOOT_RPT *p_data, UINT8 *p_report, UINT16 report_len); +#if BTA_HH_LE_INCLUDED == TRUE +/******************************************************************************* +** +** Function BTA_HhUpdateLeScanParam +** +** Description Update the scan paramteters if connected to a LE hid device as +** report host. +** +** Returns void +** +*******************************************************************************/ +BTA_API extern void BTA_HhUpdateLeScanParam(UINT8 dev_handle, UINT16 scan_int, UINT16 scan_win); +#endif /* test commands */ BTA_API extern void bta_hh_le_hid_read_rpt_clt_cfg(BD_ADDR bd_addr, UINT8 rpt_id); diff --git a/btif/src/btif_dm.c b/btif/src/btif_dm.c index 21512b585..0a71eecf4 100644 --- a/btif/src/btif_dm.c +++ b/btif/src/btif_dm.c @@ -139,6 +139,7 @@ extern bt_status_t btif_hf_execute_service(BOOLEAN b_enable); extern bt_status_t btif_av_execute_service(BOOLEAN b_enable); extern bt_status_t btif_hh_execute_service(BOOLEAN b_enable); extern int btif_hh_connect(bt_bdaddr_t *bd_addr); +extern void bta_gatt_convert_uuid16_to_uuid128(UINT8 uuid_128[LEN_UUID_128], UINT16 uuid_16); /****************************************************************************** @@ -285,7 +286,28 @@ BOOLEAN check_cod_hid(const bt_bdaddr_t *remote_bdaddr, uint32_t cod) if ((remote_cod & 0x700) == cod) return TRUE; } + return FALSE; +} + +BOOLEAN check_hid_le(const bt_bdaddr_t *remote_bdaddr) +{ + uint32_t remote_dev_type; + bt_property_t prop_name; + /* check if we already have it in our btif_storage cache */ + BTIF_STORAGE_FILL_PROPERTY(&prop_name,BT_PROPERTY_TYPE_OF_DEVICE, + sizeof(uint32_t), &remote_dev_type); + if (btif_storage_get_remote_device_property((bt_bdaddr_t *)remote_bdaddr, + &prop_name) == BT_STATUS_SUCCESS) + { + if (remote_dev_type == BT_DEVICE_DEVTYPE_BLE) + { + bdstr_t bdstr; + bd2str(remote_bdaddr, &bdstr); + if(btif_config_exist("Remote", bdstr, "HidAppId")) + return TRUE; + } + } return FALSE; } @@ -1106,12 +1128,15 @@ static void btif_dm_search_services_evt(UINT16 event, char *p_param) bond_state_changed(BT_STATUS_SUCCESS, &bd_addr, BT_BOND_STATE_BONDED); } - /* Also write this to the NVRAM */ - ret = btif_storage_set_remote_device_property(&bd_addr, &prop); - ASSERTC(ret == BT_STATUS_SUCCESS, "storing remote services failed", ret); - /* Send the event to the BTIF */ - HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb, - BT_STATUS_SUCCESS, &bd_addr, 1, &prop); + if(p_data->disc_res.num_uuids != 0) + { + /* Also write this to the NVRAM */ + ret = btif_storage_set_remote_device_property(&bd_addr, &prop); + ASSERTC(ret == BT_STATUS_SUCCESS, "storing remote services failed", ret); + /* Send the event to the BTIF */ + HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb, + BT_STATUS_SUCCESS, &bd_addr, 1, &prop); + } } break; @@ -1119,6 +1144,45 @@ static void btif_dm_search_services_evt(UINT16 event, char *p_param) /* fixme */ break; + case BTA_DM_DISC_BLE_RES_EVT: + BTIF_TRACE_DEBUG2("%s:, services 0x%x)", __FUNCTION__, + p_data->disc_ble_res.service.uu.uuid16); + bt_uuid_t uuid; + int i = 0; + int j = 15; + if (p_data->disc_ble_res.service.uu.uuid16 == UUID_SERVCLASS_LE_HID) + { + BTIF_TRACE_DEBUG1("%s: Found HOGP UUID",__FUNCTION__); + bt_property_t prop; + bt_bdaddr_t bd_addr; + char temp[256]; + + bta_gatt_convert_uuid16_to_uuid128(uuid.uu,p_data->disc_ble_res.service.uu.uuid16); + + while(i < j ) + { + unsigned char c = uuid.uu[j]; + uuid.uu[j] = uuid.uu[i]; + uuid.uu[i] = c; + i++; + j--; + } + + uuid_to_string(&uuid, temp); + BTIF_TRACE_ERROR1(" uuid:%s", temp); + + bdcpy(bd_addr.address, p_data->disc_ble_res.bd_addr); + prop.type = BT_PROPERTY_UUIDS; + prop.val = uuid.uu; + prop.len = MAX_UUID_SIZE; + + /* Send the event to the BTIF */ + HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb, + BT_STATUS_SUCCESS, &bd_addr, 1, &prop); + + } + break; + default: { ASSERTC(0, "unhandled search services event", event); @@ -1294,6 +1358,9 @@ static void btif_dm_upstreams_evt(UINT16 event, char* p_param) #if (defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE)) btif_hh_remove_device(bd_addr); #endif + #if (defined(BLE_INCLUDED) && (BLE_INCLUDED == TRUE)) + btif_storage_remove_ble_bonding_keys(&bd_addr); + #endif btif_storage_remove_bonded_device(&bd_addr); bond_state_changed(BT_STATUS_SUCCESS, &bd_addr, BT_BOND_STATE_NONE); break; diff --git a/gki/ulinux/data_types.h b/gki/ulinux/data_types.h index 8cd086aa6..33b8bb755 100644 --- a/gki/ulinux/data_types.h +++ b/gki/ulinux/data_types.h @@ -30,6 +30,8 @@ typedef unsigned char UINT8; typedef unsigned short UINT16; typedef unsigned long UINT32; +typedef unsigned long long UINT64; + typedef signed long INT32; typedef signed char INT8; typedef signed short INT16; diff --git a/include/bt_target.h b/include/bt_target.h index 0ce6971d8..4484ac7e2 100644 --- a/include/bt_target.h +++ b/include/bt_target.h @@ -187,6 +187,10 @@ #define BTA_HH_ROLE BTA_MASTER_ROLE_PREF #endif +#ifndef BTA_HH_LE_INCLUDED +#define BTA_HH_LE_INCLUDED TRUE +#endif + #ifndef BTA_AR_INCLUDED #define BTA_AR_INCLUDED TRUE #endif diff --git a/stack/Android.mk b/stack/Android.mk index f677ad239..10e9befbe 100755 --- a/stack/Android.mk +++ b/stack/Android.mk @@ -18,6 +18,7 @@ LOCAL_C_INCLUDES:= . \ $(LOCAL_PATH)/hid \ $(LOCAL_PATH)/sdp \ $(LOCAL_PATH)/smp \ + $(LOCAL_PATH)/srvc \ $(LOCAL_PATH)/../include \ $(LOCAL_PATH)/../gki/common \ $(LOCAL_PATH)/../gki/ulinux \ @@ -83,10 +84,6 @@ LOCAL_SRC_FILES:= \ ./mcap/mca_csm.c \ ./mcap/mca_cact.c \ ./mcap/mca_api.c \ - ./gap/gap_ble.c \ - ./gap/gap_api.c \ - ./gap/gap_utils.c \ - ./gap/gap_conn.c \ ./gatt/gatt_sr.c \ ./gatt/gatt_cl.c \ ./gatt/gatt_api.c \ @@ -103,12 +100,12 @@ LOCAL_SRC_FILES:= \ ./avct/avct_lcb_act.c \ ./smp/smp_main.c \ ./smp/smp_l2c.c \ - ./smp/aes.c \ ./smp/smp_cmac.c \ ./smp/smp_utils.c \ ./smp/smp_act.c \ ./smp/smp_keys.c \ ./smp/smp_api.c \ + ./smp/aes.c \ ./avdt/avdt_ccb.c \ ./avdt/avdt_scb_act.c \ ./avdt/avdt_msg.c \ @@ -124,6 +121,12 @@ LOCAL_SRC_FILES:= \ ./sdp/sdp_api.c \ ./sdp/sdp_discovery.c \ ./pan/pan_main.c \ + ./srvc/srvc_battery.c \ + ./srvc/srvc_battery_int.h \ + ./srvc/srvc_dis.c \ + ./srvc/srvc_dis_int.h \ + ./srvc/srvc_eng.c \ + ./srvc/srvc_eng_int.h \ ./pan/pan_api.c \ ./pan/pan_utils.c \ ./btu/btu_hcif.c \ @@ -136,7 +139,11 @@ LOCAL_SRC_FILES:= \ ./l2cap/l2c_utils.c \ ./l2cap/l2c_csm.c \ ./l2cap/l2c_link.c \ - ./l2cap/l2c_ble.c + ./l2cap/l2c_ble.c \ + ./gap/gap_api.c \ + ./gap/gap_ble.c \ + ./gap/gap_conn.c \ + ./gap/gap_utils.c LOCAL_MODULE := libbt-brcm_stack LOCAL_MODULE_TAGS := optional diff --git a/stack/hid/hidh_api.c b/stack/hid/hidh_api.c index 4f048fcb8..1868671f9 100644 --- a/stack/hid/hidh_api.c +++ b/stack/hid/hidh_api.c @@ -33,6 +33,7 @@ #include "hidh_int.h" #include "btm_api.h" #include "btu.h" +#include "btm_int.h" #if HID_DYNAMIC_MEMORY == FALSE tHID_HOST_CTB hh_cb; @@ -336,7 +337,6 @@ tHID_STATUS HID_HostAddDev ( BD_ADDR addr, UINT16 attr_mask, UINT8 *handle ) { int i; /* Find an entry for this device in hh_cb.devices array */ - if( !hh_cb.reg_flag ) return (HID_ERR_NOT_REGISTERED); @@ -367,7 +367,8 @@ tHID_STATUS HID_HostAddDev ( BD_ADDR addr, UINT16 attr_mask, UINT8 *handle ) hh_cb.devices[i].conn_tries = 0 ; } - hh_cb.devices[i].attr_mask = attr_mask; + if (attr_mask != HID_ATTR_MASK_IGNORE) + hh_cb.devices[i].attr_mask = attr_mask; *handle = i; @@ -500,42 +501,42 @@ tHID_STATUS HID_HostCloseDev( UINT8 dev_handle ) tHID_STATUS HID_HostSetSecurityLevel( char serv_name[], UINT8 sec_lvl ) { - if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HID_SEC_CTRL, + if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HIDH_SEC_CTRL, sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_SEC_CHN)) { HIDH_TRACE_ERROR0 ("Security Registration 1 failed"); return (HID_ERR_NO_RESOURCES); } - if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HID_SEC_CTRL, + if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HIDH_SEC_CTRL, sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_SEC_CHN)) { HIDH_TRACE_ERROR0 ("Security Registration 2 failed"); return (HID_ERR_NO_RESOURCES); } - if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HID_NOSEC_CTRL, + if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HIDH_NOSEC_CTRL, BTM_SEC_NONE, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_NOSEC_CHN)) { HIDH_TRACE_ERROR0 ("Security Registration 3 failed"); return (HID_ERR_NO_RESOURCES); } - if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HID_NOSEC_CTRL, + if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HIDH_NOSEC_CTRL, BTM_SEC_NONE, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_NOSEC_CHN)) { HIDH_TRACE_ERROR0 ("Security Registration 4 failed"); return (HID_ERR_NO_RESOURCES); } - if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HID_INTR, + if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HIDH_INTR, BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, 0)) { HIDH_TRACE_ERROR0 ("Security Registration 5 failed"); return (HID_ERR_NO_RESOURCES); } - if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HID_INTR, + if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HIDH_INTR, BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, 0)) { HIDH_TRACE_ERROR0 ("Security Registration 6 failed"); @@ -544,3 +545,55 @@ tHID_STATUS HID_HostSetSecurityLevel( char serv_name[], UINT8 sec_lvl ) return( HID_SUCCESS ); } + +/****************************************************************************** +** +** Function hid_known_hid_device +** +** Description check if this device is of type HID Device +** +** Returns TRUE if device is HID Device else FALSE +** +*******************************************************************************/ +BOOLEAN hid_known_hid_device (BD_ADDR bd_addr) +{ + UINT8 i; + tBTM_INQ_INFO *p_inq_info = BTM_InqDbRead(bd_addr); + + if ( !hh_cb.reg_flag ) + return FALSE; + + /* First check for class of device , if Inq DB has information about this device*/ + if (p_inq_info != NULL) + { + /* Check if remote major device class is of type BTM_COD_MAJOR_PERIPHERAL */ + if ((p_inq_info->results.dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) + == BTM_COD_MAJOR_PERIPHERAL ) + { + HIDH_TRACE_DEBUG0("hid_known_hid_device:dev found in InqDB & COD matches HID dev"); + return TRUE; + } + } + else + { + /* Look for this device in security device DB */ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + if ((p_dev_rec != NULL) && + ((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) == BTM_COD_MAJOR_PERIPHERAL )) + { + HIDH_TRACE_DEBUG0("hid_known_hid_device:dev found in SecDevDB & COD matches HID dev"); + return TRUE; + } + } + + /* Find an entry for this device in hh_cb.devices array */ + for ( i=0; ictrl_cid != 0) || (p_hcon->intr_cid != 0)) { @@ -201,18 +201,13 @@ static void hidh_l2cif_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 p { tHID_CONN *p_hcon; BOOLEAN bAccept = TRUE; - int i; + UINT8 i = HID_HOST_MAX_DEVICES; tHID_HOST_DEV_CTB *p_dev; - HIDH_TRACE_EVENT2 ("HID - Rcvd L2CAP conn ind, PSM: 0x%04x CID 0x%x", psm, l2cap_cid); + HIDH_TRACE_EVENT2 ("HID-Host Rcvd L2CAP conn ind, PSM: 0x%04x CID 0x%x", psm, l2cap_cid); - for( i=0; i < HID_HOST_MAX_DEVICES; i++ ) - { - if( hh_cb.devices[i].in_use && (!memcmp(bd_addr, hh_cb.devices[i].addr, sizeof(BD_ADDR))) ) - break; - } - - if (i >= HID_HOST_MAX_DEVICES) + /* always add incoming connection device into HID database by default */ + if (HID_HostAddDev(bd_addr, HID_SEC_REQUIRED, &i) != HID_SUCCESS) { L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_SECURITY_BLOCK, 0); return; @@ -226,12 +221,13 @@ static void hidh_l2cif_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 p { if (p_hcon->ctrl_cid == 0) { - HIDH_TRACE_WARNING0 ("HID - Rcvd INTR L2CAP conn ind, but no CTL channel"); + HIDH_TRACE_WARNING0 ("HID-Host Rcvd INTR L2CAP conn ind, but no CTL channel"); bAccept = FALSE; } if (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) { - HIDH_TRACE_WARNING1 ("HID - Rcvd INTR L2CAP conn ind, wrong state: %d", p_hcon->conn_state); + HIDH_TRACE_WARNING1 ("HID-Host Rcvd INTR L2CAP conn ind, wrong state: %d", + p_hcon->conn_state); bAccept = FALSE; } } @@ -243,7 +239,8 @@ static void hidh_l2cif_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 p #else if (p_hcon->conn_state != HID_CONN_STATE_UNUSED) { - HIDH_TRACE_WARNING1 ("HID - Rcvd CTL L2CAP conn ind, wrong state: %d", p_hcon->conn_state); + HIDH_TRACE_WARNING1 ("HID-Host - Rcvd CTL L2CAP conn ind, wrong state: %d", + p_hcon->conn_state); bAccept = FALSE; } #endif @@ -284,7 +281,8 @@ static void hidh_l2cif_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 p /* Send a Configuration Request. */ L2CA_ConfigReq (l2cap_cid, &hh_cb.l2cap_cfg); - HIDH_TRACE_EVENT2 ("HID - Rcvd L2CAP conn ind, sent config req, PSM: 0x%04x CID 0x%x", psm, l2cap_cid); + HIDH_TRACE_EVENT2 ("HID-Host Rcvd L2CAP conn ind, sent config req, PSM: 0x%04x CID 0x%x", + psm, l2cap_cid); } /******************************************************************************* @@ -300,7 +298,8 @@ void hidh_proc_repage_timeout (TIMER_LIST_ENT *p_tle) { hidh_conn_initiate( (UINT8) p_tle->param ) ; hh_cb.devices[p_tle->param].conn_tries++; - hh_cb.callback( (UINT8) p_tle->param, HID_HDEV_EVT_RETRYING, hh_cb.devices[p_tle->param].conn_tries, NULL ) ; + hh_cb.callback( (UINT8) p_tle->param, hh_cb.devices[p_tle->param].addr, + HID_HDEV_EVT_RETRYING, hh_cb.devices[p_tle->param].conn_tries, NULL ) ; } /******************************************************************************* @@ -325,16 +324,16 @@ void hidh_sec_check_complete_orig (BD_ADDR bd_addr, void *p_ref_data, UINT8 res) dhandle = ((UINT32)p_dev - (UINT32)&(hh_cb.devices[0]))/ sizeof(tHID_HOST_DEV_CTB); if( res == BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY ) { - HIDH_TRACE_EVENT0 ("HID - Originator security pass."); + HIDH_TRACE_EVENT0 ("HID-Host Originator security pass."); p_dev->conn.disc_reason = HID_SUCCESS; /* Authentication passed. Reset disc_reason (from HID_ERR_AUTH_FAILED) */ /* Check if L2CAP started the connection process for interrupt channel */ if ((p_dev->conn.intr_cid = L2CA_ConnectReq (HID_PSM_INTERRUPT, hh_cb.devices[dhandle].addr)) == 0) { - HIDH_TRACE_WARNING0 ("HID - INTR Originate failed"); + HIDH_TRACE_WARNING0 ("HID-Host INTR Originate failed"); reason = HID_L2CAP_REQ_FAIL ; hidh_conn_disconnect (dhandle); - hh_cb.callback( dhandle, HID_HDEV_EVT_CLOSE, reason, NULL ) ; + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, reason, NULL ) ; return; } else @@ -394,7 +393,7 @@ static void hidh_l2cif_connect_cfm (UINT16 l2cap_cid, UINT16 result) || ((l2cap_cid == p_hcon->ctrl_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_CTRL)) || ((l2cap_cid == p_hcon->intr_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR))) { - HIDH_TRACE_WARNING1 ("HID - Rcvd unexpected conn cnf, CID 0x%x ", l2cap_cid); + HIDH_TRACE_WARNING1 ("HID-Host Rcvd unexpected conn cnf, CID 0x%x ", l2cap_cid); return; } @@ -418,7 +417,7 @@ static void hidh_l2cif_connect_cfm (UINT16 l2cap_cid, UINT16 result) #endif { reason = HID_L2CAP_CONN_FAIL | (UINT32) result ; - hh_cb.callback( dhandle, HID_HDEV_EVT_CLOSE, reason, NULL ) ; + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, reason, NULL ) ; } return; } @@ -442,7 +441,7 @@ static void hidh_l2cif_connect_cfm (UINT16 l2cap_cid, UINT16 result) /* Send a Configuration Request. */ L2CA_ConfigReq (l2cap_cid, &hh_cb.l2cap_cfg); - HIDH_TRACE_EVENT1 ("HID - got CTRL conn cnf, sent cfg req, CID: 0x%x", l2cap_cid); + HIDH_TRACE_EVENT1 ("HID-Host got CTRL conn cnf, sent cfg req, CID: 0x%x", l2cap_cid); return; } @@ -471,11 +470,11 @@ static void hidh_l2cif_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) if (p_hcon == NULL) { - HIDH_TRACE_WARNING1 ("HID - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid); + HIDH_TRACE_WARNING1 ("HID-Host Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid); return; } - HIDH_TRACE_EVENT1 ("HID - Rcvd cfg ind, sent cfg cfm, CID: 0x%x", l2cap_cid); + HIDH_TRACE_EVENT1 ("HID-Host Rcvd cfg ind, sent cfg cfm, CID: 0x%x", l2cap_cid); /* Remember the remote MTU size */ if ((!p_cfg->mtu_present) || (p_cfg->mtu > HID_HOST_MTU)) @@ -502,7 +501,7 @@ static void hidh_l2cif_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) p_hcon->conn_state = HID_CONN_STATE_CONNECTED; hh_cb.devices[dhandle].state = HID_DEV_CONNECTED; - hh_cb.callback( dhandle, HID_HDEV_EVT_OPEN, 0, NULL ) ; + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_OPEN, 0, NULL ) ; } } @@ -523,7 +522,7 @@ static void hidh_l2cif_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) tHID_CONN *p_hcon = NULL; UINT32 reason; - HIDH_TRACE_EVENT2 ("HID - Rcvd cfg cfm, CID: 0x%x Result: %d", l2cap_cid, p_cfg->result); + HIDH_TRACE_EVENT2 ("HID-Host Rcvd cfg cfm, CID: 0x%x Result: %d", l2cap_cid, p_cfg->result); /* Find CCB based on CID */ if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) @@ -531,7 +530,7 @@ static void hidh_l2cif_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) if (p_hcon == NULL) { - HIDH_TRACE_WARNING1 ("HID - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid); + HIDH_TRACE_WARNING1 ("HID-Host Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid); return; } @@ -540,7 +539,7 @@ static void hidh_l2cif_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) { hidh_conn_disconnect (dhandle); reason = HID_L2CAP_CFG_FAIL | (UINT32) p_cfg->result ; - hh_cb.callback( dhandle, HID_HDEV_EVT_CLOSE, reason, NULL ) ; + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, reason, NULL ) ; return; } @@ -556,7 +555,7 @@ static void hidh_l2cif_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg) p_hcon->conn_state = HID_CONN_STATE_CONNECTED; hh_cb.devices[dhandle].state = HID_DEV_CONNECTED; - hh_cb.callback( dhandle, HID_HDEV_EVT_OPEN, 0, NULL ) ; + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_OPEN, 0, NULL ) ; } } @@ -584,14 +583,14 @@ static void hidh_l2cif_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed) if (p_hcon == NULL) { - HIDH_TRACE_WARNING1 ("HID - Rcvd L2CAP disc, unknown CID: 0x%x", l2cap_cid); + HIDH_TRACE_WARNING1 ("HID-Host Rcvd L2CAP disc, unknown CID: 0x%x", l2cap_cid); return; } if (ack_needed) L2CA_DisconnectRsp (l2cap_cid); - HIDH_TRACE_EVENT1 ("HID - Rcvd L2CAP disc, CID: 0x%x", l2cap_cid); + HIDH_TRACE_EVENT1 ("HID-Host Rcvd L2CAP disc, CID: 0x%x", l2cap_cid); p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING; @@ -636,7 +635,7 @@ static void hidh_l2cif_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed) hid_close_evt_reason = HID_ERR_AUTH_FAILED; } - hh_cb.callback( dhandle, HID_HDEV_EVT_CLOSE, hid_close_evt_reason, NULL ) ; + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, hid_close_evt_reason, NULL ) ; } } } @@ -662,11 +661,11 @@ static void hidh_l2cif_disconnect_cfm (UINT16 l2cap_cid, UINT16 result) if (p_hcon == NULL) { - HIDH_TRACE_WARNING1 ("HID - Rcvd L2CAP disc cfm, unknown CID: 0x%x", l2cap_cid); + HIDH_TRACE_WARNING1 ("HID-Host Rcvd L2CAP disc cfm, unknown CID: 0x%x", l2cap_cid); return; } - HIDH_TRACE_EVENT1 ("HID - Rcvd L2CAP disc cfm, CID: 0x%x", l2cap_cid); + HIDH_TRACE_EVENT1 ("HID-Host Rcvd L2CAP disc cfm, CID: 0x%x", l2cap_cid); if (l2cap_cid == p_hcon->ctrl_cid) p_hcon->ctrl_cid = 0; @@ -677,7 +676,7 @@ static void hidh_l2cif_disconnect_cfm (UINT16 l2cap_cid, UINT16 result) { hh_cb.devices[dhandle].state = HID_DEV_NO_CONN; p_hcon->conn_state = HID_CONN_STATE_UNUSED; - hh_cb.callback( dhandle, HID_HDEV_EVT_CLOSE, p_hcon->disc_reason, NULL ) ; + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, p_hcon->disc_reason, NULL ) ; } } @@ -702,11 +701,11 @@ static void hidh_l2cif_cong_ind (UINT16 l2cap_cid, BOOLEAN congested) if (p_hcon == NULL) { - HIDH_TRACE_WARNING1 ("HID - Rcvd L2CAP congestion status, unknown CID: 0x%x", l2cap_cid); + HIDH_TRACE_WARNING1 ("HID-Host Rcvd L2CAP congestion status, unknown CID: 0x%x", l2cap_cid); return; } - HIDH_TRACE_EVENT2 ("HID - Rcvd L2CAP congestion status, CID: 0x%x Cong: %d", l2cap_cid, congested); + HIDH_TRACE_EVENT2 ("HID-Host Rcvd L2CAP congestion status, CID: 0x%x Cong: %d", l2cap_cid, congested); if (congested) p_hcon->conn_flags |= HID_CONN_FLAGS_CONGESTED; @@ -740,7 +739,7 @@ static void hidh_l2cif_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg) UINT8 dhandle; tHID_CONN *p_hcon = NULL; - HIDH_TRACE_DEBUG1 ("HID - hidh_l2cif_data_ind [l2cap_cid=0x%04x]", l2cap_cid); + HIDH_TRACE_DEBUG1 ("HID-Host hidh_l2cif_data_ind [l2cap_cid=0x%04x]", l2cap_cid); /* Find CCB based on CID */ if ((dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES) @@ -748,7 +747,7 @@ static void hidh_l2cif_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg) if (p_hcon == NULL) { - HIDH_TRACE_WARNING1 ("HID - Rcvd L2CAP data, unknown CID: 0x%x", l2cap_cid); + HIDH_TRACE_WARNING1 ("HID-Host Rcvd L2CAP data, unknown CID: 0x%x", l2cap_cid); GKI_freebuf (p_msg); return; } @@ -766,7 +765,7 @@ static void hidh_l2cif_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg) switch (ttype) { case HID_TRANS_HANDSHAKE: - hh_cb.callback(dhandle, HID_HDEV_EVT_HANDSHAKE, param, NULL); + hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_HANDSHAKE, param, NULL); GKI_freebuf (p_msg); break; @@ -776,7 +775,7 @@ static void hidh_l2cif_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg) case HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG: hidh_conn_disconnect( dhandle ) ; /* Device is unplugging from us. Tell USB */ - hh_cb.callback(dhandle, HID_HDEV_EVT_VC_UNPLUG, 0, NULL); + hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_VC_UNPLUG, 0, NULL); break; default: @@ -789,13 +788,13 @@ static void hidh_l2cif_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg) case HID_TRANS_DATA: evt = (hh_cb.devices[dhandle].conn.intr_cid == l2cap_cid) ? HID_HDEV_EVT_INTR_DATA : HID_HDEV_EVT_CTRL_DATA; - hh_cb.callback(dhandle, evt, rep_type, p_msg); + hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, evt, rep_type, p_msg); break; case HID_TRANS_DATAC: evt = (hh_cb.devices[dhandle].conn.intr_cid == l2cap_cid) ? HID_HDEV_EVT_INTR_DATC : HID_HDEV_EVT_CTRL_DATC; - hh_cb.callback(dhandle, evt, rep_type, p_msg); + hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, evt, rep_type, p_msg); break; default: @@ -949,7 +948,7 @@ tHID_STATUS hidh_conn_snd_data (UINT8 dhandle, UINT8 trans_type, UINT8 param, *******************************************************************************/ tHID_STATUS hidh_conn_initiate (UINT8 dhandle) { - UINT8 service_id = BTM_SEC_SERVICE_HID_NOSEC_CTRL; + UINT8 service_id = BTM_SEC_SERVICE_HIDH_NOSEC_CTRL; UINT32 mx_chan_id = HID_NOSEC_CHN; tHID_HOST_DEV_CTB *p_dev = &hh_cb.devices[dhandle]; @@ -966,7 +965,7 @@ tHID_STATUS hidh_conn_initiate (UINT8 dhandle) if(p_dev->attr_mask & HID_SEC_REQUIRED) { - service_id = BTM_SEC_SERVICE_HID_SEC_CTRL; + service_id = BTM_SEC_SERVICE_HIDH_SEC_CTRL; mx_chan_id = HID_SEC_CHN; } BTM_SetOutService (p_dev->addr, service_id, mx_chan_id); @@ -974,8 +973,9 @@ tHID_STATUS hidh_conn_initiate (UINT8 dhandle) /* Check if L2CAP started the connection process */ if ((p_dev->conn.ctrl_cid = L2CA_ConnectReq (HID_PSM_CONTROL, p_dev->addr)) == 0) { - HIDH_TRACE_WARNING0 ("HID - Originate failed"); - hh_cb.callback( dhandle, HID_HDEV_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL ) ; + HIDH_TRACE_WARNING0 ("HID-Host Originate failed"); + hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, + HID_ERR_L2CAP_FAILED, NULL ) ; } else { diff --git a/stack/include/btm_api.h b/stack/include/btm_api.h index 3a7ed1d5f..2181f31f9 100644 --- a/stack/include/btm_api.h +++ b/stack/include/btm_api.h @@ -1185,9 +1185,9 @@ typedef void (tBTM_ESCO_CBACK) (tBTM_ESCO_EVT event, tBTM_ESCO_EVT_DATA *p_data) #define BTM_SEC_SERVICE_TE_PHONE_ACCESS 30 #define BTM_SEC_SERVICE_ME_PHONE_ACCESS 31 -#define BTM_SEC_SERVICE_HID_SEC_CTRL 32 -#define BTM_SEC_SERVICE_HID_NOSEC_CTRL 33 -#define BTM_SEC_SERVICE_HID_INTR 34 +#define BTM_SEC_SERVICE_HIDH_SEC_CTRL 32 +#define BTM_SEC_SERVICE_HIDH_NOSEC_CTRL 33 +#define BTM_SEC_SERVICE_HIDH_INTR 34 #define BTM_SEC_SERVICE_BIP 35 #define BTM_SEC_SERVICE_BIP_REF 36 #define BTM_SEC_SERVICE_AVDTP 37 @@ -1272,9 +1272,9 @@ typedef void (tBTM_ESCO_CBACK) (tBTM_ESCO_EVT event, tBTM_ESCO_EVT_DATA *p_data) #define BTM_SEC_TRUST_ME_PHONE_ACCESS (1 << BTM_SEC_SERVICE_ME_PHONE_ACCESS) /* 0..31 bits of mask[1] (Most Significant Word) */ -#define BTM_SEC_TRUST_HID_CTRL (1 << (BTM_SEC_SERVICE_HID_SEC_CTRL - 32)) -#define BTM_SEC_TRUST_HID_NOSEC_CTRL (1 << (BTM_SEC_SERVICE_HID_NOSEC_CTRL - 32)) -#define BTM_SEC_TRUST_HID_INTR (1 << (BTM_SEC_SERVICE_HID_INTR - 32)) +#define BTM_SEC_TRUST_HIDH_CTRL (1 << (BTM_SEC_SERVICE_HIDH_SEC_CTRL - 32)) +#define BTM_SEC_TRUST_HIDH_NOSEC_CTRL (1 << (BTM_SEC_SERVICE_HIDH_NOSEC_CTRL - 32)) +#define BTM_SEC_TRUST_HIDH_INTR (1 << (BTM_SEC_SERVICE_HIDH_INTR - 32)) #define BTM_SEC_TRUST_BIP (1 << (BTM_SEC_SERVICE_BIP - 32)) #define BTM_SEC_TRUST_BIP_REF (1 << (BTM_SEC_SERVICE_BIP_REF - 32)) #define BTM_SEC_TRUST_AVDTP (1 << (BTM_SEC_SERVICE_AVDTP - 32)) diff --git a/stack/include/gattdefs.h b/stack/include/gattdefs.h index e2150f2a6..38e18f3d4 100644 --- a/stack/include/gattdefs.h +++ b/stack/include/gattdefs.h @@ -57,4 +57,69 @@ #define GATT_UUID_GATT_SRV_CHGD 0x2A05 /* Attribute Protocol Test */ +/* Link Loss Service */ +#define GATT_UUID_ALERT_LEVEL 0x2A06 /* Alert Level */ +#define GATT_UUID_TX_POWER_LEVEL 0x2A07 /* TX power level */ + +/* Time Profile */ +/* Current Time Service */ +#define GATT_UUID_CURRENT_TIME 0x2A2B /* Current Time */ +#define GATT_UUID_LOCAL_TIME_INFO 0x2A0F /* Local time info */ +#define GATT_UUID_REF_TIME_INFO 0x2A14 /* reference time information */ + +/* NwA Profile */ +#define GATT_UUID_NW_STATUS 0x2A18 /* network availability status */ +#define GATT_UUID_NW_TRIGGER 0x2A1A /* Network availability trigger */ + +/* phone alert */ +#define GATT_UUID_ALERT_STATUS 0x2A40 /* alert status */ +#define GATT_UUID_RINGER_CP 0x2A42 /* ringer control point */ +#define GATT_UUID_RINGER_SETTING 0x2A41 /* ringer setting */ + +/* Glucose Service */ +#define GATT_UUID_GM_MEASUREMENT 0x2A18 +#define GATT_UUID_GM_CONTEXT 0x2A34 +#define GATT_UUID_GM_CONTROL_POINT 0x2A52 +#define GATT_UUID_GM_FEATURE 0x2A51 + +/* device infor characteristic */ +#define GATT_UUID_SYSTEM_ID 0x2A23 +#define GATT_UUID_MODEL_NUMBER_STR 0x2A24 +#define GATT_UUID_SERIAL_NUMBER_STR 0x2A25 +#define GATT_UUID_FW_VERSION_STR 0x2A26 +#define GATT_UUID_HW_VERSION_STR 0x2A27 +#define GATT_UUID_SW_VERSION_STR 0x2A28 +#define GATT_UUID_MANU_NAME 0x2A29 +#define GATT_UUID_IEEE_DATA 0x2A2A +#define GATT_UUID_PNP_ID 0x2A50 + +/* HID characteristics */ +#define GATT_UUID_HID_INFORMATION 0x2A4A +#define GATT_UUID_HID_REPORT_MAP 0x2A4B +#define GATT_UUID_HID_CONTROL_POINT 0x2A4C +#define GATT_UUID_HID_REPORT 0x2A4D +#define GATT_UUID_HID_PROTO_MODE 0x2A4E +#define GATT_UUID_HID_BT_KB_INPUT 0x2A22 +#define GATT_UUID_HID_BT_KB_OUTPUT 0x2A32 +#define GATT_UUID_HID_BT_MOUSE_INPUT 0x2A33 + +/* Battery Service char */ +#define GATT_UUID_BATTERY_LEVEL 0x2A19 + +#define GATT_UUID_SC_CONTROL_POINT 0x2A55 +#define GATT_UUID_SENSOR_LOCATION 0x2A5D + +/* RUNNERS SPEED AND CADENCE SERVICE */ +#define GATT_UUID_RSC_MEASUREMENT 0x2A53 +#define GATT_UUID_RSC_FEATURE 0x2A54 + +/* CYCLING SPEED AND CADENCE SERVICE */ +#define GATT_UUID_CSC_MEASUREMENT 0x2A5B +#define GATT_UUID_CSC_FEATURE 0x2A5C + + +/* Scan Parameter charatceristics */ +#define GATT_UUID_SCAN_INT_WINDOW 0x2A4F +#define GATT_UUID_SCAN_REFRESH 0x2A31 + #endif diff --git a/stack/include/hiddefs.h b/stack/include/hiddefs.h index e29a4c4a1..bf5d0212e 100644 --- a/stack/include/hiddefs.h +++ b/stack/include/hiddefs.h @@ -49,6 +49,7 @@ enum HID_ERR_L2CAP_FAILED, HID_ERR_AUTH_FAILED, HID_ERR_SDP_BUSY, + HID_ERR_GATT, HID_ERR_INVALID = 0xFF }; diff --git a/stack/include/hidh_api.h b/stack/include/hidh_api.h index 466263032..27e8ac5de 100644 --- a/stack/include/hidh_api.h +++ b/stack/include/hidh_api.h @@ -42,6 +42,7 @@ enum { #define HID_SSR_MIN_TOUT 0x0100 #define HID_SEC_REQUIRED 0x8000 +#define HID_ATTR_MASK_IGNORE 0 /***************************************************************************** @@ -76,6 +77,7 @@ enum HID_HDEV_EVT_VC_UNPLUG }; typedef void (tHID_HOST_DEV_CALLBACK) (UINT8 dev_handle, + BD_ADDR addr, UINT8 event, /* Event from HID-DEVICE. */ UINT32 data, /* Integer data corresponding to the event.*/ BT_HDR *p_buf ); /* Pointer data corresponding to the event. */ @@ -205,6 +207,18 @@ HID_API extern tHID_STATUS HID_HostSetSecurityLevel( char serv_name[], UINT8 sec /******************************************************************************* ** +** Function hid_known_hid_device +** +** Description This function checks if this device is of type HID Device +** +** Returns TRUE if device exists else FALSE +** +*******************************************************************************/ +BOOLEAN hid_known_hid_device (BD_ADDR bd_addr); + + +/******************************************************************************* +** ** Function HID_HostSetTraceLevel ** ** Description This function sets the trace level for HID Host. If called with diff --git a/stack/include/srvc_api.h b/stack/include/srvc_api.h new file mode 100644 index 000000000..b67a14fb3 --- /dev/null +++ b/stack/include/srvc_api.h @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Name: srvc_api.h +** Function this file contains the definitions for the DIS API +** +** +** Copyright (c) 1999-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +******************************************************************************/ +#ifndef SRVC_DIS_API_H +#define SRVC_DIS_API_H + +#include "bt_target.h" +#include "gatt_api.h" +#include "gattdefs.h" + +#define DIS_SUCCESS GATT_SUCCESS +#define DIS_ILLEGAL_PARAM GATT_ILLEGAL_PARAMETER +#define DIS_NO_RESOURCES GATT_NO_RESOURCES +typedef UINT8 tDIS_STATUS; + + +/***************************************************************************** +** Data structure for DIS +*****************************************************************************/ + +#define DIS_ATTR_SYS_ID_BIT 0x0001 +#define DIS_ATTR_MODEL_NUM_BIT 0x0002 +#define DIS_ATTR_SERIAL_NUM_BIT 0x0004 +#define DIS_ATTR_FW_NUM_BIT 0x0008 +#define DIS_ATTR_HW_NUM_BIT 0x0010 +#define DIS_ATTR_SW_NUM_BIT 0x0020 +#define DIS_ATTR_MANU_NAME_BIT 0x0040 +#define DIS_ATTR_IEEE_DATA_BIT 0x0080 +#define DIS_ATTR_PNP_ID_BIT 0x0100 +typedef UINT16 tDIS_ATTR_MASK; + +#define DIS_ATTR_ALL_MASK 0xffff + +typedef tDIS_ATTR_MASK tDIS_ATTR_BIT ; + +typedef struct +{ + UINT16 len; + UINT8 *p_data; +}tDIS_STRING; + +typedef struct +{ + UINT16 vendor_id; + UINT16 product_id; + UINT16 product_version; + UINT8 vendor_id_src; + +}tDIS_PNP_ID; + +typedef union +{ + UINT64 system_id; + tDIS_PNP_ID pnp_id; + tDIS_STRING data_str; +}tDIS_ATTR; + +#define DIS_MAX_STRING_DATA 7 + +typedef struct +{ + UINT16 attr_mask; + UINT64 system_id; + tDIS_PNP_ID pnp_id; + UINT8 *data_string[DIS_MAX_STRING_DATA]; +}tDIS_VALUE; + + +typedef void (tDIS_READ_CBACK)(BD_ADDR addr, tDIS_VALUE *p_dis_value); + +/***************************************************************************** +** Data structure used by Battery Service +*****************************************************************************/ +typedef struct +{ + BD_ADDR remote_bda; + BOOLEAN need_rsp; + UINT16 clt_cfg; +}tBA_WRITE_DATA; + +#define BA_READ_CLT_CFG_REQ 1 +#define BA_READ_PRE_FMT_REQ 2 +#define BA_READ_RPT_REF_REQ 3 +#define BA_READ_LEVEL_REQ 4 +#define BA_WRITE_CLT_CFG_REQ 5 + +typedef void (tBA_CBACK)(UINT8 app_id, UINT8 event, tBA_WRITE_DATA *p_data); + +#define BA_LEVEL_NOTIFY 0x01 +#define BA_LEVEL_PRE_FMT 0x02 +#define BA_LEVEL_RPT_REF 0x04 +typedef UINT8 tBA_LEVEL_DESCR; + +typedef struct +{ + BOOLEAN is_pri; + tBA_LEVEL_DESCR ba_level_descr; + tGATT_TRANSPORT transport; + tBA_CBACK *p_cback; + +}tBA_REG_INFO; + +typedef union +{ + UINT8 ba_level; + UINT16 clt_cfg; + tGATT_CHAR_RPT_REF rpt_ref; + tGATT_CHAR_PRES pres_fmt; +}tBA_RSP_DATA; + +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif +/***************************************************************************** +** Service Engine API +*****************************************************************************/ +/******************************************************************************* +** +** Function srvc_eng_init +** +** Description Initializa the GATT Service engine, register a GATT application +** as for a central service management. +** +*******************************************************************************/ + GATT_API extern tGATT_STATUS srvc_eng_init (void); + + +/***************************************************************************** +** DIS Server Function +*****************************************************************************/ + +/******************************************************************************* +** +** Function DIS_SrInit +** +** Description Initializa the Device Information Service Server. +** +*******************************************************************************/ + GATT_API extern tDIS_STATUS DIS_SrInit (tDIS_ATTR_MASK dis_attr_mask); +/******************************************************************************* +** +** Function DIS_SrUpdate +** +** Description Update the DIS server attribute values +** +*******************************************************************************/ + GATT_API extern tDIS_STATUS DIS_SrUpdate(tDIS_ATTR_BIT dis_attr_bit, tDIS_ATTR *p_info); +/***************************************************************************** +** DIS Client Function +*****************************************************************************/ +/******************************************************************************* +** +** Function DIS_ReadDISInfo +** +** Description Read remote device DIS information. +** +** Returns void +** +*******************************************************************************/ + GATT_API extern BOOLEAN DIS_ReadDISInfo(BD_ADDR peer_bda, tDIS_READ_CBACK *p_cback); + +/******************************************************************************* +** BATTERY SERVICE API +*******************************************************************************/ +/******************************************************************************* +** +** Function Battery_Instantiate +** +** Description Instantiate a Battery service +** +*******************************************************************************/ + GATT_API extern UINT16 Battery_Instantiate (UINT8 app_id, tBA_REG_INFO *p_reg_info); + +/******************************************************************************* +** +** Function Battery_Rsp +** +** Description Respond to a battery service request +** +*******************************************************************************/ + GATT_API extern void Battery_Rsp (UINT8 app_id, tGATT_STATUS st, UINT8 event, tBA_RSP_DATA *p_rsp); +/******************************************************************************* +** +** Function Battery_Notify +** +** Description Send battery level notification +** +*******************************************************************************/ + GATT_API extern void Battery_Notify (UINT8 app_id, BD_ADDR remote_bda, UINT8 battery_level); + + +#ifdef __cplusplus + +} +#endif + +#endif + + diff --git a/stack/srvc/srvc_battery.c b/stack/srvc/srvc_battery.c new file mode 100644 index 000000000..25db24c61 --- /dev/null +++ b/stack/srvc/srvc_battery.c @@ -0,0 +1,394 @@ +/***************************************************************************** +** +** Name: srvc_battery.c +** +** Description: this file contains the main Battery Service over GATT +** server and handling functions. +** +** Copyright (c) 2008-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +******************************************************************************/ + +#include "bt_target.h" + +#include "gatt_api.h" +#include "gatt_int.h" +#include "srvc_eng_int.h" +#include "srvc_battery_int.h" + +#if BLE_INCLUDED == TRUE + +#define BA_MAX_CHAR_NUM 1 +#define BA_MAX_ATTR_NUM (BA_MAX_CHAR_NUM * 5 + 1) /* max 3 descriptors, 1 desclration and 1 value */ + +#ifndef BATTER_LEVEL_PROP +#define BATTER_LEVEL_PROP (GATT_CHAR_PROP_BIT_READ|GATT_CHAR_PROP_BIT_NOTIFY) +#endif + + +#ifndef BATTER_LEVEL_PERM +#define BATTER_LEVEL_PERM (GATT_PERM_READ) +#endif + +tBATTERY_CB battery_cb; + + +/******************************************************************************* +** battery_valid_handle_range +** +** validate a handle to be a DIS attribute handle or not. +*******************************************************************************/ +BOOLEAN battery_valid_handle_range(UINT16 handle) +{ + UINT8 i = 0; + tBA_INST *p_inst = &battery_cb.battery_inst[0]; + + for (;i < BA_MAX_INT_NUM; i ++, p_inst++) + { + if (handle == p_inst->ba_level_hdl || + handle == p_inst->clt_cfg_hdl || + handle == p_inst->rpt_ref_hdl || + handle == p_inst->pres_fmt_hdl ) + { + return TRUE; + } + } + return FALSE; +} +/******************************************************************************* +** battery_s_write_attr_value +** +** Process write DIS attribute request. +*******************************************************************************/ +UINT8 battery_s_write_attr_value(UINT8 clcb_idx, tGATT_WRITE_REQ * p_value, + tGATT_STATUS *p_status) +{ + UINT8 *p = p_value->value, i; + UINT16 handle = p_value->handle; + tBA_INST *p_inst = &battery_cb.battery_inst[0]; + tGATT_STATUS st = GATT_NOT_FOUND; + tBA_WRITE_DATA cfg; + UINT8 act = SRVC_ACT_RSP; + + for (i = 0; i < BA_MAX_INT_NUM; i ++, p_inst ++) + { + /* read battery level */ + if (handle == p_inst->clt_cfg_hdl) + { + memcpy(cfg.remote_bda, srvc_eng_cb.clcb[clcb_idx].bda, BD_ADDR_LEN); + STREAM_TO_UINT16(cfg.clt_cfg, p); + + if (p_inst->p_cback) + { + p_inst->pending_clcb_idx = clcb_idx; + p_inst->pending_evt = BA_WRITE_CLT_CFG_REQ; + p_inst->pending_handle = handle; + cfg.need_rsp = p_value->need_rsp; + act = SRVC_ACT_PENDING; + + (* p_inst->p_cback)(p_inst->app_id, BA_WRITE_CLT_CFG_REQ, &cfg); + } + } + else /* all other handle is not writable */ + { + st = GATT_WRITE_NOT_PERMIT; + break; + } + } + *p_status = st; + + return act; +} +/******************************************************************************* +** BA Attributes Database Server Request callback +*******************************************************************************/ +UINT8 battery_s_read_attr_value (UINT8 clcb_idx, UINT16 handle, tGATT_VALUE *p_value, BOOLEAN is_long, tGATT_STATUS* p_status) +{ + UINT8 i; + tBA_INST *p_inst = &battery_cb.battery_inst[0]; + tGATT_STATUS st = GATT_NOT_FOUND; + UINT8 act = SRVC_ACT_RSP; + + for (i = 0; i < BA_MAX_INT_NUM; i ++, p_inst ++) + { + /* read battery level */ + if (handle == p_inst->ba_level_hdl || + handle == p_inst->clt_cfg_hdl || + handle == p_inst->rpt_ref_hdl || + handle == p_inst->pres_fmt_hdl) + { + if (is_long) + st = GATT_NOT_LONG; + + if (p_inst->p_cback) + { + if (handle == p_inst->ba_level_hdl) p_inst->pending_evt = BA_READ_LEVEL_REQ; + if (handle == p_inst->clt_cfg_hdl) p_inst->pending_evt = BA_READ_CLT_CFG_REQ; + if (handle == p_inst->pres_fmt_hdl) p_inst->pending_evt = BA_READ_PRE_FMT_REQ; + if (handle == p_inst->rpt_ref_hdl) p_inst->pending_evt = BA_READ_RPT_REF_REQ ; + + p_inst->pending_clcb_idx = clcb_idx; + p_inst->pending_handle = handle; + act = SRVC_ACT_PENDING; + + (* p_inst->p_cback)(p_inst->app_id, p_inst->pending_evt, NULL); + } + else /* application is not registered */ + st = GATT_ERR_UNLIKELY; + break; + } + /* else attribute not found */ + } + + + *p_status = st; + return act; +} + + +/******************************************************************************* +** +** Function battery_gatt_c_read_ba_req +** +** Description Read remote device BA level attribute request. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN battery_gatt_c_read_ba_req(UINT16 conn_id) +{ + return TRUE; +} + +/******************************************************************************* +** +** Function battery_c_cmpl_cback +** +** Description Client operation complete callback. +** +** Returns void +** +*******************************************************************************/ +void battery_c_cmpl_cback (tSRVC_CLCB *p_clcb, tGATTC_OPTYPE op, + tGATT_STATUS status, tGATT_CL_COMPLETE *p_data) +{ +} + + +/******************************************************************************* +** +** Function Battery_Instantiate +** +** Description Instantiate a Battery service +** +*******************************************************************************/ +UINT16 Battery_Instantiate (UINT8 app_id, tBA_REG_INFO *p_reg_info) +{ + tBT_UUID uuid = {LEN_UUID_16, {UUID_SERVCLASS_BATTERY}}; + UINT16 srvc_hdl; + tGATT_STATUS status = GATT_ERROR; + tBA_INST *p_inst; + tGATT_CHAR_PROP prop = GATT_CHAR_PROP_BIT_READ; + + if (battery_cb.inst_id == BA_MAX_INT_NUM) + { + GATT_TRACE_ERROR0("MAX battery service has been reached"); + return 0; + } + + p_inst = &battery_cb.battery_inst[battery_cb.inst_id]; + + srvc_hdl = GATTS_CreateService (srvc_eng_cb.gatt_if , + &uuid, + battery_cb.inst_id , + BA_MAX_ATTR_NUM, + p_reg_info->is_pri); + + if (srvc_hdl == 0) + { + GATT_TRACE_ERROR0("Can not create service, Battery_Instantiate() failed!"); + return 0; + } + + battery_cb.inst_id ++; + + p_inst->app_id = app_id; + p_inst->p_cback = p_reg_info->p_cback; + + /* add battery level + */ + uuid.uu.uuid16 = GATT_UUID_BATTERY_LEVEL; + + if (p_reg_info->ba_level_descr & BA_LEVEL_NOTIFY) + prop |= GATT_CHAR_PROP_BIT_NOTIFY; + + if ((p_inst->ba_level_hdl = GATTS_AddCharacteristic(srvc_hdl, + &uuid, + BATTER_LEVEL_PERM, + prop)) == 0) + { + GATT_TRACE_ERROR0("Can not add Battery Level, Battery_Instantiate() failed!"); + status = GATT_ERROR; + } + else + { + if (p_reg_info->ba_level_descr & BA_LEVEL_NOTIFY) + { + uuid.uu.uuid16 = GATT_UUID_CHAR_CLIENT_CONFIG; + p_inst->clt_cfg_hdl = GATTS_AddCharDescriptor(srvc_hdl, + (GATT_PERM_READ|GATT_PERM_WRITE), + &uuid); + if (p_inst->clt_cfg_hdl == 0) + { + GATT_TRACE_ERROR0("Add battery level client notification FAILED!"); + } + } + /* need presentation format descriptor? */ + if (p_reg_info->ba_level_descr & BA_LEVEL_PRE_FMT) + { + uuid.uu.uuid16 = GATT_UUID_CHAR_PRESENT_FORMAT; + if ( (p_inst->pres_fmt_hdl = GATTS_AddCharDescriptor(srvc_hdl, + GATT_PERM_READ, + &uuid)) + == 0) + { + GATT_TRACE_ERROR0("Add battery level presentation format descriptor FAILED!"); + } + + } + /* need presentation format descriptor? */ + if (p_reg_info->ba_level_descr & BA_LEVEL_RPT_REF) + { + uuid.uu.uuid16 = GATT_UUID_RPT_REF_DESCR; + if ( (p_inst->rpt_ref_hdl = GATTS_AddCharDescriptor(srvc_hdl, + GATT_PERM_READ, + &uuid)) + == 0) + { + GATT_TRACE_ERROR0("Add battery level report reference descriptor FAILED!"); + } + + } + /* start service + */ + status = GATTS_StartService (srvc_eng_cb.gatt_if, srvc_hdl, p_reg_info->transport); + } + + if (status != GATT_SUCCESS) + { + battery_cb.inst_id --; + uuid.uu.uuid16 = UUID_SERVCLASS_BATTERY; + GATTS_DeleteService(srvc_eng_cb.gatt_if, &uuid, battery_cb.inst_id); + srvc_hdl = 0; + } + + return srvc_hdl; +} +/******************************************************************************* +** +** Function Battery_Rsp +** +** Description Respond to a battery service request +** +*******************************************************************************/ +void Battery_Rsp (UINT8 app_id, tGATT_STATUS st, UINT8 event, tBA_RSP_DATA *p_rsp) +{ + tBA_INST *p_inst = &battery_cb.battery_inst[0]; + tGATTS_RSP rsp; + UINT8 *pp; + + UINT8 i = 0; + while (i < BA_MAX_INT_NUM) + { + if (p_inst->app_id == app_id && p_inst->ba_level_hdl != 0) + break; + i ++; + } + + if (i == BA_MAX_INT_NUM) + return; + + memset(&rsp, 0, sizeof(tGATTS_RSP)); + + if (p_inst->pending_evt == event) + { + switch (event) + { + case BA_READ_CLT_CFG_REQ: + rsp.attr_value.handle = p_inst->pending_handle; + rsp.attr_value.len = 2; + pp = rsp.attr_value.value; + UINT16_TO_STREAM(pp, p_rsp->clt_cfg); + srvc_sr_rsp(p_inst->pending_clcb_idx, st, &rsp); + break; + + case BA_READ_LEVEL_REQ: + rsp.attr_value.handle = p_inst->pending_handle; + rsp.attr_value.len = 1; + pp = rsp.attr_value.value; + UINT8_TO_STREAM(pp, p_rsp->ba_level); + srvc_sr_rsp(p_inst->pending_clcb_idx, st, &rsp); + break; + + case BA_WRITE_CLT_CFG_REQ: + srvc_sr_rsp(p_inst->pending_clcb_idx, st, NULL); + break; + + case BA_READ_RPT_REF_REQ: + rsp.attr_value.handle = p_inst->pending_handle; + rsp.attr_value.len = 2; + pp = rsp.attr_value.value; + UINT8_TO_STREAM(pp, p_rsp->rpt_ref.rpt_id); + UINT8_TO_STREAM(pp, p_rsp->rpt_ref.rpt_type); + srvc_sr_rsp(p_inst->pending_clcb_idx, st, &rsp); + break; + + default: + break; + } + p_inst->pending_clcb_idx = 0; + p_inst->pending_evt = 0; + p_inst->pending_handle = 0; + } + return; +} +/******************************************************************************* +** +** Function Battery_Notify +** +** Description Send battery level notification +** +*******************************************************************************/ +void Battery_Notify (UINT8 app_id, BD_ADDR remote_bda, UINT8 battery_level) +{ + tBA_INST *p_inst = &battery_cb.battery_inst[0]; + UINT8 i = 0; + + while (i < BA_MAX_INT_NUM) + { + if (p_inst->app_id == app_id && p_inst->ba_level_hdl != 0) + break; + i ++; + } + + if (i == BA_MAX_INT_NUM || p_inst->clt_cfg_hdl == 0) + return; + + srvc_sr_notify(remote_bda, p_inst->ba_level_hdl, 1, &battery_level); + +} +/******************************************************************************* +** +** Function Battery_ReadBatteryLevel +** +** Description Read remote device Battery Level information. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN Battery_ReadBatteryLevel(BD_ADDR peer_bda) +{ + /* to be implemented */ + return TRUE; +} +#endif /* BLE_INCLUDED */ diff --git a/stack/srvc/srvc_battery_int.h b/stack/srvc/srvc_battery_int.h new file mode 100644 index 000000000..97c808a5c --- /dev/null +++ b/stack/srvc/srvc_battery_int.h @@ -0,0 +1,75 @@ +/***************************************************************************** +** +** Name: srvc_battery_int.h +** +** Description: this file contains the Battery service internal interface +** definitions. +** +** +** Copyright (c) 1999-2008, Broadcom Corp., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +******************************************************************************/ + +#ifndef SRVC_BATTERY_INT_H +#define SRVC_BATTERY_INT_H + +#include "bt_target.h" +#include "srvc_api.h" +#include "gatt_api.h" + +#ifndef BA_MAX_INT_NUM +#define BA_MAX_INT_NUM 4 +#endif + +#define BATTERY_LEVEL_SIZE 1 + + +typedef struct +{ + UINT8 app_id; + UINT16 ba_level_hdl; + UINT16 clt_cfg_hdl; + UINT16 rpt_ref_hdl; + UINT16 pres_fmt_hdl; + + tBA_CBACK *p_cback; + + UINT16 pending_handle; + UINT8 pending_clcb_idx; + UINT8 pending_evt; + +}tBA_INST; + +typedef struct +{ + tBA_INST battery_inst[BA_MAX_INT_NUM]; + UINT8 inst_id; + BOOLEAN enabled; + +}tBATTERY_CB; + +#ifdef __cplusplus +extern "C" { +#endif + +/* Global GATT data */ +#if GATT_DYNAMIC_MEMORY == FALSE +GATT_API extern tBATTERY_CB battery_cb; +#else +GATT_API extern tBATTERY_CB *battery_cb_ptr; +#define battery_cb (*battery_cb_ptr) +#endif + + +extern BOOLEAN battery_valid_handle_range(UINT16 handle); + +extern UINT8 battery_s_write_attr_value(UINT8 clcb_idx, tGATT_WRITE_REQ * p_value, + tGATT_STATUS *p_status); +extern UINT8 battery_s_read_attr_value (UINT8 clcb_idx, UINT16 handle, tGATT_VALUE *p_value, BOOLEAN is_long, tGATT_STATUS* p_status); + + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/stack/srvc/srvc_dis.c b/stack/srvc/srvc_dis.c new file mode 100644 index 000000000..505d791af --- /dev/null +++ b/stack/srvc/srvc_dis.c @@ -0,0 +1,451 @@ +/***************************************************************************** +** +** Name: srvc_dis.c +** +** Description: this file contains the main Device Information Service over GATT +** server/client and request handling functions. +** +** Copyright (c) 2008-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +******************************************************************************/ + +#include "bt_target.h" + +#include "gatt_api.h" +#include "gatt_int.h" +#include "srvc_eng_int.h" +#include "srvc_dis_int.h" + +#if BLE_INCLUDED == TRUE + +#define DIS_UUID_TO_ATTR_MASK(x) (UINT16)(1 << ((x) - GATT_UUID_SYSTEM_ID)) + +#define DIS_MAX_NUM_INC_SVR 0 +#define DIS_MAX_CHAR_NUM 9 +#define DIS_MAX_ATTR_NUM (DIS_MAX_CHAR_NUM * 2 + DIS_MAX_NUM_INC_SVR + 1) + +#ifndef DIS_ATTR_DB_SIZE +#define DIS_ATTR_DB_SIZE GATT_DB_MEM_SIZE(DIS_MAX_NUM_INC_SVR, DIS_MAX_CHAR_NUM, 0) +#endif + +#define UINT64_TO_STREAM(p, u64) {*(p)++ = (UINT8)(u64); *(p)++ = (UINT8)((u64) >> 8);*(p)++ = (UINT8)((u64) >> 16); *(p)++ = (UINT8)((u64) >> 24); \ + *(p)++ = (UINT8)((u64) >> 32); *(p)++ = (UINT8)((u64) >> 40);*(p)++ = (UINT8)((u64) >> 48); *(p)++ = (UINT8)((u64) >> 56);} + +#define STREAM_TO_UINT64(u64, p) {u64 = (((UINT64)(*(p))) + ((((UINT64)(*((p) + 1)))) << 8) + ((((UINT64)(*((p) + 2)))) << 16) + ((((UINT64)(*((p) + 3)))) << 24) \ + + ((((UINT64)(*((p) + 4)))) << 32) + ((((UINT64)(*((p) + 5)))) << 40) + ((((UINT64)(*((p) + 6)))) << 48) + ((((UINT64)(*((p) + 7)))) << 56)); (p) += 8;} + + + +static const UINT16 dis_attr_uuid[DIS_MAX_CHAR_NUM] = +{ + GATT_UUID_SYSTEM_ID, + GATT_UUID_MODEL_NUMBER_STR, + GATT_UUID_SERIAL_NUMBER_STR, + GATT_UUID_FW_VERSION_STR, + GATT_UUID_HW_VERSION_STR, + GATT_UUID_SW_VERSION_STR, + GATT_UUID_MANU_NAME, + GATT_UUID_IEEE_DATA, + GATT_UUID_PNP_ID +}; + +tDIS_CB dis_cb; +/******************************************************************************* +** dis_valid_handle_range +** +** validate a handle to be a DIS attribute handle or not. +*******************************************************************************/ +BOOLEAN dis_valid_handle_range(UINT16 handle) +{ + if (handle >= dis_cb.service_handle && handle <= dis_cb.max_handle) + return TRUE; + else + return FALSE; +} +/******************************************************************************* +** dis_write_attr_value +** +** Process write DIS attribute request. +*******************************************************************************/ +UINT8 dis_write_attr_value(tGATT_WRITE_REQ * p_data, tGATT_STATUS *p_status) +{ + *p_status = GATT_WRITE_NOT_PERMIT; + return SRVC_ACT_RSP; +} +/******************************************************************************* +** DIS Attributes Database Server Request callback +*******************************************************************************/ +UINT8 dis_read_attr_value (UINT8 clcb_idx, UINT16 handle, tGATT_VALUE *p_value, + BOOLEAN is_long, tGATT_STATUS *p_status) +{ + tDIS_DB_ENTRY *p_db_attr = dis_cb.dis_attr; + UINT8 *p = p_value->value, i, *pp; + UINT16 offset = p_value->offset; + UINT8 act = SRVC_ACT_RSP; + tGATT_STATUS st = GATT_NOT_FOUND; + + for (i = 0; i < DIS_MAX_CHAR_NUM; i ++, p_db_attr ++) + { + if (handle == p_db_attr->handle) + { + if ((p_db_attr->uuid == GATT_UUID_PNP_ID || p_db_attr->uuid == GATT_UUID_SYSTEM_ID)&& + is_long == TRUE) + { + st = GATT_NOT_LONG; + break; + } + st = GATT_SUCCESS; + + switch (p_db_attr->uuid) + { + case GATT_UUID_MANU_NAME: + case GATT_UUID_MODEL_NUMBER_STR: + case GATT_UUID_SERIAL_NUMBER_STR: + case GATT_UUID_FW_VERSION_STR: + case GATT_UUID_HW_VERSION_STR: + case GATT_UUID_SW_VERSION_STR: + case GATT_UUID_IEEE_DATA: + pp = dis_cb.dis_value.data_string[p_db_attr->uuid - GATT_UUID_MODEL_NUMBER_STR]; + if (pp != NULL) + { + if (strlen ((char *)pp) > GATT_MAX_ATTR_LEN) + p_value->len = GATT_MAX_ATTR_LEN; + else + p_value->len = (UINT16)strlen ((char *)pp); + } + else + p_value->len = 0; + + if (offset > p_value->len) + { + st = GATT_INVALID_OFFSET; + break; + } + else + { + p_value->len -= offset; + pp += offset; + ARRAY_TO_STREAM(p, pp, p_value->len); + GATT_TRACE_EVENT1("GATT_UUID_MANU_NAME len=0x%04x", p_value->len); + } + break; + + + case GATT_UUID_SYSTEM_ID: + UINT64_TO_STREAM(p, dis_cb.dis_value.system_id); /* int_min */ + p_value->len = DIS_SYSTEM_ID_SIZE; + break; + + case GATT_UUID_PNP_ID: + UINT8_TO_STREAM(p, dis_cb.dis_value.pnp_id.vendor_id_src); + UINT16_TO_STREAM(p, dis_cb.dis_value.pnp_id.vendor_id); + UINT16_TO_STREAM(p, dis_cb.dis_value.pnp_id.product_id); + UINT16_TO_STREAM(p, dis_cb.dis_value.pnp_id.product_version); + p_value->len = DIS_PNP_ID_SIZE; + break; + + } + break; + } + } + *p_status = st; + return act; +} + +/******************************************************************************* +** +** Function dis_gatt_c_read_dis_value_cmpl +** +** Description Client read DIS database complete callback. +** +** Returns void +** +*******************************************************************************/ +void dis_gatt_c_read_dis_value_cmpl(UINT16 conn_id) +{ + tSRVC_CLCB *p_clcb = srvc_eng_find_clcb_by_conn_id(conn_id); + + dis_cb.dis_read_uuid_idx = 0xff; + + srvc_eng_release_channel(conn_id); + + if (dis_cb.p_read_dis_cback && p_clcb) + { + GATT_TRACE_ERROR1("dis_gatt_c_read_dis_value_cmpl: attr_mask = 0x%04x", p_clcb->dis_value.attr_mask); + GATT_TRACE_EVENT0("calling p_read_dis_cbackd"); + + (*dis_cb.p_read_dis_cback)(p_clcb->bda, &p_clcb->dis_value); + dis_cb.p_read_dis_cback=NULL; + } + +} + +/******************************************************************************* +** +** Function dis_gatt_c_read_dis_req +** +** Description Read remote device DIS attribute request. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN dis_gatt_c_read_dis_req(UINT16 conn_id) +{ + tGATT_READ_PARAM param; + + memset(¶m, 0, sizeof(tGATT_READ_PARAM)); + + param.service.uuid.len = LEN_UUID_16; + param.service.s_handle = 1; + param.service.e_handle = 0xFFFF; + param.service.auth_req = 0; + + while (dis_cb.dis_read_uuid_idx < DIS_MAX_CHAR_NUM) + { + param.service.uuid.uu.uuid16 = dis_attr_uuid[dis_cb.dis_read_uuid_idx]; + + if (GATTC_Read(conn_id, GATT_READ_BY_TYPE, ¶m) == GATT_SUCCESS) + { + return(TRUE); + } + else + { + GATT_TRACE_ERROR1 ("Read DISInfo: 0x%04x GATT_Read Failed", param.service.uuid.uu.uuid16); + dis_cb.dis_read_uuid_idx ++; + } + } + + dis_gatt_c_read_dis_value_cmpl(conn_id); + + return(FALSE); +} + +/******************************************************************************* +** +** Function dis_c_cmpl_cback +** +** Description Client operation complete callback. +** +** Returns void +** +*******************************************************************************/ +void dis_c_cmpl_cback (tSRVC_CLCB *p_clcb, tGATTC_OPTYPE op, + tGATT_STATUS status, tGATT_CL_COMPLETE *p_data) +{ + UINT16 read_type = dis_attr_uuid[dis_cb.dis_read_uuid_idx]; + UINT8 *pp = NULL, *p_str; + UINT16 conn_id = p_clcb->conn_id; + + GATT_TRACE_EVENT3 ("dis_c_cmpl_cback() - op_code: 0x%02x status: 0x%02x \ + read_type: 0x%04x", op, status, read_type); + + if (op != GATTC_OPTYPE_READ) + return; + + if (p_data != NULL && status == GATT_SUCCESS) + { + pp = p_data->att_value.value; + + switch (read_type) + { + case GATT_UUID_SYSTEM_ID: + GATT_TRACE_EVENT0 ("DIS_ATTR_SYS_ID_BIT"); + if (p_data->att_value.len == DIS_SYSTEM_ID_SIZE) + { + p_clcb->dis_value.attr_mask |= DIS_ATTR_SYS_ID_BIT; + /* save system ID*/ + STREAM_TO_UINT64 (p_clcb->dis_value.system_id, pp); + } + break; + + case GATT_UUID_PNP_ID: + if (p_data->att_value.len == DIS_PNP_ID_SIZE) + { + p_clcb->dis_value.attr_mask |= DIS_ATTR_PNP_ID_BIT; + STREAM_TO_UINT8 (p_clcb->dis_value.pnp_id.vendor_id_src, pp); + STREAM_TO_UINT16 (p_clcb->dis_value.pnp_id.vendor_id, pp); + STREAM_TO_UINT16 (p_clcb->dis_value.pnp_id.product_id, pp); + STREAM_TO_UINT16 (p_clcb->dis_value.pnp_id.product_version, pp); + } + break; + + case GATT_UUID_MODEL_NUMBER_STR: + case GATT_UUID_SERIAL_NUMBER_STR: + case GATT_UUID_FW_VERSION_STR: + case GATT_UUID_HW_VERSION_STR: + case GATT_UUID_SW_VERSION_STR: + case GATT_UUID_MANU_NAME: + case GATT_UUID_IEEE_DATA: + p_str = p_clcb->dis_value.data_string[read_type - GATT_UUID_MODEL_NUMBER_STR]; + if (p_str != NULL) + GKI_freebuf(p_str); + if ((p_str = (UINT8 *)GKI_getbuf((UINT16)(p_data->att_value.len + 1))) != NULL) + { + memset(p_str, 0, p_data->att_value.len + 1); + p_clcb->dis_value.attr_mask |= DIS_UUID_TO_ATTR_MASK (read_type); + memcpy(p_str, p_data->att_value.value, p_data->att_value.len); + } + break; + + default: + break; + + break; + }/* end switch */ + }/* end if */ + + dis_cb.dis_read_uuid_idx ++; + + dis_gatt_c_read_dis_req(conn_id); +} + + +/******************************************************************************* +** +** Function DIS_SrInit +** +** Description Initializa the Device Information Service Server. +** +*******************************************************************************/ +tDIS_STATUS DIS_SrInit (tDIS_ATTR_MASK dis_attr_mask) +{ + tBT_UUID uuid = {LEN_UUID_16, {UUID_SERVCLASS_DEVICE_INFO}}; + UINT16 i = 0; + tGATT_STATUS status; + tDIS_DB_ENTRY *p_db_attr = &dis_cb.dis_attr[0]; + + if (dis_cb.enabled) + { + GATT_TRACE_ERROR0("DIS already initalized"); + return DIS_SUCCESS; + } + + memset(&dis_cb, 0, sizeof(tDIS_CB)); + + dis_cb.service_handle = GATTS_CreateService (srvc_eng_cb.gatt_if , &uuid, 0, DIS_MAX_ATTR_NUM, TRUE); + + if (dis_cb.service_handle == 0) + { + GATT_TRACE_ERROR0("Can not create service, DIS_Init failed!"); + return GATT_ERROR; + } + dis_cb.max_handle = dis_cb.service_handle + DIS_MAX_ATTR_NUM; + + while (dis_attr_mask != 0 && i < DIS_MAX_CHAR_NUM) + { + /* add Manufacturer name + */ + uuid.uu.uuid16 = p_db_attr->uuid = dis_attr_uuid[i]; + p_db_attr->handle = GATTS_AddCharacteristic(dis_cb.service_handle, &uuid, GATT_PERM_READ, GATT_CHAR_PROP_BIT_READ); + GATT_TRACE_DEBUG2 ("DIS_SrInit: handle of new attribute 0x%04 = x%d", uuid.uu.uuid16, p_db_attr->handle ); + p_db_attr ++; + i ++; + dis_attr_mask >>= 1; + } + + /* start service + */ + status = GATTS_StartService (srvc_eng_cb.gatt_if, dis_cb.service_handle, GATT_TRANSPORT_LE_BR_EDR); + + dis_cb.enabled = TRUE; + + return (tDIS_STATUS) status; +} +/******************************************************************************* +** +** Function DIS_SrUpdate +** +** Description Update the DIS server attribute values +** +*******************************************************************************/ +tDIS_STATUS DIS_SrUpdate(tDIS_ATTR_BIT dis_attr_bit, tDIS_ATTR *p_info) +{ + UINT8 i = 1; + tDIS_STATUS st = DIS_SUCCESS; + + if (dis_attr_bit & DIS_ATTR_SYS_ID_BIT) + { + dis_cb.dis_value.system_id = p_info->system_id; + } + else if (dis_attr_bit & DIS_ATTR_PNP_ID_BIT) + { + dis_cb.dis_value.pnp_id.vendor_id = p_info->pnp_id.vendor_id; + dis_cb.dis_value.pnp_id.vendor_id_src = p_info->pnp_id.vendor_id_src; + dis_cb.dis_value.pnp_id.product_id = p_info->pnp_id.product_id; + dis_cb.dis_value.pnp_id.product_version = p_info->pnp_id.product_version; + } + else + { + st = DIS_ILLEGAL_PARAM; + + while (dis_attr_bit && i < (DIS_MAX_CHAR_NUM -1 )) + { + if (dis_attr_bit & (UINT16)(1 << i)) + { + if (dis_cb.dis_value.data_string[i - 1] != NULL) + GKI_freebuf(dis_cb.dis_value.data_string[i]); +/* coverity[OVERRUN-STATIC] False-positive : when i = 8, (1 << i) == DIS_ATTR_PNP_ID_BIT, and it will never come down here +CID 49902: Out-of-bounds read (OVERRUN_STATIC) +Overrunning static array "dis_cb.dis_value.data_string", with 7 elements, at position 7 with index variable "i". +*/ + if ((dis_cb.dis_value.data_string[i - 1] = (UINT8 *)GKI_getbuf((UINT16)(p_info->data_str.len + 1))) != NULL) + { + memset(dis_cb.dis_value.data_string[i - 1], 0, p_info->data_str.len + 1); /* make sure null terminate */ + memcpy(dis_cb.dis_value.data_string[i - 1], p_info->data_str.p_data, p_info->data_str.len); + st = DIS_SUCCESS; + } + else + st = DIS_NO_RESOURCES; + + break; + } + i ++; + } + } + return st; +} +/******************************************************************************* +** +** Function DIS_ReadDISInfo +** +** Description Read remote device DIS information. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN DIS_ReadDISInfo(BD_ADDR peer_bda, tDIS_READ_CBACK *p_cback) +{ + UINT16 conn_id; + + /* For now we only handle one at a time */ + if (dis_cb.dis_read_uuid_idx != 0xff) + return(FALSE); + + if (p_cback == NULL) + return(FALSE); + + dis_cb.p_read_dis_cback = p_cback; + /* Mark currently active operation */ + dis_cb.dis_read_uuid_idx = 0; + + GATT_TRACE_EVENT3 ("DIS_ReadDISInfo() - BDA: %08x%04x cl_read_uuid: 0x%04x", + (peer_bda[0]<<24)+(peer_bda[1]<<16)+(peer_bda[2]<<8)+peer_bda[3], + (peer_bda[4]<<8)+peer_bda[5], dis_attr_uuid[dis_cb.dis_read_uuid_idx]); + + + GATT_GetConnIdIfConnected(srvc_eng_cb.gatt_if, peer_bda, &conn_id); + + /* need to enhance it as multiple service is needed */ + srvc_eng_request_channel(peer_bda, SRVC_ID_DIS); + + if (conn_id == GATT_INVALID_CONN_ID) + { + return GATT_Connect(srvc_eng_cb.gatt_if, peer_bda, TRUE); + } + + return dis_gatt_c_read_dis_req(conn_id); + +} +#endif /* BLE_INCLUDED */ + + diff --git a/stack/srvc/srvc_dis_int.h b/stack/srvc/srvc_dis_int.h new file mode 100644 index 000000000..ede7a85cc --- /dev/null +++ b/stack/srvc/srvc_dis_int.h @@ -0,0 +1,76 @@ +/***************************************************************************** +** +** Name: srvc_dis_int.h +** +** Description: this file contains the GAP internal interface +** definitions. +** +** +** Copyright (c) 1999-2008, Broadcom Corp., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +******************************************************************************/ + +#ifndef SRVC_DIS_INT_H +#define SRVC_DIS_INT_H + +#include "bt_target.h" +#include "srvc_api.h" +#include "gatt_api.h" + +#define DIS_MAX_CHAR_NUM 9 + + +typedef struct +{ + UINT16 uuid; + UINT16 handle; +}tDIS_DB_ENTRY; + +#define DIS_SYSTEM_ID_SIZE 8 +#define DIS_PNP_ID_SIZE 7 + + + +typedef struct +{ + tDIS_DB_ENTRY dis_attr[DIS_MAX_CHAR_NUM]; + tDIS_VALUE dis_value; + + tDIS_READ_CBACK *p_read_dis_cback; + + UINT16 service_handle; + UINT16 max_handle; + + BOOLEAN enabled; + + UINT8 dis_read_uuid_idx; +}tDIS_CB; + + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Global GATT data */ +#if GATT_DYNAMIC_MEMORY == FALSE +GATT_API extern tDIS_CB dis_cb; +#else +GATT_API extern tDIS_CB *dis_cb_ptr; +#define dis_cb (*dis_cb_ptr) +#endif + + +extern BOOLEAN dis_valid_handle_range(UINT16 handle); +extern UINT8 dis_read_attr_value (UINT8 clcb_idx, UINT16 handle, tGATT_VALUE *p_value, + BOOLEAN is_long, tGATT_STATUS *p_status); +extern UINT8 dis_write_attr_value(tGATT_WRITE_REQ * p_data, tGATT_STATUS *p_status); + +extern void dis_c_cmpl_cback (tSRVC_CLCB *p_clcb, tGATTC_OPTYPE op, + tGATT_STATUS status, tGATT_CL_COMPLETE *p_data); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/stack/srvc/srvc_eng.c b/stack/srvc/srvc_eng.c new file mode 100644 index 000000000..c4f5527b0 --- /dev/null +++ b/stack/srvc/srvc_eng.c @@ -0,0 +1,456 @@ +/***************************************************************************** +** +** Name: srvc_eng.c +** +** Description: this file contains the main Device Information Service over GATT +** server/client and request handling functions. +** +** Copyright (c) 2008-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +******************************************************************************/ + +#include "bt_target.h" + +#include "gatt_api.h" +#include "gatt_int.h" +#include "srvc_eng_int.h" + +#if BLE_INCLUDED == TRUE + +//#if DIS_INCLUDED == TRUE +#include "srvc_dis_int.h" +//#endif +#include "srvc_battery_int.h" + +static void srvc_eng_s_request_cback (UINT16 conn_id, UINT32 trans_id, UINT8 op_code, tGATTS_DATA *p_data); +static void srvc_eng_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, BOOLEAN connected, tGATT_DISCONN_REASON reason); +static void srvc_eng_c_cmpl_cback (UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS status, tGATT_CL_COMPLETE *p_data); + +static tGATT_CBACK srvc_gatt_cback = +{ + srvc_eng_connect_cback, + srvc_eng_c_cmpl_cback, + NULL, + NULL, + srvc_eng_s_request_cback +} ; +/* type for action functions */ +typedef void (*tSRVC_ENG_C_CMPL_ACTION)(tSRVC_CLCB *p_clcb, tGATTC_OPTYPE op, + tGATT_STATUS status, tGATT_CL_COMPLETE *p_data); + +const tSRVC_ENG_C_CMPL_ACTION srvc_eng_c_cmpl_act[SRVC_ID_MAX] = +{ + dis_c_cmpl_cback, +}; + +tSRVC_ENG_CB srvc_eng_cb; + +/******************************************************************************* +** +** Function srvc_eng_find_conn_id_by_bd_addr +** +** Description The function searches all LCB with macthing bd address +** +** Returns total number of clcb found. +** +*******************************************************************************/ +UINT16 srvc_eng_find_conn_id_by_bd_addr(BD_ADDR bda) +{ + UINT8 i_clcb; + tSRVC_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb= srvc_eng_cb.clcb; i_clcb < SRVC_MAX_APPS; i_clcb++, p_clcb++) + { + if (p_clcb->in_use && p_clcb->connected && !memcmp(p_clcb->bda, bda, BD_ADDR_LEN)) + { + return p_clcb->conn_id; + } + } + + return GATT_INVALID_CONN_ID; +} + +/******************************************************************************* +** +** Function srvc_eng_find_clcb_by_bd_addr +** +** Description The function searches all LCBs with macthing bd address. +** +** Returns Pointer to the found link conenction control block. +** +*******************************************************************************/ +tSRVC_CLCB *srvc_eng_find_clcb_by_bd_addr(BD_ADDR bda) +{ + UINT8 i_clcb; + tSRVC_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb= srvc_eng_cb.clcb; i_clcb < SRVC_MAX_APPS; i_clcb++, p_clcb++) + { + if (p_clcb->in_use && p_clcb->connected && !memcmp(p_clcb->bda, bda, BD_ADDR_LEN)) + { + return p_clcb; + } + } + + return NULL; +} +/******************************************************************************* +** +** Function srvc_eng_find_clcb_by_conn_id +** +** Description The function searches all LCBs with macthing connection ID. +** +** Returns Pointer to the found link conenction control block. +** +*******************************************************************************/ +tSRVC_CLCB *srvc_eng_find_clcb_by_conn_id(UINT16 conn_id) +{ + UINT8 i_clcb; + tSRVC_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb= srvc_eng_cb.clcb; i_clcb < SRVC_MAX_APPS; i_clcb++, p_clcb++) + { + if (p_clcb->in_use && p_clcb->connected && p_clcb->conn_id == conn_id) + { + return p_clcb; + } + } + + return NULL; +} +/******************************************************************************* +** +** Function srvc_eng_find_clcb_by_conn_id +** +** Description The function searches all LCBs with macthing connection ID. +** +** Returns Pointer to the found link conenction control block. +** +*******************************************************************************/ +UINT8 srvc_eng_find_clcb_idx_by_conn_id(UINT16 conn_id) +{ + UINT8 i_clcb; + tSRVC_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb= srvc_eng_cb.clcb; i_clcb < SRVC_MAX_APPS; i_clcb++, p_clcb++) + { + if (p_clcb->in_use && p_clcb->connected && p_clcb->conn_id == conn_id) + { + return i_clcb; + } + } + + return SRVC_MAX_APPS; +} +/******************************************************************************* +** +** Function srvc_eng_clcb_alloc +** +** Description The function allocates a GATT profile connection link control block +** +** Returns NULL if not found. Otherwise pointer to the connection link block. +** +*******************************************************************************/ +tSRVC_CLCB *srvc_eng_clcb_alloc (UINT16 conn_id, BD_ADDR bda) +{ + UINT8 i_clcb = 0; + tSRVC_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb= srvc_eng_cb.clcb; i_clcb < SRVC_MAX_APPS; i_clcb++, p_clcb++) + { + if (!p_clcb->in_use) + { + p_clcb->in_use = TRUE; + p_clcb->conn_id = conn_id; + p_clcb->connected = TRUE; + memcpy (p_clcb->bda, bda, BD_ADDR_LEN); + break; + } + } + return p_clcb; +} +/******************************************************************************* +** +** Function srvc_eng_clcb_dealloc +** +** Description The function deallocates a GATT profile connection link control block +** +** Returns NTrue the deallocation is successful +** +*******************************************************************************/ +BOOLEAN srvc_eng_clcb_dealloc (UINT16 conn_id) +{ + UINT8 i_clcb = 0; + tSRVC_CLCB *p_clcb = NULL; + + for (i_clcb = 0, p_clcb= srvc_eng_cb.clcb; i_clcb < SRVC_MAX_APPS; i_clcb++, p_clcb++) + { + if (p_clcb->in_use && p_clcb->connected && (p_clcb->conn_id == conn_id)) + { + memset(p_clcb, 0, sizeof(tSRVC_CLCB)); + return TRUE; + } + } + return FALSE; +} +/******************************************************************************* +** Service Engine Server Attributes Database Read/Read Blob Request process +*******************************************************************************/ +UINT8 srvc_eng_process_read_req (UINT8 clcb_idx, tGATT_READ_REQ *p_data, tGATTS_RSP *p_rsp, tGATT_STATUS *p_status) +{ + tGATT_STATUS status = GATT_NOT_FOUND; + UINT8 act = SRVC_ACT_RSP; + + if (p_data->is_long) + p_rsp->attr_value.offset = p_data->offset; + + p_rsp->attr_value.handle = p_data->handle; + + if (dis_valid_handle_range(p_data->handle)) + act = dis_read_attr_value(clcb_idx, p_data->handle, &p_rsp->attr_value, p_data->is_long, p_status); + + else if (battery_valid_handle_range(p_data->handle)) + act = battery_s_read_attr_value(clcb_idx, p_data->handle, &p_rsp->attr_value, p_data->is_long, p_status); + + else + *p_status = status; + return act; +} +/******************************************************************************* +** Service Engine Server Attributes Database write Request process +*******************************************************************************/ +UINT8 srvc_eng_process_write_req (UINT8 clcb_idx, tGATT_WRITE_REQ *p_data, tGATTS_RSP *p_rsp, tGATT_STATUS *p_status) +{ + UINT8 act = SRVC_ACT_RSP; + + if (dis_valid_handle_range(p_data->handle)) + { + act = dis_write_attr_value(p_data, p_status); + } + else if (battery_valid_handle_range(p_data->handle)) + { + act = battery_s_write_attr_value(clcb_idx, p_data, p_status); + } + else + *p_status = GATT_NOT_FOUND; + + return act; +} + +/******************************************************************************* +** +** Function srvc_eng_s_request_cback +** +** Description GATT DIS attribute access request callback. +** +** Returns void. +** +*******************************************************************************/ +static void srvc_eng_s_request_cback (UINT16 conn_id, UINT32 trans_id, tGATTS_REQ_TYPE type, + tGATTS_DATA *p_data) +{ + UINT8 status = GATT_INVALID_PDU; + tGATTS_RSP rsp_msg ; + UINT8 act = SRVC_ACT_IGNORE; + UINT8 clcb_idx = srvc_eng_find_clcb_idx_by_conn_id(conn_id); + + GATT_TRACE_EVENT1("srvc_eng_s_request_cback : recv type (0x%02x)", type); + + memset(&rsp_msg, 0, sizeof(tGATTS_RSP)); + + srvc_eng_cb.clcb[clcb_idx].trans_id = trans_id; + + switch (type) + { + case GATTS_REQ_TYPE_READ: + act = srvc_eng_process_read_req(clcb_idx, &p_data->read_req, &rsp_msg, &status); + break; + + case GATTS_REQ_TYPE_WRITE: + act = srvc_eng_process_write_req(clcb_idx, &p_data->write_req, &rsp_msg, &status); + if (!p_data->write_req.need_rsp) + act = SRVC_ACT_IGNORE; + break; + + case GATTS_REQ_TYPE_WRITE_EXEC: + GATT_TRACE_EVENT0("Ignore GATT_REQ_EXEC_WRITE/WRITE_CMD" ); + break; + + case GATTS_REQ_TYPE_MTU: + GATT_TRACE_EVENT1("Get MTU exchange new mtu size: %d", p_data->mtu); + break; + + default: + GATT_TRACE_EVENT1("Unknown/unexpected LE GAP ATT request: 0x%02x", type); + break; + } + + srvc_eng_cb.clcb[clcb_idx].trans_id = 0; + + if (act == SRVC_ACT_RSP) + GATTS_SendRsp (conn_id, trans_id, status, &rsp_msg); + + +} + + +/******************************************************************************* +** +** Function srvc_eng_c_cmpl_cback +** +** Description Client operation complete callback. +** +** Returns void +** +*******************************************************************************/ +static void srvc_eng_c_cmpl_cback (UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS status, + tGATT_CL_COMPLETE *p_data) +{ + tSRVC_CLCB *p_clcb = srvc_eng_find_clcb_by_conn_id(conn_id); + + GATT_TRACE_EVENT2 ("srvc_eng_c_cmpl_cback() - op_code: 0x%02x status: 0x%02x ", op, status); + + if (p_clcb == NULL) + { + GATT_TRACE_ERROR0("srvc_eng_c_cmpl_cback received for unknown connection"); + return; + } + + if (p_clcb->cur_srvc_id != SRVC_ID_NONE && + p_clcb->cur_srvc_id <= SRVC_ID_MAX) + srvc_eng_c_cmpl_act[p_clcb->cur_srvc_id - 1](p_clcb, op, status, p_data); +} + + +/******************************************************************************* +** +** Function srvc_eng_connect_cback +** +** Description Gatt profile connection callback. +** +** Returns void +** +*******************************************************************************/ +static void srvc_eng_connect_cback (tGATT_IF gatt_if, BD_ADDR bda, UINT16 conn_id, + BOOLEAN connected, tGATT_DISCONN_REASON reason) +{ + GATT_TRACE_EVENT5 ("srvc_eng_connect_cback: from %08x%04x connected:%d conn_id=%d reason = 0x%04x", + (bda[0]<<24)+(bda[1]<<16)+(bda[2]<<8)+bda[3], + (bda[4]<<8)+bda[5], connected, conn_id, reason); + + if (connected) + { + if (srvc_eng_clcb_alloc(conn_id, bda) == NULL) + { + GATT_TRACE_ERROR0 ("srvc_eng_connect_cback: no_resource"); + return; + } + } + else + { + srvc_eng_clcb_dealloc(conn_id); + } + +} +/******************************************************************************* +** +** Function srvc_eng_c_cmpl_cback +** +** Description Client operation complete callback. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN srvc_eng_request_channel (BD_ADDR remote_bda, UINT8 srvc_id ) +{ + BOOLEAN set = TRUE; + tSRVC_CLCB *p_clcb = srvc_eng_find_clcb_by_bd_addr(remote_bda); + + if (p_clcb == NULL) + p_clcb = srvc_eng_clcb_alloc(0, remote_bda); + + if (p_clcb && p_clcb->cur_srvc_id == SRVC_ID_NONE) + p_clcb->cur_srvc_id = srvc_id; + else + set = FALSE; + + return set; +} +/******************************************************************************* +** +** Function srvc_eng_release_channel +** +** Description Client operation complete callback. +** +** Returns void +** +*******************************************************************************/ +void srvc_eng_release_channel (UINT16 conn_id) +{ + tSRVC_CLCB *p_clcb = srvc_eng_find_clcb_by_conn_id(conn_id); + + p_clcb->cur_srvc_id = SRVC_ID_NONE; + + /* check pending request */ + //if (p_clcb->pend_req == NULL) + GATT_Disconnect(p_clcb->conn_id); +} +/******************************************************************************* +** +** Function srvc_eng_init +** +** Description Initializa the GATT Service engine. +** +*******************************************************************************/ +tGATT_STATUS srvc_eng_init (void) +{ + tBT_UUID app_uuid = {LEN_UUID_16, {UUID_SERVCLASS_DEVICE_INFO}}; + + if (srvc_eng_cb.enabled) + { + GATT_TRACE_ERROR0("DIS already initalized"); + } + else + { + memset(&srvc_eng_cb, 0, sizeof(tDIS_CB)); + + /* Create a GATT profile service */ + srvc_eng_cb.gatt_if = GATT_Register(&app_uuid, &srvc_gatt_cback); + GATT_StartIf(srvc_eng_cb.gatt_if); + + GATT_TRACE_DEBUG1 ("Srvc_Init: gatt_if=%d ", srvc_eng_cb.gatt_if); + + srvc_eng_cb.enabled = TRUE; +//#if DIS_INCLUDED == TRUE + dis_cb.dis_read_uuid_idx = 0xff; +//#endif + } + return GATT_SUCCESS; +} + +void srvc_sr_rsp(UINT8 clcb_idx, tGATT_STATUS st, tGATTS_RSP *p_rsp) +{ + if (srvc_eng_cb.clcb[clcb_idx].trans_id != 0) + { + GATTS_SendRsp(srvc_eng_cb.clcb[clcb_idx].conn_id, + srvc_eng_cb.clcb[clcb_idx].trans_id, + st, + p_rsp); + + srvc_eng_cb.clcb[clcb_idx].trans_id = 0; + } +} +void srvc_sr_notify(BD_ADDR remote_bda, UINT16 handle, UINT16 len, UINT8 *p_value) +{ + UINT16 conn_id = srvc_eng_find_conn_id_by_bd_addr(remote_bda); + + if (conn_id != GATT_INVALID_CONN_ID) + { + GATTS_HandleValueNotification( conn_id, handle, len, p_value); + } +} + +#endif + + + diff --git a/stack/srvc/srvc_eng_int.h b/stack/srvc/srvc_eng_int.h new file mode 100644 index 000000000..40aae814e --- /dev/null +++ b/stack/srvc/srvc_eng_int.h @@ -0,0 +1,82 @@ +/***************************************************************************** +** +** Name: srvc_eng_int.h +** +** Description: this file contains the Service Engine internal interface +** definitions. +** +** +** Copyright (c) 1999-2008, Broadcom Corp., All Rights Reserved. +** Broadcom Corp. Bluetooth Core. Proprietary and confidential. +******************************************************************************/ + +#ifndef SRVC_ENG_INT_H +#define SRVC_ENG_INT_H + +#include "bt_target.h" +#include "gatt_api.h" +#include "srvc_api.h" + +#define SRVC_MAX_APPS GATT_CL_MAX_LCB + +#define SRVC_ID_NONE 0 +#define SRVC_ID_DIS 1 +#define SRVC_ID_MAX SRVC_ID_DIS + +#define SRVC_ACT_IGNORE 0 +#define SRVC_ACT_RSP 1 +#define SRVC_ACT_PENDING 2 + +typedef struct +{ + BOOLEAN in_use; + UINT16 conn_id; + BOOLEAN connected; + BD_ADDR bda; + UINT32 trans_id; + UINT8 cur_srvc_id; + + tDIS_VALUE dis_value; + +}tSRVC_CLCB; + + +/* service engine control block */ +typedef struct +{ + tSRVC_CLCB clcb[SRVC_MAX_APPS]; /* connection link*/ + tGATT_IF gatt_if; + BOOLEAN enabled; + +}tSRVC_ENG_CB; + + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Global GATT data */ +#if GATT_DYNAMIC_MEMORY == FALSE +GATT_API extern tSRVC_ENG_CB srvc_eng_cb; +#else +GATT_API extern tSRVC_ENG_CB srvc_eng_cb_ptr; +#define srvc_eng_cb (*srvc_eng_cb_ptr) + +#endif + +extern tSRVC_CLCB *srvc_eng_find_clcb_by_conn_id(UINT16 conn_id); +extern tSRVC_CLCB *srvc_eng_find_clcb_by_bd_addr(BD_ADDR bda); +extern UINT16 srvc_eng_find_conn_id_by_bd_addr(BD_ADDR bda); + + +extern void srvc_eng_release_channel (UINT16 conn_id) ; +extern BOOLEAN srvc_eng_request_channel (BD_ADDR remote_bda, UINT8 srvc_id ); +extern void srvc_sr_rsp(UINT8 clcb_idx, tGATT_STATUS st, tGATTS_RSP *p_rsp); +extern void srvc_sr_notify(BD_ADDR remote_bda, UINT16 handle, UINT16 len, UINT8 *p_value); + + +#ifdef __cplusplus +} +#endif +#endif